~subsetpark/bagatto

f522addabb0c1031e7235da7b47b7173f9cde109 — Zach Smith 4 months ago 9f6ca6b
Better naming for site/data attributes
M MANUAL.md => MANUAL.md +50 -527
@@ 37,7 37,7 @@ inside the module.

They are:

- The [`bagatto`](#bagatto-api) library itself, a collection of useful functions
- The [`bagatto`](./api.html#bagatto-api) library itself, a collection of useful functions
  designed to reduce boilerplate in Bagatto modules, whose API is
  listed below;
- The [`path`](https://github.com/janet-lang/path) library, which


@@ 46,8 46,14 @@ They are:
  library, which exposes a useful DSL for shelling out to the command
  line.

One of Bagatto's principles is to expose as much of its API as
possible in the form of ordinary functions to be used to produce the
data structures you define in your index module. There are a couple
places where that isn't possible, and where we have to expose a
"global" API instead.

In addition to the helper functions exposed in the `bagatto/`
namespace, there is one more feature that can be accessed directly
namespace, there are a few features that can be accessed directly
inside of index modules:

### Defaults Handling


@@ 55,8 61,19 @@ inside of index modules:
Bagatto exposes the `bagattto/set-defaults!` function, which can be
called at any point inside an index module. It takes a single
argument: a struct or dictionary specifying the default value for any
of the specification attributes: `:src`, `:attrs`, `:path`,
`:contents`.
of the specification attributes: `:src`, `:attrs`, `:dest`,
`:out`.

### Output Directory

By calling `bagatto/set-output-dir!`, you can specify the directory
that Bagatto should write its generated file tree into. This is
principally exactly the same as appending that directory name to every
path that you generate; however, if you use this feature then you can
re-use paths in your business logic (for instance, you can define a
path value and use it when generating a file, and when rendering a
link in your site), as the additional file hierarchy will be
transparently dealt with.

## Data



@@ 118,12 135,12 @@ the content of the site data associated with `:config-json`.
repl:17:> (bagatto/eval-data data)
@{:config-json @{"subtitle" "A Very Good Blog." 
                 :path "config.json" 
                 :src @"{\"subtitle\":\"A Very Good Blog.\"}\n"}}
                 :contents @"{\"subtitle\":\"A Very Good Blog.\"}\n"}}
```

We see that the resulting site data has a single entry,
`:config-json`. The table associated with this entry has the two
attributes we get for free---`:path` and `:src`, which are the file
attributes we get for free---`:path` and `:contents`, which are the file
path and contents, respectively---but that the call to `parse-json`
has resulted in the key/value pairs inside the JSON file have been
parsed and put in the site data too.


@@ 192,10 209,10 @@ additional metadata.

```clj
repl:33:> (bagatto/eval-data data)
@{:posts @[@{:path "demo/posts/post.md" :src @"..."} 
@{:posts @[@{:path "demo/posts/post.md" :contents @"..."} 
           @{"status" "post" 
             :path "demo/posts/post2.md" 
             :src @"..." 
             :contents @"..." 
             "title" "A Really Good Title"}]}
```



@@ 224,7 241,7 @@ parsed.
                   :transform (bagatto/attr-sorter "topic")}})
```

[`bagatto/attr-sorter`](#bagattoattr-sorter) is exposed as a part of
[`bagatto/attr-sorter`](./api.html#bagattoattr-sorter) is exposed as a part of
the Bagatto library and allows us to specify a key present in all the
items, and sort the collection by it.



@@ 272,8 289,8 @@ simplest possible site is one consisting of a static path and
contents:

```clj
repl:2:> (def site {:_ {:path "out.txt" :contents "Welcome to my website"}})
{:_ {:path "out.txt" :contents "Welcome to my website"}}
repl:2:> (def site {:_ {:dest "out.txt" :out "Welcome to my website"}})
{:_ {:dest "out.txt" :out "Welcome to my website"}}
```

We can use `bagatto/eval-site` to get an output of the path and


@@ 304,13 321,13 @@ data and outputs some file contents. We can write our own extremely
simple one, which looks for a secret in the site data and outputs it
in JDN format to a file. Then we can define a simple site
specification with a static path that passes that function in directly
as the `:contents` attribute.
as the `:out` attribute.

```clj
repl:6:> (defn renderer [data] (string/format "%j" (data :secret)))
<function renderer>
repl:7:> (def site {:_ {:path "out.txt" :contents renderer}})
{:_ {:path "out.txt" :contents <function renderer>}}
repl:7:> (def site {:_ {:dest "out.txt" :out renderer}})
{:_ {:dest "out.txt" :out <function renderer>}}
```

Now, of course, we need to ensure that `:secret` is present in the


@@ 337,8 354,8 @@ repl:12:> (defn renderer [data] string/format "%s:%j:%f"
                                              (get-in data [:personal :password]) 
                                              (math/random)))
<function renderer>
repl:14:> (def site {:_ {:path "out.txt" :contents renderer}})
{:_ {:path "out.txt" :contents <function renderer>}}
repl:14:> (def site {:_ {:dest "out.txt" :out renderer}})
{:_ {:dest "out.txt" :out <function renderer>}}
repl:15:> (bagatto/eval-site site {:personal {:password "p@ssw0rd"} 
                                   :config {:prefix "md5"}})
@[(:write "out.txt" "md5:\"p@ssw0rd\":0.487181")]


@@ 367,7 384,7 @@ For that we can use **site selectors**.

Given some site data with a series of named data entries, we can use
the `each` attribute to refer to one of those entries. Bagatto will
then call the `path` and `contents` functions on each file in the
then call the `dest` and `out` functions on each file in the
entry.

Because there's now an additional piece of data in addition to the


@@ 409,17 426,17 @@ to the `users` site data.

```clj
repl:35:> (def site {:_ {:each :users 
                         :path (bagatto/path-copier "passwords/") 
                         :contents renderer}})
{:_ {:path <function 0x55F89DCBC880> :contents <function renderer> :each :users}}
                         :dest (bagatto/path-copier "passwords/") 
                         :out renderer}})
{:_ {:dest <function 0x55F89DCBC880> :out <function renderer> :each :users}}
```

`:each :users` will cause Bagatto to call the renderer once for each
item in `:users`. In addition, we now need to specify an actual
function for `:path`. If we left it as a static value, the contents
function for `:dest`. If we left it as a static value, the contents
would be repeatedly written to the same file, which is obviously not
what we want. Here we use the
[`bagatto/path-copier`](#bagattopath-copier) helper, which gives us a
[`bagatto/path-copier`](./api.html#bagattopath-copier) helper, which gives us a
function that will accept any file and return a new path with the base
we specify.



@@ 438,10 455,10 @@ are interpolated from the contents of their respective source files.

A very common operation when generating a website is to copy a source
file without touching it. If Bagatto receives a site specification
with a site selector and a `:path` entry, but no `:contents` entry, it
with a site selector and a `:dest` entry, but no `:out` entry, it
will interpret that as a **copy** operation. It will read the `:path`
of whatever item or item it receives (this attribute is always
present), and copy it to the `:path` attribute of the site
present), and copy it to the `:dest` attribute of the site
specification.

Here's a super simple data spec:


@@ 459,8 476,8 @@ We can now define a `site` that simply refers to `:users` and
specifies a path without specifying contents.

```clj
repl:58:> (def site {:_ {:each :users :path (bagatto/path-copier "passwords/")}})
{:_ {:path <function 0x55F89DCD8B30> :each :users}}
repl:58:> (def site {:_ {:each :users :dest (bagatto/path-copier "passwords/")}})
{:_ {:dest <function 0x55F89DCD8B30> :each :users}}
```

Evaluating the site produces two copy instructions to the new paths:


@@ 491,7 508,7 @@ Here's the contents of `post.temple` in the Bagatto demo directory:
      <p class="post-info">
        {{ (get-in args [:_item :date]) }}
      </p>
      {- (bagatto/mmarkdown->html (get-in args [:_item :src])) -}
      {- (bagatto/mmarkdown->html (get-in args [:_item :contents])) -}

{$ (import ./base_bottom :as base_bottom) $}
{% (base_bottom/render-dict args) %}


@@ 550,7 567,7 @@ the post.
### Rendering a Template

The basic call to render a template is
[`bagatto/render`](#bagattorender). This allows us to directly invoke
[`bagatto/render`](./api.html#bagattorender). This allows us to directly invoke
a template by name, with site data and an optional item, and returns
the fully rendered template. For instance, if we have a simple
template at `templates/simple.temple`:


@@ 572,516 589,22 @@ with placeholders for the values to be interpolated.
### Renderer Generators

Because `bagatto/render` is such a common operation, Bagatto offers a
[convenience function](#bagattorenderer) that will generate a
[convenience function](./api.html#bagattorenderer) that will generate a
**renderer** that will make the above call. For instance, if I wanted
to specify the above template in a site specification, I'd probably
write this:

```clj
repl:6:> (def site {:_ {:path "out.txt" 
                        :contents (bagatto/renderer "templates/simple")}})
{:_ {:path "out.txt" :contents <function 0x55DAC976C660>}}
repl:6:> (def site {:_ {:dest "out.txt" 
                        :out (bagatto/renderer "templates/simple")}})
{:_ {:dest "out.txt" :out <function 0x55DAC976C660>}}
```

Thus I avoid having to write a new renderer function for each
`:contents` entry, if I'm just going to pass on the data to a specific
`:out` entry, if I'm just going to pass on the data to a specific
template. Evaluating the site we get the same thing:

```clj
repl:7:> (bagatto/eval-site site {"topic" "Web Design"})
@[(:write "out.txt" @"I am known for my Web Design skills.\n")]
```

# bagatto API

[bagatto/%p](#bagatto%p)
, [bagatto/*](#bagatto*)
, [bagatto/attr-sorter](#bagattoattr-sorter)
, [bagatto/datestr-&gt;date](#bagattodatestr-&gt;date)
, [bagatto/datestr-&gt;secs](#bagattodatestr-&gt;secs)
, [bagatto/epp](#bagattoepp)
, [bagatto/eval-data](#bagattoeval-data)
, [bagatto/eval-loader](#bagattoeval-loader)
, [bagatto/eval-site](#bagattoeval-site)
, [bagatto/format](#bagattoformat)
, [bagatto/get-default](#bagattoget-default)
, [bagatto/item-getter](#bagattoitem-getter)
, [bagatto/jdn-data](#bagattojdn-data)
, [bagatto/json-data](#bagattojson-data)
, [bagatto/markdown-&gt;html](#bagattomarkdown-&gt;html)
, [bagatto/mmarkdown-&gt;html](#bagattommarkdown-&gt;html)
, [bagatto/mmarkdown-data](#bagattommarkdown-data)
, [bagatto/parse-base](#bagattoparse-base)
, [bagatto/parse-jdn](#bagattoparse-jdn)
, [bagatto/parse-json](#bagattoparse-json)
, [bagatto/parse-mmarkdown](#bagattoparse-mmarkdown)
, [bagatto/path-copier](#bagattopath-copier)
, [bagatto/render](#bagattorender)
, [bagatto/renderer](#bagattorenderer)
, [bagatto/set-defaults!](#bagattoset-defaults)
, [bagatto/site-getter](#bagattosite-getter)
, [bagatto/slug-from-attr](#bagattoslug-from-attr)
, [bagatto/slugify](#bagattoslugify)
, [bagatto/slurp-*](#bagattoslurp-*)
, [bagatto/write-site](#bagattowrite-site)

## bagatto/%p

**function**  | [source][1]

```janet
(%p & elements)
```

Simple DSL for generating paths. 

Given any number of string or keyword arguments, will return a
function that takes an item and returns a file path.

The arguments make up the components of the path. To include an
element directly in the path, put it as an argument. To indicate
that an element should be treated as a key in `site`, precede it by
a `'%s`. To indicate that an element should be treated as a key in
`item`, precede it by a `'%i`. To join two elements directly,
without a path separator, put a `'%` between them. For instance,

```clj
repl:9:> (def f (bagatto/%p "site" '%s "author" '%i :title '% ".html"))
<function 0x55F38B3B3880>
repl:10:> (f {"author" "Z. D. Smith"} {:title "My first post"})
"site/z-d-smith/my-first-post.html"
repl:11:> (f {"author" "Z. D. Smith"} {:title "My second post"})
"site/z-d-smith/my-second-post.html"
```

[1]: /usr/lib/janet/bagatto.janet#L244

## bagatto/*

**function**  | [source][2]

```janet
(* pattern)
```

Generate a fiber that will return all the filenames that match a given
file blob.

[2]: /usr/lib/janet/bagatto.janet#L104

## bagatto/attr-sorter

**function**  | [source][3]

```janet
(attr-sorter key &opt descending?)
```

Return a sorter function that, given a list of items, sorts
according to the items' values at the specified key.

[3]: /usr/lib/janet/bagatto.janet#L327

## bagatto/datestr-&gt;date

**function**  | [source][4]

```janet
(datestr->date datestr &opt for-display?)
```

Given a string representation of a date or datetime, return a struct
of the kind returned by `os/date`. Pass a truthy value in the second
argument to adjust for human display (setting values like day and
month to be 1-indexed).

[4]: /usr/lib/janet/bagatto.janet#L77

## bagatto/datestr-&gt;secs

**function**  | [source][5]

```janet
(datestr->secs datestr)
```

Given a string representation of a date or datetime, return the
epoch seconds value for that date.

[5]: /usr/lib/janet/bagatto.janet#L69

## bagatto/epp

**function**  | [source][6]

```janet
(epp x)
```

Pretty-print to stderr. Since Temple templates operate over stdout,
we should use stderr instead if we need to print something to the
console for debugging purposes.

[6]: /usr/lib/janet/bagatto.janet#L413

## bagatto/eval-data

**function**  | [source][7]

```janet
(eval-data data)
```

Evaluate an object according to the Bagatto *site data specification*.

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

[7]: /usr/lib/janet/bagatto.janet#L340

## bagatto/eval-loader

**function**  | [source][8]

```janet
(eval-loader fiber)
```

Evaluate a loader fiber to view the list of filenames, or filename
and contents, it produces.

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

[8]: /usr/lib/janet/bagatto.janet#L350

## bagatto/eval-site

**function**  | [source][9]

```janet
(eval-site site data)
```

Evaluate an object according to the Bagatto *site generation specification*,
given a site data object as context.

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

[9]: /usr/lib/janet/bagatto.janet#L361

## bagatto/format

**function**  | [source][10]

```janet
(format templ & xs)
```

A simple wrapper around `string/format` to ease development. If one
of `xs` is nil, it will output an empty string rather than crashing
the template.

[10]: /usr/lib/janet/bagatto.janet#L403

## bagatto/get-default

**function**  | [source][11]

```janet
(get-default attribute)
```

Get the value set in the bagatto defaults for the specified key. Can
be used for a simple form of inheritence; if you have a default
callback or value set, and you have some specific handler that needs
to build on top of that functionality, you can call
`bagatto/get-default` to get or evaluate it inside of the handler
and then implement your more specific logic.

(see `bagatto/set-defaults!` for more information.)

[11]: /usr/lib/janet/bagatto.janet#L26

## bagatto/item-getter

**function**  | [source][12]

```janet
(item-getter path)
```

Return a function that, given site and item data, gets the value at
the given path in the item.

The path should be a tuple of keys, eg.: `[:user :full-name]`. 

[12]: /usr/lib/janet/bagatto.janet#L234

## bagatto/jdn-data

**function**  | [source][13]

```janet
(jdn-data src)
```

Get metadata from a JDN-formatted string.

[13]: /usr/lib/janet/bagatto.janet#L49

## bagatto/json-data

**function**  | [source][14]

```janet
(json-data src)
```

Get metadata from a JSON-formatted string.

[14]: /usr/lib/janet/bagatto.janet#L54

## bagatto/markdown-&gt;html

**function**  | [source][15]

```janet
(markdown->html md)
```

Render a markdown string into HTML.

[15]: /usr/lib/janet/bagatto.janet#L389

## bagatto/mmarkdown-&gt;html

**function**  | [source][16]

```janet
(mmarkdown->html md)
```

Render a markdown string using multimarkdown.

(requires the presence of the multimarkdown executable.)

[16]: /usr/lib/janet/bagatto.janet#L394

## bagatto/mmarkdown-data

**function**  | [source][17]

```janet
(mmarkdown-data md)
```

Get metadata from a markdown string using multimarkdown.

[17]: /usr/lib/janet/bagatto.janet#L44

## bagatto/parse-base

**function**  | [source][18]

```janet
(parse-base _src attrs)
```

Base attribute parser: bypasses the input source and returns the
default attributes that are provided for every file. These are:
- `:path`: the file path
- `:src`: (if the file was read) the contents of the file 

[18]: /usr/lib/janet/bagatto.janet#L122

## bagatto/parse-jdn

**function**  | [source][19]

```janet
(parse-jdn src attrs)
```

Attribute parser for JDN.

[19]: /usr/lib/janet/bagatto.janet#L141

## bagatto/parse-json

**function**  | [source][20]

```janet
(parse-json src attrs)
```

Attribute parser for JSON.

[20]: /usr/lib/janet/bagatto.janet#L146

## bagatto/parse-mmarkdown

**function**  | [source][21]

```janet
(parse-mmarkdown src attrs)
```

Attribute parser for multimarkdown.

(requires the presence of the multimarkdown executable.)

[21]: /usr/lib/janet/bagatto.janet#L132

## bagatto/path-copier

**function**  | [source][22]

```janet
(path-copier base &opt key)
```

Return a function that will generate a new path with the same
base, from the same key, for any item.

eg.:

```clj
repl:2:> (def f (bagatto/path-copier "destination"))
<function 0x55F38B3AE640>
repl:3:> (f {} {:path "original/source/directory/filename.md"})
"destination/filename.md"
repl:4:> (f {} {:path "original/source/directory/newfile.png"})
"destination/newfile.png"
```

[22]: /usr/lib/janet/bagatto.janet#L306

## bagatto/render

**function**  | [source][23]

```janet
(render template site &opt item)
```

 
Given site data and an optional item, render the specified Temple template.

All the attributes will be available in the template under `args`.

If a single item was passed in in addition to the site, that item
will be available at `(args :_item)`.

In addition to the standard Janet library, the same additional
libraries that Bagatto makes available when writing an index module
are also available (without explicitly importing them) from within a
template.

See the [Temple](https://git.sr.ht/~bakpakin/temple) site for more details.

[23]: /usr/lib/janet/bagatto.janet#L155

## bagatto/renderer

**function**  | [source][24]

```janet
(renderer template-path &opt extra-attrs)
```

Return a function, given a template and optional extra attributes,
that will call `render` with that template on any input.

(see `bagatto/render` for more details.)

[24]: /usr/lib/janet/bagatto.janet#L210

## bagatto/set-defaults!

**function**  | [source][25]

```janet
(set-defaults! defaults)
```

Set the defaults for the values specified in data and site
specifications. For instance, setting the default of `:attrs` to
`bagatto/parse-json` will attempt to set the attributes for any file
by parsing it as JSON. `:attrs` could still be set on any entry in
the data spec in order to override the default.

[25]: /usr/lib/janet/bagatto.janet#L15

## bagatto/site-getter

**function**  | [source][26]

```janet
(site-getter path)
```

Return a function that, given site data or site and item data, gets
the value at the given path in the site.

The path should be a tuple of keys, eg.: `[:blog :description]`. 

[26]: /usr/lib/janet/bagatto.janet#L224

## bagatto/slug-from-attr

**function**  | [source][27]

```janet
(slug-from-attr item key)
```

Given an object and a key, get the value of that key in the object
it make the value into a slug.

(see `bagatto/slugify` for more details.)

[27]: /usr/lib/janet/bagatto.janet#L196

## bagatto/slugify

**function**  | [source][28]

```janet
(slugify s)
```

Normalize a string for use as a slug, eg., in a file path.

[28]: /usr/lib/janet/bagatto.janet#L182

## bagatto/slurp-*

**function**  | [source][29]

```janet
(slurp-* pattern)
```

Generate a fiber that will slurp all the files that match a given
file blob.

[29]: /usr/lib/janet/bagatto.janet#L111

## bagatto/write-site

**function**  | [source][30]

```janet
(write-site writer-specs)
```

Given the output of a site generation specification, trigger the
actual file generation.

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

[30]: /usr/lib/janet/bagatto.janet#L372


M bagatto.janet => bagatto.janet +1 -1
@@ 137,7 137,7 @@
  Base attribute parser: bypasses the input source and returns the
  default attributes that are provided for every file. These are:
  - `:path`: the file path
  - `:src`: (if the file was read) the contents of the file 
  - `:contents`: (if the file was read) the contents of the file 
  ```
  [_src attrs]
  attrs)

M demo/index.janet => demo/index.janet +10 -10
@@ 33,7 33,7 @@
##
## The attributes available by default are:
## * :path - The path of the file from the index
## * :src - The contents of the file
## * :contents - The contents of the file
##
## (Thus providing the source as the first argument is a little
## unnecessary, but slightly more convenient.)


@@ 110,15 110,15 @@
# the index), so we can specify it as a simple string.
(def index-path "index.html")

## The *contents* function has the same argument signature as the
## *path* function. It is also called once for each source file. It
## The *out* function has the same argument signature as the
## *dest* function. It is also called once for each source file. It
## should return the contents of the new file.

# Here we use the bagatto/renderer function to generate a renderer
# function. It returns a function that will take the site data and a
# post and call `bagatto/render` on the specified template, with the
# site and the post as arguments.
(def render-post (bagatto/renderer "templates/post" {:root "../.."}))
(def render-post (bagatto/renderer "templates/post" {:root "../"}))
                
# The Posts index is generated from the site data and lists out all
# the posts. Therefore, it isn't rendered out of a specific source


@@ 130,7 130,7 @@
# accept only one argument---just the site data---without any specific
# item being rendered. The only difference will be that the `:_item`
# attribute won't be available in the template.
(def render-post-index (bagatto/renderer "templates/posts" {:root ".."}))
(def render-post-index (bagatto/renderer "templates/posts" {:root "./"}))

# Likewise, our `site` struct has three entries---there doesn't have
# to be any clean mapping between entries in `data` and entries in


@@ 154,10 154,10 @@
#   `:contents`. This means that it will simply use `make-static-path`
#   to generate a new path, then copy the file from the original path
#   to the new one.
(def site {:post-index {:path index-path
                        :contents render-post-index}
(def site {:post-index {:dest index-path
                        :out render-post-index}
           :posts {:each :posts
                   :path make-post-path
                   :contents render-post}
                   :dest make-post-path
                   :out render-post}
           :static {:each :static
                    :path make-static-path}})
                    :dest make-static-path}})

M demo/templates/post.temple => demo/templates/post.temple +1 -1
@@ 5,7 5,7 @@
      <p class="post-info">
        {{ (get-in args [:_item :date]) }}
      </p>
      {- (bagatto/mmarkdown->html (get-in args [:_item :src])) -}
      {- (bagatto/mmarkdown->html (get-in args [:_item :contents])) -}

{$ (import ./base_bottom :as base_bottom) $}
{% (base_bottom/render-dict args) %}

M demo/templates/posts.temple => demo/templates/posts.temple +1 -1
@@ 35,7 35,7 @@

<ul>
{% (loop [post :in (args :posts)]
     (def dest (path/join (args :root) (make-post-path args post)))
     (def dest (make-post-path args post))
     (print (hypertext/markup
            (li (a :href dest
                   [(post :title)]))))) %}

M src/core.janet => src/core.janet +6 -6
@@ 79,8 79,8 @@
      (match with-defaults
        
        {:each site-selector
         :path path-generator
         :contents renderer}
         :dest path-generator
         :out renderer}
        (loop [item :in (data site-selector)]
          (if-let [_should-read (filter data item)
                   path (maybe-apply path-generator [data item] :di :p)


@@ 88,21 88,21 @@
            (push-writer :write path contents)))
        
        {:each site-selector
         :path path-generator}
         :dest path-generator}
        (loop [item :in (data site-selector)]
          (if-let [_should-read (filter data item)
                   from (item :path)
                   to (maybe-apply path-generator [data item] :di :p)]
            (push-writer :copy from to)))
        
        {:path path-generator
         :contents renderer}
        {:dest path-generator
         :out renderer}
        (if-let [path (maybe-apply path-generator [data] :d :p)
                 contents (maybe-apply renderer [data] :d :c)]
          (push-writer :write path contents))
        # TODO: When is this necessary? 
        {:some site-selector
         :path path-generator}
         :dest path-generator}
        (if-let [item (data site-selector)
                 from (item :path)
                 to (maybe-apply path-generator [data] :d :p)]

M src/error.janet => src/error.janet +5 -5
@@ 10,7 10,7 @@
  (error (string/format
          "%s;\nExpected %s function signature:\n(defn %s %s\n %s)"
          err
          (case ret-type :p "path" :c "contents")
          (case ret-type :p "dest" :c "out")
          (f-name f)
          (case args-type :d "[data]" :di "[data item]")
          (case ret-type :p `"foo/bar/..."` :c `"<html>..."`))))


@@ 41,9 41,9 @@
          Received invalid site spec: %q

          Specification can be one of the following:
          {:each site-selector :path path-generator :contents renderer} (write)
          {:each site-selector :path path-generator} (copy)
          {:path (path|path-generator) :contents (contents|renderer)} (write)
          {:some site-selector :path (path|path-generator)} (copy)
          {:each site-selector :dest path-generator :out renderer} (write)
          {:each site-selector :dest path-generator} (copy)
          {:dest (path|path-generator) :out (contents|renderer)} (write)
          {:some site-selector :dest (path|path-generator)} (copy)
          ```
          spec)))

M src/loaders.janet => src/loaders.janet +1 -1
@@ 1,7 1,7 @@
(import src/error)

(defn- make-attrs [parser filename &opt file-contents]
    (let [base-attrs @{:path filename :src file-contents}]
    (let [base-attrs @{:path filename :contents file-contents}]
      (if file-contents
        (try (parser file-contents base-attrs) 
             ([err fib] (error/attrs-error err parser)))