|
2781 | 2781 | (if (and (.exists cljcf) (.isFile cljcf)) |
2782 | 2782 | cljcf)))))) |
2783 | 2783 |
|
| 2784 | +(defn external-dep? |
| 2785 | + "Returns true if the library is an :external? foreign dep. This means no source is provided |
| 2786 | + for the library, i.e. it will be provided by some script tag on the page, or loaded by some |
| 2787 | + other means into the JS execution environment." |
| 2788 | + #?(:cljs {:tag boolean}) |
| 2789 | + [dep] |
| 2790 | + (let [js-index (:js-dependency-index @env/*compiler*)] |
| 2791 | + (if-some [[_ {:keys [foreign external?]}] (find js-index (name (-> dep lib&sublib first)))] |
| 2792 | + (and foreign external?) |
| 2793 | + false))) |
| 2794 | + |
2784 | 2795 | (defn foreign-dep? |
2785 | 2796 | #?(:cljs {:tag boolean}) |
2786 | 2797 | [dep] |
|
2828 | 2839 | (error env |
2829 | 2840 | (error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)})))))))))))) |
2830 | 2841 |
|
| 2842 | +(defn global-ns? [x] |
| 2843 | + (and (symbol? x) |
| 2844 | + (or (= 'js x) |
| 2845 | + (= "js" (namespace x))))) |
| 2846 | + |
2831 | 2847 | (defn missing-use? [lib sym cenv] |
2832 | | - (let [js-lib (get-in cenv [:js-dependency-index (name lib)])] |
2833 | | - (and (= (get-in cenv [::namespaces lib :defs sym] ::not-found) ::not-found) |
2834 | | - (not (= (get js-lib :group) :goog)) |
2835 | | - (not (get js-lib :closure-lib)) |
2836 | | - (not (node-module-dep? lib)) |
2837 | | - (not (dep-has-global-exports? lib))))) |
| 2848 | + ;; ignore globals referred via :refer-global |
| 2849 | + (when-not (global-ns? lib) |
| 2850 | + (let [js-lib (get-in cenv [:js-dependency-index (name lib)])] |
| 2851 | + (and (= (get-in cenv [::namespaces lib :defs sym] ::not-found) ::not-found) |
| 2852 | + (not (= (get js-lib :group) :goog)) |
| 2853 | + (not (get js-lib :closure-lib)) |
| 2854 | + (not (node-module-dep? lib)) |
| 2855 | + (not (dep-has-global-exports? lib)))))) |
2838 | 2856 |
|
2839 | 2857 | (defn missing-rename? [sym cenv] |
2840 | 2858 | (let [lib (symbol (namespace sym)) |
|
3047 | 3065 | ret |
3048 | 3066 | (recur fs ret true))))) |
3049 | 3067 |
|
| 3068 | +(defn parse-global-refer-spec |
| 3069 | + [env args] |
| 3070 | + (let [xs (filter #(-> % first (= :refer-global)) args) |
| 3071 | + cnt (count xs)] |
| 3072 | + (cond |
| 3073 | + (> cnt 1) |
| 3074 | + (throw (error env "Only one :refer-global form is allowed per namespace definition")) |
| 3075 | + |
| 3076 | + (== cnt 1) |
| 3077 | + (let [[_ & {:keys [only rename] :as parsed-spec}] (first xs) |
| 3078 | + only-set (set only) |
| 3079 | + err-str "Only (:refer-global :only [names]) and optionally `:rename {from to}` specs supported. |
| 3080 | + :rename symbols must be present in :only"] |
| 3081 | + (when-not (or (empty? only) |
| 3082 | + (and (vector? only) |
| 3083 | + (every? symbol only))) |
| 3084 | + (throw (error env err-str))) |
| 3085 | + (when-not (or (empty? rename) |
| 3086 | + (and (map? rename) |
| 3087 | + (every? symbol (mapcat identity rename)) |
| 3088 | + (every? only-set (keys rename)))) |
| 3089 | + (throw (error env (str err-str (pr-str parsed-spec))))) |
| 3090 | + (when-not (every? #{:only :rename} (keys parsed-spec)) |
| 3091 | + (throw (error env (str err-str (pr-str parsed-spec))))) |
| 3092 | + {:use (zipmap only (repeat 'js)) |
| 3093 | + :rename (into {} |
| 3094 | + (map (fn [[orig new-name]] |
| 3095 | + [new-name (symbol "js" (str orig))])) |
| 3096 | + rename)})))) |
| 3097 | + |
| 3098 | +(defn parse-global-require-spec |
| 3099 | + [env cenv deps aliases spec] |
| 3100 | + (if (or (symbol? spec) (string? spec)) |
| 3101 | + (recur env cenv deps aliases [spec]) |
| 3102 | + (do |
| 3103 | + (basic-validate-ns-spec env false spec) |
| 3104 | + (let [[lib & opts] spec |
| 3105 | + {alias :as referred :refer renamed :rename |
| 3106 | + :or {alias (if (string? lib) |
| 3107 | + (symbol (munge lib)) |
| 3108 | + lib)}} |
| 3109 | + (apply hash-map opts) |
| 3110 | + referred-without-renamed (seq (remove (set (keys renamed)) referred)) |
| 3111 | + [rk uk renk] [:require :use :rename]] |
| 3112 | + (when-not (or (symbol? alias) (nil? alias)) |
| 3113 | + (throw |
| 3114 | + (error env |
| 3115 | + (parse-ns-error-msg spec |
| 3116 | + ":as must be followed by a symbol in :require / :require-macros")))) |
| 3117 | + (when (some? alias) |
| 3118 | + (let [lib' ((:fns @aliases) alias)] |
| 3119 | + (when (and (some? lib') (not= lib lib')) |
| 3120 | + (throw (error env (parse-ns-error-msg spec ":as alias must be unique")))) |
| 3121 | + (when (= alias 'js) |
| 3122 | + (when-not (= lib (get-in @aliases [:fns 'js])) ; warn only once |
| 3123 | + (warning :js-used-as-alias env {:spec spec}))) |
| 3124 | + (swap! aliases update-in [:fns] conj [alias lib]))) |
| 3125 | + (when-not (or (and (sequential? referred) |
| 3126 | + (every? symbol? referred)) |
| 3127 | + (nil? referred)) |
| 3128 | + (throw |
| 3129 | + (error env |
| 3130 | + (parse-ns-error-msg spec |
| 3131 | + ":refer must be followed by a sequence of symbols in :require / :require-macros")))) |
| 3132 | + (swap! deps conj lib) |
| 3133 | + (let [ret (merge |
| 3134 | + (when (some? alias) |
| 3135 | + {rk (merge {alias lib} {lib lib})}) |
| 3136 | + (when (some? referred-without-renamed) |
| 3137 | + {uk (apply hash-map (interleave referred-without-renamed (repeat lib)))}) |
| 3138 | + (when (some? renamed) |
| 3139 | + {renk (reduce (fn [m [original renamed]] |
| 3140 | + (when-not (some #{original} referred) |
| 3141 | + (throw (error env |
| 3142 | + (str "Renamed symbol " original " not referred")))) |
| 3143 | + (assoc m renamed (symbol (str lib) (str original)))) |
| 3144 | + {} renamed)}))] |
| 3145 | + (swap! cenv assoc-in [:js-dependency-index (str lib)] |
| 3146 | + {:external? true |
| 3147 | + :foreign true |
| 3148 | + :provides [(str lib)] |
| 3149 | + :global-exports {lib lib}}) |
| 3150 | + ret))))) |
| 3151 | + |
3050 | 3152 | (defn parse-require-spec [env macros? deps aliases spec] |
3051 | 3153 | (if (or (symbol? spec) (string? spec)) |
3052 | 3154 | (recur env macros? deps aliases [spec]) |
|
3300 | 3402 | (select-keys new deep-merge-keys)))) |
3301 | 3403 | new)) |
3302 | 3404 |
|
| 3405 | +(def ns-spec-cases |
| 3406 | + #{:use :use-macros :require :require-macros |
| 3407 | + :import :refer-global :require-global}) |
| 3408 | + |
3303 | 3409 | (defmethod parse 'ns |
3304 | 3410 | [_ env [_ name & args :as form] _ opts] |
3305 | 3411 | (when-not *allow-ns* |
|
3334 | 3440 | core-renames (reduce (fn [m [original renamed]] |
3335 | 3441 | (assoc m renamed (symbol "cljs.core" (str original)))) |
3336 | 3442 | {} core-renames) |
| 3443 | + {global-uses :use global-renames :rename} (parse-global-refer-spec env args) |
3337 | 3444 | deps (atom []) |
3338 | 3445 | ;; as-aliases can only be used *once* because they are about the reader |
3339 | 3446 | aliases (atom {:fns as-aliases :macros as-aliases}) |
|
3343 | 3450 | (partial use->require env)) |
3344 | 3451 | :use-macros (comp (partial parse-require-spec env true deps aliases) |
3345 | 3452 | (partial use->require env)) |
3346 | | - :import (partial parse-import-spec env deps)} |
3347 | | - valid-forms (atom #{:use :use-macros :require :require-macros :import}) |
| 3453 | + :import (partial parse-import-spec env deps) |
| 3454 | + :require-global #(parse-global-require-spec env env/*compiler* deps aliases %)} |
| 3455 | + valid-forms (atom #{:use :use-macros :require :require-macros :require-global :import}) |
3348 | 3456 | reload (atom {:use nil :require nil :use-macros nil :require-macros nil}) |
3349 | 3457 | reloads (atom {}) |
3350 | 3458 | {uses :use requires :require renames :rename |
3351 | 3459 | use-macros :use-macros require-macros :require-macros |
3352 | 3460 | rename-macros :rename-macros imports :import :as params} |
3353 | 3461 | (reduce |
3354 | 3462 | (fn [m [k & libs :as libspec]] |
3355 | | - (when-not (#{:use :use-macros :require :require-macros :import} k) |
3356 | | - (throw (error env (str "Only :refer-clojure, :require, :require-macros, :use, :use-macros, and :import libspecs supported. Got " libspec " instead.")))) |
| 3463 | + (when-not (#{:use :use-macros :require :require-macros :require-global :import} k) |
| 3464 | + (throw (error env (str "Only :refer-clojure, :require, :require-macros, :use, :use-macros, :require-global and :import libspecs supported. Got " libspec " instead.")))) |
3357 | 3465 | (when-not (@valid-forms k) |
3358 | 3466 | (throw (error env (str "Only one " k " form is allowed per namespace definition")))) |
3359 | 3467 | (swap! valid-forms disj k) |
|
3370 | 3478 | (apply merge-with merge m |
3371 | 3479 | (map (spec-parsers k) |
3372 | 3480 | (remove #{:reload :reload-all} libs)))) |
3373 | | - {} (remove (fn [[r]] (= r :refer-clojure)) args)) |
| 3481 | + {} (remove (fn [[r]] (#{:refer-clojure :refer-global} r)) args)) |
3374 | 3482 | ;; patch `require-macros` and `use-macros` in Bootstrap for namespaces |
3375 | 3483 | ;; that require their own macros |
3376 | 3484 | #?@(:cljs [[require-macros use-macros] |
|
3392 | 3500 | :use-macros use-macros |
3393 | 3501 | :require-macros require-macros |
3394 | 3502 | :rename-macros rename-macros |
3395 | | - :uses uses |
| 3503 | + :uses (merge uses global-uses) |
3396 | 3504 | :requires requires |
3397 | | - :renames (merge renames core-renames) |
| 3505 | + :renames (merge renames core-renames global-renames) |
3398 | 3506 | :imports imports}] |
3399 | 3507 | (swap! env/*compiler* update-in [::namespaces name] merge ns-info) |
3400 | 3508 | (merge {:op :ns |
|
3434 | 3542 | core-renames (reduce (fn [m [original renamed]] |
3435 | 3543 | (assoc m renamed (symbol "cljs.core" (str original)))) |
3436 | 3544 | {} core-renames) |
| 3545 | + {global-uses :use global-renames :rename} (parse-global-refer-spec env args) |
3437 | 3546 | deps (atom []) |
3438 | 3547 | ;; as-aliases can only be used *once* because they are about the reader |
3439 | 3548 | aliases (atom {:fns as-aliases :macros as-aliases}) |
|
3443 | 3552 | (partial use->require env)) |
3444 | 3553 | :use-macros (comp (partial parse-require-spec env true deps aliases) |
3445 | 3554 | (partial use->require env)) |
3446 | | - :import (partial parse-import-spec env deps)} |
| 3555 | + :import (partial parse-import-spec env deps) |
| 3556 | + :require-global #(parse-global-require-spec env env/*compiler* deps aliases %)} |
3447 | 3557 | reload (atom {:use nil :require nil :use-macros nil :require-macros nil}) |
3448 | 3558 | reloads (atom {}) |
3449 | 3559 | {uses :use requires :require renames :rename |
|
3464 | 3574 | (apply merge-with merge m |
3465 | 3575 | (map (spec-parsers k) |
3466 | 3576 | (remove #{:reload :reload-all} libs)))) |
3467 | | - {} (remove (fn [[r]] (= r :refer-clojure)) args))] |
| 3577 | + {} (remove (fn [[r]] (#{:refer-clojure :refer-global} r)) args))] |
3468 | 3578 | (set! *cljs-ns* name) |
3469 | 3579 | (let [require-info |
3470 | 3580 | {:as-aliases as-aliases |
|
3473 | 3583 | :use-macros use-macros |
3474 | 3584 | :require-macros require-macros |
3475 | 3585 | :rename-macros rename-macros |
3476 | | - :uses uses |
| 3586 | + :uses (merge uses global-uses) |
3477 | 3587 | :requires requires |
3478 | | - :renames (merge renames core-renames) |
| 3588 | + :renames (merge renames core-renames global-renames) |
3479 | 3589 | :imports imports}] |
3480 | 3590 | (swap! env/*compiler* update-in [::namespaces name] merge-ns-info require-info env) |
3481 | 3591 | (merge {:op :ns* |
|
0 commit comments