~subsetpark/bagatto

44898a0e67507610a9e13a6373e425a937b566a5 — Zach Smith 9 months ago 22683be
Check path for executables before running
7 files changed, 117 insertions(+), 57 deletions(-)

M bagatto.janet
M main.janet
M src/core.janet
M src/env.janet
M src/generators.janet
M src/loaders.janet
M src/multimarkdown.janet
M bagatto.janet => bagatto.janet +3 -4
@@ 368,7 368,7 @@
  configuration from within the REPL.
  ```
  [data]
  (core/load-data data))
  (core/load-data data @{}))

(defn eval-loader
  ```


@@ 388,11 388,9 @@

  Not necessary to define a module, but can be useful to debug your
  configuration from within the REPL.

  # TODO : To make this work again, expose the generator functions in a non-threaded context.
  ```
  [site data]
  (core/produce-writer-specs site data @{} @{}))
  (core/produce-writer-specs site data @{}))

(defn write-site
  ```


@@ 450,6 448,7 @@

  (requires the presence of the multimarkdown executable.)
  ```
  :foo
  [md &keys {:smart smart}]
  (multimarkdown/snippet md smart))


M main.janet => main.janet +19 -14
@@ 17,6 17,12 @@
  (try ((env sym) :value)
    ([err fib] (error/eval-error sym index))))

(defn- prepare-env [index]
  (-> index
     (dofile)
     (merge-into bagatto)
     (env/add-exec-blacklist)))

(def argparse-params ["A transparent, extensible static site generator."
                      "repl" {:kind :flag
                              :help "Compile your index module and enter the REPL."}


@@ 26,26 32,25 @@
                                :required true}])

(defn main [& args]
  (env/prepare-env-with-bagatto! bagatto)
  (merge-into root-env bagatto)

  (let [args (argparse ;argparse-params)
        index (args :default)
        env (dofile index)]

    (env/prepare-env-with-index! env)
        env (prepare-env index)]
    
    (env/prepare-environment! env)

    (if (args "repl")
      # REPL mode: Enter a REPL to experiment with the contents of the
      # index module.
      (repl nil nil env)
      # Normal mode: evaluate index module and write site.
      (do
        (setdyn :bagatto-defaults (env :bagatto-defaults))

        (def data (let [data-spec (index-value env 'data index)]
                    (core/load-data data-spec)))

        (def writer-specs (let [site-spec (index-value env 'site index)]
                            (core/produce-writer-specs site-spec data env bagatto)))

        (core/evaluate-writer-specs env writer-specs)))))
      (do (setdyn :bagatto-defaults (env :bagatto-defaults))
          
          (def data (let [data-spec (index-value env 'data index)]
                      (core/load-data data-spec env)))
          
          (def writer-specs (let [site-spec (index-value env 'site index)]
                              (core/produce-writer-specs site-spec data env)))
          
          (core/evaluate-writer-specs env writer-specs)))))

M src/core.janet => src/core.janet +18 -13
@@ 36,7 36,7 @@
  finite number of [filename file-contents] tuples, and an :attrs
  function that will be called on each file-contents.
  ```
  [data-spec]
  [data-spec env]

  (let [data-pairs (pairs data-spec)
        jobs (seq [[spec-name spec] :in data-pairs]


@@ 49,13 49,22 @@
                     transform-f (or (spec :transform) identity)]
                 (match with-defaults
                   ({:src loader :attrs parser} (function? loader))
                   (loaders/from-file-spec-loader spec-name loader parser transform-f)
                   (loaders/from-file-spec-loader env
                                                  spec-name
                                                  loader
                                                  parser
                                                  transform-f)

                   ({:src path :attrs parser} (string? path))
                   (loaders/from-path-loader spec-name path parser)
                   (loaders/from-path-loader env
                                             spec-name
                                             path
                                             parser)

                   {:attrs attrs}
                   (loaders/bare-attr-loader spec-name attrs)
                   (loaders/bare-attr-loader env
                                             spec-name
                                             attrs)

                   _ (error/data-error with-defaults))))]
    (threads/distribute jobs)))


@@ 67,7 76,7 @@
  to do it with. Here we generate a new writer specification (a tuple
  of path and contents) for each file in the website.
  ```
  [site data bagatto env]
  [site data env]
  (let [site-pairs (pairs site)
        jobs (seq [[spec-name spec] :in site-pairs]



@@ 81,8 90,7 @@
                   {:each site-selector
                    :dest path-generator
                    :out renderer}
                   (generators/render-each-generator bagatto
                                                     env
                   (generators/render-each-generator env
                                                     data
                                                     spec-name
                                                     filter


@@ 92,8 100,7 @@

                   {:each site-selector
                    :dest path-generator}
                   (generators/copy-each-generator bagatto
                                                   env
                   (generators/copy-each-generator env
                                                   data
                                                   spec-name
                                                   filter


@@ 102,8 109,7 @@

                   {:dest path-generator
                    :out renderer}
                   (generators/render-generator bagatto
                                                env
                   (generators/render-generator env
                                                data
                                                spec-name
                                                path-generator


@@ 111,8 117,7 @@

                   {:some site-selector
                    :dest path-generator}
                   (generators/copy-some-generator bagatto
                                                   env
                   (generators/copy-some-generator env
                                                   data
                                                   spec-name
                                                   site-selector

M src/env.janet => src/env.janet +45 -11
@@ 1,18 1,52 @@
(import temple)
(import sh)

(defn prepare-env-with-bagatto! [bagatto]
  (temple/add-loader)
(def required-checks @{})

(defmacro defrequire
  ```
  Define a function, preceded by the name of an external program. If
  this program is not present when Bagatto is run, this function will
  raise an error.
  ```
  [program name args & body]
  ~((put env/required-checks ,program true)
     (defn ,name
       ,args
       (when (in (dyn :executable-blacklist) ,program)
         (error (string "Required executable not installed: " ,program)))
       ,;body)))

(defn- on-path?
  [prog]
  (let [f (file/open "/dev/null")]
    (case (os/execute ["which" prog] :p {:out f :err f})
      0 true
      false)))

(defn- prepare-syspath! []
  (match (os/getenv "JANET_PATH")
    nil :ok
    janet-path (put root-env :syspath janet-path))
  (merge-into root-env bagatto))
    janet-path (put root-env :syspath janet-path)))

(defn prepare-env-with-index! [env]
  # Monkey-patch the temple environment with the functions defined
  # in the index module.
  (merge-into temple/base-env env))
(defn add-exec-blacklist
  ```
  Iterate through all the executables declared with `defrequire` and
  check for their existence in the path. Add a blacklist of all
  executables that are absent from the path to the given environment.
  ```
  [env]
  (let [blacklist @{}]
    (loop [check :keys required-checks]
      (unless (on-path? check)
        (put blacklist check true)))
    (put env :executable-blacklist blacklist)))

(defn prepare-environment!
  [bagatto env]
  (prepare-env-with-bagatto! bagatto)
  (prepare-env-with-index! env))
  [env]
  (prepare-syspath!)
  (temple/add-loader)
  # Monkey-patch the temple environment with the functions defined
  # in the index module.
  (merge-into temple/base-env env)
  (setdyn :executable-blacklist (env :executable-blacklist)))

M src/generators.janet => src/generators.janet +10 -10
@@ 5,8 5,8 @@
(import src/error)
(import src/env)

(defn- set-cxt! [bagatto env spec-name]
  (env/prepare-environment! bagatto env)
(defn- set-cxt! [env spec-name]
  (env/prepare-environment! env)
  (setdyn :error-context {:spec-name spec-name}))

(defn- maybe-apply [f args]


@@ 22,9 22,9 @@
      (error/renderer-error err f args-type))))

(defn render-each-generator
  [bagatto env data spec-name filter site-selector path-generator renderer]
  [env data spec-name filter site-selector path-generator renderer]
  (fn [parent]
    (set-cxt! bagatto env spec-name)
    (set-cxt! env spec-name)

    (threads/print "Rendering " spec-name "...")
    (def res @[])


@@ 37,9 37,9 @@
    (:send parent [:res spec-name res])))

(defn copy-each-generator
  [bagatto env data spec-name filter site-selector path-generator]
  [env data spec-name filter site-selector path-generator]
  (fn [parent]
    (set-cxt! bagatto env spec-name)
    (set-cxt! env spec-name)

    (threads/print "Generating paths for " spec-name "...")
    (def res @[])


@@ 52,9 52,9 @@
    (:send parent [:res spec-name res])))

(defn render-generator
  [bagatto env data spec-name path-generator renderer]
  [env data spec-name path-generator renderer]
  (fn [parent]
    (set-cxt! bagatto env spec-name)
    (set-cxt! env spec-name)

    (threads/print "Rendering " spec-name "...")
    (def res @[])


@@ 65,9 65,9 @@
    (:send parent [:res spec-name res])))

(defn copy-some-generator
  [bagatto env data spec-name site-selector path-generator]
  [env data spec-name site-selector path-generator]
  (fn [parent]
    (set-cxt! bagatto env spec-name)
    (set-cxt! env spec-name)

    (threads/print "Generating path for " spec-name "...")
    (def res @[])

M src/loaders.janet => src/loaders.janet +14 -3
@@ 1,5 1,10 @@
(import src/error)
(import src/threads)
(import src/env)

(defn- set-cxt! [env spec-name]
  (env/prepare-environment! env)
  (setdyn :error-context {:spec-name spec-name}))

(defn- make-attrs [parser filename &opt file-contents]
  (let [base-attrs @{:path filename :contents file-contents}]


@@ 13,9 18,11 @@
  A loader that takes either a single file spec or a sequence of file
  specs and sends back a list of attributes.
  ```
  [spec-name loader parser transform-f]
  [env spec-name loader parser transform-f]
  (fn [parent]
    (set-cxt! env spec-name)
    (threads/print "Loading " spec-name "...")

    (let [loader-specs (loader)
          res (match loader-specs
                {:each ind}


@@ 36,9 43,11 @@
  A loader that takes a literal file path and generates file
  attributes.
  ```
  [spec-name path parser]
  [env spec-name path parser]
  (fn [parent]
    (set-cxt! env spec-name)
    (threads/print "Loading " spec-name " (" path ")...")

    (let [file-contents (slurp path)
          res (make-attrs parser path file-contents)]
      (:send parent [:res spec-name res]))))


@@ 47,7 56,9 @@
  ```
  A loader that takes an attributes literal and returns it.
  ```
  [spec-name attrs]
  [env spec-name attrs]
  (set-cxt! env spec-name)

  (fn [parent]
    (threads/print "Loaded " spec-name)
    (:send parent [:res spec-name attrs])))

M src/multimarkdown.janet => src/multimarkdown.janet +8 -2
@@ 1,4 1,5 @@
(import sh)
(import src/env)

(defn- metadata-keys
  [md]


@@ 12,11 13,16 @@
  (->> (sh/$< echo ,(string md) |multimarkdown -e ,key --)
       (string/trim)))

(defn metadata [md]
(env/defrequire
  "multimarkdown"
  metadata [md]
  (let [keys (metadata-keys md)]
    (table ;(mapcat |[$0 (metadata-value md $0)] keys))))

(defn snippet

(env/defrequire
  "multimarkdown"
  snippet
  [md &opt smart]
  (default smart true)