elm-frontmatter
v2.1.2
Published
In a vaguely similar way to [Haykll](https://jaspervdj.be/hakyll/), [Jekyll](https://jekyllrb.com/), turns Frontmatter files into Elm code that you can then import and use in your Elm app like any other module. Also handles image copying and processing.
Downloads
5
Readme
In a vaguely similar way to Haykll, Jekyll, turns Frontmatter files into Elm code that you can then import and use in your Elm app like any other module. Also handles image copying and processing.
The package works as expected, but worth being aware there is still quite a bit of work to go into making it a nice user experience.
Contents
Installation and setup
├── Install Packages
└── Get Started
Running
└── Options
Directory Structure
├── content.md
Files
└── Singleton vs Collection Item Files
Installation and Setup
Install Packages
npm install elm-frontmatter
npm install elm-format
(Optionally)elm install thomasin/elm-frontmatter
Note: elm-format
will be used if it is found.
If it is not installed, files will still be written but unformatted.
Get Started
Create a folder /content
in the same directory as your elm.json
.
To decode your first frontmatter file you can populate:
(1) A file /content/index.md
with
---
title: First page
---
woohoo
(2) A file Content.elm
(placed in whichever folder your Main.elm
is in) with
module Content exposing (decoder)
import Content.Decode as Decode
import Content.Type as Type
-- Required by the Node app and cannot be removed or unexposed.
decoder : Type.Path -> Decode.QueryResult
decoder typePath =
case typePath of
Type.Single [ "Content", "Index" ] ->
Decode.frontmatter Decode.string
[ Decode.attribute "title" Decode.string
]
_ ->
Decode.throw
(3) Run npx elm-frontmatter
Running
Run with npx elm-frontmatter
# Will look for frontmatter files in ./content/ and an elm.json file at ./elm.json.
# Will look for a Content.elm file in the first folder listed in your elm.json's `source-directories`,
# and will output the generated `Content` files into that same folder.
elm-frontmatter
# Will look for frontmatter files in ./md/ and an elm.json file at ./elm/elm.json.
# Will look for a Content.elm file in the first folder listed in your elm.json's `source-directories`,
# and will output the generated `Content` files into that same folder.
elm-frontmatter --elm-json-dir=./elm ./md
# Will look for .md files in ./md/ and an elm.json file at ./elm/elm.json.
# Will look for a Content.elm file in the first folder listed in your elm.json's `source-directories`,
# and will output the generated `Content` files into that same folder.
elm-frontmatter --elm-dir='./src/elm'
# Will look for .md files in ./content/ and an elm.json file at ./elm.json.
# Will look for a Content.elm file at ./src/elm/,
# and will output the generated `Content` files also into ./src/elm.
elm-frontmatter --elm-dir='./src/elm'
# Same as previous except will generate new files without asking for confirmation.
# If `--yes` or `-y` is used, the `--elm-dir` argument must also be provided.
elm-frontmatter --elm-dir='./src/elm' -y
Options
--glob
Will only process frontmatter files in your content directory that match this glob.
Defaults to**/*.md
.--elm-json-dir
The directory that contains your project'selm.json
file. Defaults to.
(current directory).--elm-dir
The directory that contains your project'sMain.elm
file. Defaults to the first directory in yourelm.json
source-directories
array.--yes
/-y
Set this to generate Elm files without asking for permission.--elm-dir
needs to be set to use this argument.
Directory Structure
.
└── content
└── about
| └── content.md --> /Content/About.elm
├── posts
| ├── content.md --> /Content/Posts.elm
| ├── [first-post].md --> /Content/Posts.elm
| ├── [second-post]
| | └── content.md --> /Content/Posts.elm
| └── happy
| ├── banner.jpg --> /image-gen/happy/banner.jpg
| └── ness.md --> /Content/Posts/Happy/Ness.elm
└── quote
├── first.md --> /Content/Quote/First.elm
└── second.md --> /Content/Quote/Second.elm
Once decoded, a generated /Content
folder for this would look like
.
└── Content
└── About.elm
| └── #content : Content
├── Posts.elm
| ├── #content : Content
| ├── #firstPost : CollectionItem
| └── #secondPost : CollectionItem
├── Posts
| └── Happy
| └── Ness.elm
| └── #content : Content
└── Quote
├── First.elm
| └── #content : Content
└── Second.elm
└── #content : Content
content.md
Files
content.md
files are treated similarly to index.html
files in webpages. If there is one in a folder,
it will treat its containing folder name as its file name. This is useful if you want to keep images or other
information colocated with .md files e.g.
.
└── content
└── people
├── [person1]
| ├── content.md
| └── thumbnail.jpg
└── [person2]
├── content.md
└── thumbnail.jpg
will generate
.
└── Content
└── People.elm
├── #person1 : CollectionItem
└── #person2 : CollectionItem
Notes:
- If you have two conflicting files, say
posts.md
andposts/content.md
, one will be overwritten. - Since
content.md
files have special behaviour, having a top levelcontent
file/folder or a[content]
file/folder will throw an error and terminate the content generation.
Singleton vs Collection Item Files
See the Content.Type
module
The two types of files you can have are singleton or collection item files. Collection item files are surrounded by brackets [file-name].md
.
Collection item files share a type with other bracketed files at the same level, and will be generated into the same module.
Singleton files will be turned into a content
function in a module based on their file name. They can share a module with collection item functions, but two singleton functions won't share a module.
When writing your Content.elm#decoder
function, singleton files can be matched using Content.Type.Single [ "Content", "Output", "Module", "Dir" ]
. Collection item files can be matched using Content.Type.Collection [ "Content", "Output", "Module", "Dir" ]
.
module Content exposing (decoder)
import Content.Decode as Decode
import Content.Type as Type
decoder : Type.Path -> Decode.QueryResult
decoder typePath =
case typePath of
-- Will match `content/posts.md`
Type.Single [ "Content", "Posts" ] ->
Decode.frontmatter
[ Decode.attribute "title" Decode.string
, Decode.attribute "allPosts" (Decode.list (Decode.reference (Type.Collection [ "Content", "Posts" ])))
]
-- Will match `content/posts/[first-post].md`, `content/posts/[second-post].md`, etc
Type.Collection [ "Content", "Posts" ] ->
Decode.frontmatter
[ Decode.attribute "title" Decode.string
, Decode.attribute "author" Decode.string
, Decode.attribute "publishedAt" Decode.datetime
]
_ ->
Decode.throw
this decoder will generate a Content/Posts.elm
module that is like
module Content.Posts exposing (Content, CollectionItem, content, firstPost, secondPost)
import Time
type alias Content =
{ title : String
, allPosts : List CollectionItem
}
type alias CollectionItem =
{ title : String
, author : String
, publishedAt : Time.Posix
}
content : Content
content =
...
firstPost : CollectionItem
firstPost =
...
secondPost : CollectionItem
secondPost =
...