Initial commit

This commit is contained in:
Youwen Wu 2024-05-22 22:17:41 -07:00 committed by GitHub
commit 3b41eef57d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 1568 additions and 0 deletions

7
.ghci Normal file
View file

@ -0,0 +1,7 @@
:def hoogle \x -> return $ ":!hoogle --count=15 \"" ++ x ++ "\""
:def doc \x -> return $ ":!hoogle --info \"" ++ x ++ "\""
:set -Wall
:set -fno-warn-type-defaults -ferror-spans -freverse-errors -fprint-expanded-synonyms
:set prompt "\ESC[0;32m%s\n\ESC[m[ghci]\ESC[38;5;172mλ \ESC[m\STX"
:set prompt-cont " \ESC[38;5;172m> \ESC[m"
:load ssg/src/Main.hs

3
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,3 @@
# These are supported funding model platforms
github: rpearce

13
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,13 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: daily
time: '00:00'
timezone: UTC
open-pull-requests-limit: 10
commit-message:
prefix: "chore"
include: "scope"

58
.github/workflows/main.yml vendored Normal file
View file

@ -0,0 +1,58 @@
name: CI
on:
pull_request:
push:
jobs:
build-nix:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@V27
with:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
allow-import-from-derivation = true
auto-optimise-store = true
experimental-features = nix-command flakes
substituters = https://cache.nixos.org https://cache.iog.io
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=
- name: Build with cachix
uses: cachix/cachix-action@v15
with:
name: hakyll-nix-template
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- run: nix build --accept-flake-config
- name: Artifact pages
uses: actions/upload-artifact@v4
with:
name: pages
path: result/dist
deploy:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
needs: [build-nix]
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: pages
path: result
- name: Deploy to GitHub Pages
if: success()
uses: crazy-max/ghaction-github-pages@v4.0.0
with:
build_dir: result
target_branch: gh-pages
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

9
.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
!src/js/dist
.DS_Store
.ghc.environment.*
_cache
_tmp
dist
dist-newstyle
node_modules
result

29
LICENSE Normal file
View file

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2020, Robert Pearce
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

108
README.md Normal file
View file

@ -0,0 +1,108 @@
# hakyll-nix-template
[Hakyll](https://jaspervdj.be/hakyll/) + [Nix](https://nixos.org) template
## Quick tips
* Read the tutorial to get started! https://robertwpearce.com/the-hakyll-nix-template-tutorial.html
* If you make changes to anything inside of `ssg/`, you'll need to clean the
hakyll cache and rebuild. This is the preferred series of commands for
rebuilding (with logs), cleaning the cache, and re-running the dev server:
```default
nix build --print-build-logs && \
nix run . clean && \
nix run . watch
```
## Features
* Build your site into the `./result/dist` folder:
```
λ nix build
```
* Start hakyll's dev server that reloads when changes are made:
```
λ nix run . watch
Listening on http://127.0.0.1:8000
...more logs
```
* Run any hakyll command through `nix run .`!
```
λ nix run . clean
Removing dist...
Removing ssg/_cache...
Removing ssg/_tmp...
```
* Start a development environment that
* has your shell environment
* has `hakyll-site` (for building/watching/cleaning hakyll projects)
* has `hakyll-init` (for generating new projects)
* can have anything else you put in the `shell.buildInputs` of the
`hakyllProject` in `flake.nix`
* is set up to run `ghci` with some defaults and the modules loaded so you can
make your own changes and test them out in the ghci REPL
```
λ nix develop
[hakyll-nix]λ hakyll-site build
...
Success
[hakyll-nix]λ ghci
...
[1 of 1] Compiling Main ( ssg/src/Main.hs, interpreted )
...
λ >
```
### hakyll
All of this is custmomizable, and here are some things that are already done for
you:
* [pandoc](https://github.com/jgm/pandoc/) markdown customization to make it as
close to GitHub's markdown style as possible
* [`slugger`](https://hackage.haskell.org/package/slugger) module is included that makes nice link URIs based on post titles
* RSS & Atom XML feed generation
* Sitemap generation
* Code syntax highlighting customization
* ...other reasonable defaults
Configure the dev server, cache & tmp directories, and more in
`./ssg/src/Main.hs`.
### Deployment
Deployment is set up through a [GitHub
Action](https://github.com/features/actions) with [cachix](https://cachix.org),
and it deploys to a [GitHub Pages](https://pages.github.com/) branch,
`gh-pages`, when you merge code into your main branch.
Setup information can be found below in the "Cachix" section.
Note: If your main branch's name isn't `main`, ensure `'refs/heads/main'` gets
updated to `'refs/heads/my-main-branch'` in `./github/workflows/main.yml`.
## Setup
### Nix & Flakes
If you don't have [nix](https://nixos.org), follow [the nix installation
instructions](https://nixos.org/download.html).
Once you have nix installed, follow the instructions here to get access to
flakes: https://nixos.wiki/wiki/Flakes.
### Cachix
The `./.github/workflows/main.yml` file builds with help from
[cachix](https://app.cachix.org), so you'll need to generate a signing key to be
able to do this.
1. Create a cache on cachix for your project
1. Follow cachix's instructions to generate a signing keypair
1. Copy the signing keypair value to a new `CACHIX_SIGNING_KEY` secret on
https://github.com/settings/secrets

693
flake.lock Normal file
View file

@ -0,0 +1,693 @@
{
"nodes": {
"HTTP": {
"flake": false,
"locked": {
"lastModified": 1451647621,
"narHash": "sha256-oHIyw3x0iKBexEo49YeUDV1k74ZtyYKGR2gNJXXRxts=",
"owner": "phadej",
"repo": "HTTP",
"rev": "9bc0996d412fef1787449d841277ef663ad9a915",
"type": "github"
},
"original": {
"owner": "phadej",
"repo": "HTTP",
"type": "github"
}
},
"cabal-32": {
"flake": false,
"locked": {
"lastModified": 1603716527,
"narHash": "sha256-X0TFfdD4KZpwl0Zr6x+PLxUt/VyKQfX7ylXHdmZIL+w=",
"owner": "haskell",
"repo": "cabal",
"rev": "48bf10787e27364730dd37a42b603cee8d6af7ee",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "3.2",
"repo": "cabal",
"type": "github"
}
},
"cabal-34": {
"flake": false,
"locked": {
"lastModified": 1645834128,
"narHash": "sha256-wG3d+dOt14z8+ydz4SL7pwGfe7SiimxcD/LOuPCV6xM=",
"owner": "haskell",
"repo": "cabal",
"rev": "5ff598c67f53f7c4f48e31d722ba37172230c462",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "3.4",
"repo": "cabal",
"type": "github"
}
},
"cabal-36": {
"flake": false,
"locked": {
"lastModified": 1669081697,
"narHash": "sha256-I5or+V7LZvMxfbYgZATU4awzkicBwwok4mVoje+sGmU=",
"owner": "haskell",
"repo": "cabal",
"rev": "8fd619e33d34924a94e691c5fea2c42f0fc7f144",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "3.6",
"repo": "cabal",
"type": "github"
}
},
"cardano-shell": {
"flake": false,
"locked": {
"lastModified": 1608537748,
"narHash": "sha256-PulY1GfiMgKVnBci3ex4ptk2UNYMXqGjJOxcPy2KYT4=",
"owner": "input-output-hk",
"repo": "cardano-shell",
"rev": "9392c75087cb9a3d453998f4230930dea3a95725",
"type": "github"
},
"original": {
"owner": "input-output-hk",
"repo": "cardano-shell",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1672831974,
"narHash": "sha256-z9k3MfslLjWQfnjBtEtJZdq3H7kyi2kQtUThfTgdRk0=",
"owner": "input-output-hk",
"repo": "flake-compat",
"rev": "45f2638735f8cdc40fe302742b79f248d23eb368",
"type": "github"
},
"original": {
"owner": "input-output-hk",
"ref": "hkm/gitlab-fix",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1705309234,
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"ghc-8.6.5-iohk": {
"flake": false,
"locked": {
"lastModified": 1600920045,
"narHash": "sha256-DO6kxJz248djebZLpSzTGD6s8WRpNI9BTwUeOf5RwY8=",
"owner": "input-output-hk",
"repo": "ghc",
"rev": "95713a6ecce4551240da7c96b6176f980af75cae",
"type": "github"
},
"original": {
"owner": "input-output-hk",
"ref": "release/8.6.5-iohk",
"repo": "ghc",
"type": "github"
}
},
"ghc98X": {
"flake": false,
"locked": {
"lastModified": 1696643148,
"narHash": "sha256-E02DfgISH7EvvNAu0BHiPvl1E5FGMDi0pWdNZtIBC9I=",
"ref": "ghc-9.8",
"rev": "443e870d977b1ab6fc05f47a9a17bc49296adbd6",
"revCount": 61642,
"submodules": true,
"type": "git",
"url": "https://gitlab.haskell.org/ghc/ghc"
},
"original": {
"ref": "ghc-9.8",
"submodules": true,
"type": "git",
"url": "https://gitlab.haskell.org/ghc/ghc"
}
},
"ghc99": {
"flake": false,
"locked": {
"lastModified": 1701580282,
"narHash": "sha256-drA01r3JrXnkKyzI+owMZGxX0JameMzjK0W5jJE/+V4=",
"ref": "refs/heads/master",
"rev": "f5eb0f2982e9cf27515e892c4bdf634bcfb28459",
"revCount": 62197,
"submodules": true,
"type": "git",
"url": "https://gitlab.haskell.org/ghc/ghc"
},
"original": {
"submodules": true,
"type": "git",
"url": "https://gitlab.haskell.org/ghc/ghc"
}
},
"hackage": {
"flake": false,
"locked": {
"lastModified": 1706574159,
"narHash": "sha256-OcE1NT4zQfV4h8Ybq1ttNT+TkXjTV1O2doiCfJzjCbw=",
"owner": "input-output-hk",
"repo": "hackage.nix",
"rev": "ccff69cc942afd51dbdd7466192e3f4cfe09cde2",
"type": "github"
},
"original": {
"owner": "input-output-hk",
"repo": "hackage.nix",
"type": "github"
}
},
"haskellNix": {
"inputs": {
"HTTP": "HTTP",
"cabal-32": "cabal-32",
"cabal-34": "cabal-34",
"cabal-36": "cabal-36",
"cardano-shell": "cardano-shell",
"flake-compat": "flake-compat",
"ghc-8.6.5-iohk": "ghc-8.6.5-iohk",
"ghc98X": "ghc98X",
"ghc99": "ghc99",
"hackage": "hackage",
"hls-1.10": "hls-1.10",
"hls-2.0": "hls-2.0",
"hls-2.2": "hls-2.2",
"hls-2.3": "hls-2.3",
"hls-2.4": "hls-2.4",
"hls-2.5": "hls-2.5",
"hls-2.6": "hls-2.6",
"hpc-coveralls": "hpc-coveralls",
"hydra": "hydra",
"iserv-proxy": "iserv-proxy",
"nix-tools-static": "nix-tools-static",
"nixpkgs": [
"haskellNix",
"nixpkgs-unstable"
],
"nixpkgs-2003": "nixpkgs-2003",
"nixpkgs-2105": "nixpkgs-2105",
"nixpkgs-2111": "nixpkgs-2111",
"nixpkgs-2205": "nixpkgs-2205",
"nixpkgs-2211": "nixpkgs-2211",
"nixpkgs-2305": "nixpkgs-2305",
"nixpkgs-2311": "nixpkgs-2311",
"nixpkgs-unstable": "nixpkgs-unstable",
"old-ghc-nix": "old-ghc-nix",
"stackage": "stackage"
},
"locked": {
"lastModified": 1706575804,
"narHash": "sha256-/2oEg1CD3FtXOsF0dQm/5RLGZ5LwILA5Gnuw/FkNm4w=",
"owner": "input-output-hk",
"repo": "haskell.nix",
"rev": "8bc501003577cf9a1077f04d2dabf58f68b256b6",
"type": "github"
},
"original": {
"owner": "input-output-hk",
"repo": "haskell.nix",
"type": "github"
}
},
"hls-1.10": {
"flake": false,
"locked": {
"lastModified": 1680000865,
"narHash": "sha256-rc7iiUAcrHxwRM/s0ErEsSPxOR3u8t7DvFeWlMycWgo=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "b08691db779f7a35ff322b71e72a12f6e3376fd9",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "1.10.0.0",
"repo": "haskell-language-server",
"type": "github"
}
},
"hls-2.0": {
"flake": false,
"locked": {
"lastModified": 1687698105,
"narHash": "sha256-OHXlgRzs/kuJH8q7Sxh507H+0Rb8b7VOiPAjcY9sM1k=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "783905f211ac63edf982dd1889c671653327e441",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "2.0.0.1",
"repo": "haskell-language-server",
"type": "github"
}
},
"hls-2.2": {
"flake": false,
"locked": {
"lastModified": 1693064058,
"narHash": "sha256-8DGIyz5GjuCFmohY6Fa79hHA/p1iIqubfJUTGQElbNk=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "b30f4b6cf5822f3112c35d14a0cba51f3fe23b85",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "2.2.0.0",
"repo": "haskell-language-server",
"type": "github"
}
},
"hls-2.3": {
"flake": false,
"locked": {
"lastModified": 1695910642,
"narHash": "sha256-tR58doOs3DncFehHwCLczJgntyG/zlsSd7DgDgMPOkI=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "458ccdb55c9ea22cd5d13ec3051aaefb295321be",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "2.3.0.0",
"repo": "haskell-language-server",
"type": "github"
}
},
"hls-2.4": {
"flake": false,
"locked": {
"lastModified": 1699862708,
"narHash": "sha256-YHXSkdz53zd0fYGIYOgLt6HrA0eaRJi9mXVqDgmvrjk=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "54507ef7e85fa8e9d0eb9a669832a3287ffccd57",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "2.4.0.1",
"repo": "haskell-language-server",
"type": "github"
}
},
"hls-2.5": {
"flake": false,
"locked": {
"lastModified": 1701080174,
"narHash": "sha256-fyiR9TaHGJIIR0UmcCb73Xv9TJq3ht2ioxQ2mT7kVdc=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "27f8c3d3892e38edaef5bea3870161815c4d014c",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "2.5.0.0",
"repo": "haskell-language-server",
"type": "github"
}
},
"hls-2.6": {
"flake": false,
"locked": {
"lastModified": 1705325287,
"narHash": "sha256-+P87oLdlPyMw8Mgoul7HMWdEvWP/fNlo8jyNtwME8E8=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "6e0b342fa0327e628610f2711f8c3e4eaaa08b1e",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "2.6.0.0",
"repo": "haskell-language-server",
"type": "github"
}
},
"hpc-coveralls": {
"flake": false,
"locked": {
"lastModified": 1607498076,
"narHash": "sha256-8uqsEtivphgZWYeUo5RDUhp6bO9j2vaaProQxHBltQk=",
"owner": "sevanspowell",
"repo": "hpc-coveralls",
"rev": "14df0f7d229f4cd2e79f8eabb1a740097fdfa430",
"type": "github"
},
"original": {
"owner": "sevanspowell",
"repo": "hpc-coveralls",
"type": "github"
}
},
"hydra": {
"inputs": {
"nix": "nix",
"nixpkgs": [
"haskellNix",
"hydra",
"nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1671755331,
"narHash": "sha256-hXsgJj0Cy0ZiCiYdW2OdBz5WmFyOMKuw4zyxKpgUKm4=",
"owner": "NixOS",
"repo": "hydra",
"rev": "f48f00ee6d5727ae3e488cbf9ce157460853fea8",
"type": "github"
},
"original": {
"id": "hydra",
"type": "indirect"
}
},
"iserv-proxy": {
"flake": false,
"locked": {
"lastModified": 1691634696,
"narHash": "sha256-MZH2NznKC/gbgBu8NgIibtSUZeJ00HTLJ0PlWKCBHb0=",
"ref": "hkm/remote-iserv",
"rev": "43a979272d9addc29fbffc2e8542c5d96e993d73",
"revCount": 14,
"type": "git",
"url": "https://gitlab.haskell.org/hamishmack/iserv-proxy.git"
},
"original": {
"ref": "hkm/remote-iserv",
"type": "git",
"url": "https://gitlab.haskell.org/hamishmack/iserv-proxy.git"
}
},
"lowdown-src": {
"flake": false,
"locked": {
"lastModified": 1633514407,
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
"owner": "kristapsdz",
"repo": "lowdown",
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
"type": "github"
},
"original": {
"owner": "kristapsdz",
"repo": "lowdown",
"type": "github"
}
},
"nix": {
"inputs": {
"lowdown-src": "lowdown-src",
"nixpkgs": "nixpkgs",
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1661606874,
"narHash": "sha256-9+rpYzI+SmxJn+EbYxjGv68Ucp22bdFUSy/4LkHkkDQ=",
"owner": "NixOS",
"repo": "nix",
"rev": "11e45768b34fdafdcf019ddbd337afa16127ff0f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "2.11.0",
"repo": "nix",
"type": "github"
}
},
"nix-tools-static": {
"flake": false,
"locked": {
"lastModified": 1706266250,
"narHash": "sha256-9t+GRk3eO9muCtKdNAwBtNBZ5dH1xHcnS17WaQyftwA=",
"owner": "input-output-hk",
"repo": "haskell-nix-example",
"rev": "580cb6db546a7777dad3b9c0fa487a366c045c4e",
"type": "github"
},
"original": {
"owner": "input-output-hk",
"ref": "nix",
"repo": "haskell-nix-example",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1657693803,
"narHash": "sha256-G++2CJ9u0E7NNTAi9n5G8TdDmGJXcIjkJ3NF8cetQB8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "365e1b3a859281cf11b94f87231adeabbdd878a2",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-22.05-small",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-2003": {
"locked": {
"lastModified": 1620055814,
"narHash": "sha256-8LEHoYSJiL901bTMVatq+rf8y7QtWuZhwwpKE2fyaRY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1db42b7fe3878f3f5f7a4f2dc210772fd080e205",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-20.03-darwin",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-2105": {
"locked": {
"lastModified": 1659914493,
"narHash": "sha256-lkA5X3VNMKirvA+SUzvEhfA7XquWLci+CGi505YFAIs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "022caabb5f2265ad4006c1fa5b1ebe69fb0c3faf",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-21.05-darwin",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-2111": {
"locked": {
"lastModified": 1659446231,
"narHash": "sha256-hekabNdTdgR/iLsgce5TGWmfIDZ86qjPhxDg/8TlzhE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "eabc38219184cc3e04a974fe31857d8e0eac098d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-21.11-darwin",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-2205": {
"locked": {
"lastModified": 1685573264,
"narHash": "sha256-Zffu01pONhs/pqH07cjlF10NnMDLok8ix5Uk4rhOnZQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "380be19fbd2d9079f677978361792cb25e8a3635",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-22.05-darwin",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-2211": {
"locked": {
"lastModified": 1688392541,
"narHash": "sha256-lHrKvEkCPTUO+7tPfjIcb7Trk6k31rz18vkyqmkeJfY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "ea4c80b39be4c09702b0cb3b42eab59e2ba4f24b",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-22.11-darwin",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-2305": {
"locked": {
"lastModified": 1701362232,
"narHash": "sha256-GVdzxL0lhEadqs3hfRLuj+L1OJFGiL/L7gCcelgBlsw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d2332963662edffacfddfad59ff4f709dde80ffe",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-23.05-darwin",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-2311": {
"locked": {
"lastModified": 1701386440,
"narHash": "sha256-xI0uQ9E7JbmEy/v8kR9ZQan6389rHug+zOtZeZFiDJk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "293822e55ec1872f715a66d0eda9e592dc14419f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-23.11-darwin",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-regression": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1694822471,
"narHash": "sha256-6fSDCj++lZVMZlyqOe9SIOL8tYSBz1bI8acwovRwoX8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "47585496bcb13fb72e4a90daeea2f434e2501998",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "47585496bcb13fb72e4a90daeea2f434e2501998",
"type": "github"
}
},
"old-ghc-nix": {
"flake": false,
"locked": {
"lastModified": 1631092763,
"narHash": "sha256-sIKgO+z7tj4lw3u6oBZxqIhDrzSkvpHtv0Kki+lh9Fg=",
"owner": "angerman",
"repo": "old-ghc-nix",
"rev": "af48a7a7353e418119b6dfe3cd1463a657f342b8",
"type": "github"
},
"original": {
"owner": "angerman",
"ref": "master",
"repo": "old-ghc-nix",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"haskellNix": "haskellNix",
"nixpkgs": [
"haskellNix",
"nixpkgs-unstable"
]
}
},
"stackage": {
"flake": false,
"locked": {
"lastModified": 1706573344,
"narHash": "sha256-nOc8eVWcS//QjV4tdZlJVMWPyGauD71BNdCXjvTRXDs=",
"owner": "input-output-hk",
"repo": "stackage.nix",
"rev": "c911e99f7ab959a8d2b83c038a803c255a6d215a",
"type": "github"
},
"original": {
"owner": "input-output-hk",
"repo": "stackage.nix",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

93
flake.nix Normal file
View file

@ -0,0 +1,93 @@
{
description = "hakyll-nix-template";
nixConfig = {
allow-import-from-derivation = "true";
bash-prompt = "[hakyll-nix]λ ";
extra-substituters = [
"https://cache.iog.io"
];
extra-trusted-public-keys = [
"hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ="
];
};
inputs.haskellNix.url = "github:input-output-hk/haskell.nix";
inputs.nixpkgs.follows = "haskellNix/nixpkgs-unstable";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = { self, nixpkgs, flake-utils, haskellNix }:
flake-utils.lib.eachDefaultSystem (system:
let
overlays = [ haskellNix.overlay
(final: prev: {
hakyllProject = final.haskell-nix.project' {
src = ./ssg;
compiler-nix-name = "ghc948";
modules = [{ doHaddock = false; }];
shell.buildInputs = [
hakyll-site
];
shell.tools = {
cabal = "latest";
hlint = "latest";
haskell-language-server = "latest";
};
};
})
];
pkgs = import nixpkgs {
inherit overlays system;
inherit (haskellNix) config;
};
flake = pkgs.hakyllProject.flake {};
executable = "ssg:exe:hakyll-site";
hakyll-site = flake.packages.${executable};
website = pkgs.stdenv.mkDerivation {
name = "website";
buildInputs = [];
src = pkgs.nix-gitignore.gitignoreSourcePure [
./.gitignore
".git"
".github"
] ./.;
# LANG and LOCALE_ARCHIVE are fixes pulled from the community:
# https://github.com/jaspervdj/hakyll/issues/614#issuecomment-411520691
# https://github.com/NixOS/nix/issues/318#issuecomment-52986702
# https://github.com/MaxDaten/brutal-recipes/blob/source/default.nix#L24
LANG = "en_US.UTF-8";
LOCALE_ARCHIVE = pkgs.lib.optionalString
(pkgs.buildPlatform.libc == "glibc")
"${pkgs.glibcLocales}/lib/locale/locale-archive";
buildPhase = ''
${flake.packages.${executable}}/bin/hakyll-site build --verbose
'';
installPhase = ''
mkdir -p "$out/dist"
cp -a dist/. "$out/dist"
'';
};
in flake // rec {
apps = {
default = flake-utils.lib.mkApp {
drv = hakyll-site;
exePath = "/bin/hakyll-site";
};
};
packages = {
inherit hakyll-site website;
default = website;
};
}
);
}

1
src/CNAME Normal file
View file

@ -0,0 +1 @@
mywebsite123456789123456789.com

8
src/_config.yaml Normal file
View file

@ -0,0 +1,8 @@
# This is a Jekyll config file. Jekyll is used by GitHub
# Pages to manage static site generation / distribution.
# If you don't use GitHub Pages or don't need to customize
# it, you can delete this file and remove its entry in
# `../ssg/src/Main.hs`.
#
# https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/about-github-pages-and-jekyll
include: []

54
src/css/default.css Normal file
View file

@ -0,0 +1,54 @@
:root {
font-size: 62.5%;
box-sizing: border-box;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
html,
body {
min-height: 100vh;
}
body {
font-kerning: normal;
-moz-font-feature-settings: "kern", "liga", "clig", "calt";
-ms-font-feature-settings: "kern", "liga", "clig", "calt";
-webkit-font-feature-settings: "kern", "liga", "clig", "calt";
font-feature-settings: "kern", "liga", "clig", "calt";
scroll-behavior: smooth;
font-size: 2rem;
}
body,
input,
button {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1,
h2 {
font-family: "Palatino Linotype", "Book Antiqua", Palatino, serif;
}
h1 {
font-size: 4.0rem;
}
h2 {
font-size: 2.6rem;
}
small,
p {
font-family: Tahoma, Arial, sans-serif;
}
small {
font-size: 1.6rem;
font-style: italic;
}
p {
font-size: 1.8rem;
}
pre.sourceCode {
padding: 2rem 1.5rem;
}

BIN
src/favicon.ico Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

29
src/index.html Normal file
View file

@ -0,0 +1,29 @@
---
desc: "This is me saying hello to the world"
image: "./images/robert-pearce-UwHN0jU_YqQ-unsplash-800w.jpg"
lang: "en"
stylesheet: "default"
title: "Hello, world!"
---
<header>
<h1>Hello, world!</h1>
<img
alt="A woman sitting on a bench amongst trees at the end of a boardwalk leading to a pond with mountains in the background"
src="./images/robert-pearce-UwHN0jU_YqQ-unsplash-800w.jpg"
height="300"
/>
</header>
<main>
<section>
<h2>Blog Posts</h2>
<ul>
$for(posts)$
<li>
<div><a href=".$url$">$title$</a></div>
<small>$date$</small>
</li>
$endfor$
</ul>
</section>
</main>

3
src/js/script.js Normal file
View file

@ -0,0 +1,3 @@
;(function() {
console.log('Hello, world!');
})();

View file

@ -0,0 +1,26 @@
---
author: "My name"
authorTwitter: "@MyName"
desc: "I announce myself to the world"
image: "./images/waiheke-stony-batter.jpg"
keywords: "hello, announcement"
lang: "en"
title: "Hello, world!"
updated: "2020-09-22T12:00:00Z"
---
Hello, world! I am here!
<img
alt="Grapevines among rolling hills leading to the sea"
src="./images/waiheke-stony-batter.jpg"
height="200"
/>
Haskell, for example:
```haskell
toSlug :: T.Text -> T.Text
toSlug =
T.intercalate (T.singleton '-') . T.words . T.toLower . clean
```

View file

@ -0,0 +1,26 @@
---
author: "Mi nombre"
authorTwitter: "@MiNombre"
desc: "Me anuncio al mundo"
image: "./images/waiheke-stony-batter.jpg"
keywords: "hola, anuncio"
lang: "es"
title: "¡Hola Mundo!"
updated: "2020-09-23T12:00:00Z"
---
¡Hola Mundo! ¡Estoy aquí!
<img
alt="Grapevines among rolling hills leading to the sea"
src="./images/waiheke-stony-batter.jpg"
height="200"
/>
Haskell, por ejemplo:
```haskell
toSlug :: T.Text -> T.Text
toSlug =
T.intercalate (T.singleton '-') . T.words . T.toLower . clean
```

2
src/robots.txt Normal file
View file

@ -0,0 +1,2 @@
User-agent: *
Disallow:

View file

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="$lang$">
<head>
<title>$title$</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="$desc$">
$if(author)$
<meta name="author" content="$author$">
$endif$
$if(keywords)$
<meta name="keywords" content="$keywords$">
$endif$
<meta property="og:site_name" content="$siteName$">
<meta property="og:title" content="$title$">
<meta property="og:url" content="$root$$url$">
<meta property="og:description" content="$desc$">
$if(image)$
<meta property="og:image" content="$root$$image$">
$endif$
$if(type)$
<meta property="og:type" content="$type$">
$else$
<meta property="og:type" content="website">
$endif$
$if(image)$
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:image" content="$root$$image$">
$endif$
<meta property="twitter:site" content="$siteName$">
<meta property="twitter:title" content="$title$">
<meta property="twitter:description" content="$desc$">
$if(authorTwitter)$
<meta property="twitter:creator" content="$authorTwitter$">
$endif$
<link rel="shortcut icon" href="/favicon.ico">
<link rel="canonical" href="$root$$url$">
<link rel="alternate" href="./atom.xml" title="$feedTitle$" type="application/atom+xml">
<link rel="alternate" href="./rss.xml" title="$feedTitle$" type="application/rss+xml">
<link rel="stylesheet" href="./css/default.css" />
<link rel="stylesheet" href="./css/code.css" />
</head>
<body>
$body$
<script defer src="./js/script.js"></script>
</body>
</html>

15
src/templates/post.html Normal file
View file

@ -0,0 +1,15 @@
<main>
<article>
<header>
<h1>
<a href=".$url$">$title$</a>
</h1>
<div>
$date$ $if(updated)$(updated: $updated$)$endif$
</div>
</header>
<section>
$body$
</section>
</article>
</main>

16
src/templates/sitemap.xml Normal file
View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
<url>
<loc>$root$</loc>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
$for(pages)$
<url>
<loc>$root$$url$</loc>
<lastmod>$if(updated)$$updated$$else$$if(date)$$date$$endif$$endif$</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
$endfor$
</urlset>

29
ssg/LICENSE Normal file
View file

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2020, Robert Pearce
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

264
ssg/src/Main.hs Normal file
View file

@ -0,0 +1,264 @@
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad (forM_)
import Data.List (isPrefixOf, isSuffixOf)
import Data.Maybe (fromMaybe)
import Hakyll
import qualified Data.Text as T
import qualified Data.Text.Slugger as Slugger
import System.FilePath (takeFileName)
import Text.Pandoc
( Extension (Ext_fenced_code_attributes, Ext_footnotes, Ext_gfm_auto_identifiers, Ext_implicit_header_references, Ext_smart),
Extensions,
ReaderOptions,
WriterOptions (writerHighlightStyle),
extensionsFromList,
githubMarkdownExtensions,
readerExtensions,
writerExtensions,
)
import Text.Pandoc.Highlighting (Style, breezeDark, styleToCss)
--------------------------------------------------------------------------------
-- PERSONALIZATION
mySiteName :: String
mySiteName = "My Site Name"
mySiteRoot :: String
mySiteRoot = "https://my-site.com"
myFeedTitle :: String
myFeedTitle = "My Feed Title"
myFeedDescription :: String
myFeedDescription = "My Site Description"
myFeedAuthorName :: String
myFeedAuthorName = "My Name"
myFeedAuthorEmail :: String
myFeedAuthorEmail = "me@myemail.com"
myFeedRoot :: String
myFeedRoot = mySiteRoot
--------------------------------------------------------------------------------
-- CONFIG
-- Default configuration: https://github.com/jaspervdj/hakyll/blob/cd74877d41f41c4fba27768f84255e797748a31a/lib/Hakyll/Core/Configuration.hs#L101-L125
config :: Configuration
config =
defaultConfiguration
{ destinationDirectory = "dist"
, ignoreFile = ignoreFile'
, previewHost = "127.0.0.1"
, previewPort = 8000
, providerDirectory = "src"
, storeDirectory = "ssg/_cache"
, tmpDirectory = "ssg/_tmp"
}
where
ignoreFile' path
| "." `isPrefixOf` fileName = False
| "#" `isPrefixOf` fileName = True
| "~" `isSuffixOf` fileName = True
| ".swp" `isSuffixOf` fileName = True
| otherwise = False
where
fileName = takeFileName path
--------------------------------------------------------------------------------
-- BUILD
main :: IO ()
main = hakyllWith config $ do
forM_
[ "CNAME"
, "favicon.ico"
, "robots.txt"
, "_config.yml"
, "images/*"
, "js/*"
, "fonts/*"
]
$ \f -> match f $ do
route idRoute
compile copyFileCompiler
match "css/*" $ do
route idRoute
compile compressCssCompiler
match "posts/*" $ do
let ctx = constField "type" "article" <> postCtx
route $ metadataRoute titleRoute
compile $
pandocCompilerCustom
>>= loadAndApplyTemplate "templates/post.html" ctx
>>= saveSnapshot "content"
>>= loadAndApplyTemplate "templates/default.html" ctx
match "index.html" $ do
route idRoute
compile $ do
posts <- recentFirst =<< loadAll "posts/*"
let indexCtx =
listField "posts" postCtx (return posts)
<> constField "root" mySiteRoot
<> constField "feedTitle" myFeedTitle
<> constField "siteName" mySiteName
<> defaultContext
getResourceBody
>>= applyAsTemplate indexCtx
>>= loadAndApplyTemplate "templates/default.html" indexCtx
match "templates/*" $
compile templateBodyCompiler
create ["sitemap.xml"] $ do
route idRoute
compile $ do
posts <- recentFirst =<< loadAll "posts/*"
let pages = posts
sitemapCtx =
constField "root" mySiteRoot
<> constField "siteName" mySiteName
<> listField "pages" postCtx (return pages)
makeItem ("" :: String)
>>= loadAndApplyTemplate "templates/sitemap.xml" sitemapCtx
create ["rss.xml"] $ do
route idRoute
compile (feedCompiler renderRss)
create ["atom.xml"] $ do
route idRoute
compile (feedCompiler renderAtom)
create ["css/code.css"] $ do
route idRoute
compile (makeStyle pandocHighlightStyle)
--------------------------------------------------------------------------------
-- COMPILER HELPERS
makeStyle :: Style -> Compiler (Item String)
makeStyle =
makeItem . compressCss . styleToCss
--------------------------------------------------------------------------------
-- CONTEXT
feedCtx :: Context String
feedCtx =
titleCtx
<> postCtx
<> bodyField "description"
postCtx :: Context String
postCtx =
constField "root" mySiteRoot
<> constField "feedTitle" myFeedTitle
<> constField "siteName" mySiteName
<> dateField "date" "%Y-%m-%d"
<> defaultContext
titleCtx :: Context String
titleCtx =
field "title" updatedTitle
--------------------------------------------------------------------------------
-- TITLE HELPERS
replaceAmp :: String -> String
replaceAmp =
replaceAll "&" (const "&amp;")
replaceTitleAmp :: Metadata -> String
replaceTitleAmp =
replaceAmp . safeTitle
safeTitle :: Metadata -> String
safeTitle =
fromMaybe "no title" . lookupString "title"
updatedTitle :: Item a -> Compiler String
updatedTitle =
fmap replaceTitleAmp . getMetadata . itemIdentifier
--------------------------------------------------------------------------------
-- PANDOC
pandocCompilerCustom :: Compiler (Item String)
pandocCompilerCustom =
pandocCompilerWith pandocReaderOpts pandocWriterOpts
pandocExtensionsCustom :: Extensions
pandocExtensionsCustom =
githubMarkdownExtensions
<> extensionsFromList
[ Ext_fenced_code_attributes
, Ext_gfm_auto_identifiers
, Ext_implicit_header_references
, Ext_smart
, Ext_footnotes
]
pandocReaderOpts :: ReaderOptions
pandocReaderOpts =
defaultHakyllReaderOptions
{ readerExtensions = pandocExtensionsCustom
}
pandocWriterOpts :: WriterOptions
pandocWriterOpts =
defaultHakyllWriterOptions
{ writerExtensions = pandocExtensionsCustom
, writerHighlightStyle = Just pandocHighlightStyle
}
pandocHighlightStyle :: Style
pandocHighlightStyle =
breezeDark -- https://hackage.haskell.org/package/pandoc/docs/Text-Pandoc-Highlighting.html
--------------------------------------------------------------------------------
-- FEEDS
type FeedRenderer =
FeedConfiguration ->
Context String ->
[Item String] ->
Compiler (Item String)
feedCompiler :: FeedRenderer -> Compiler (Item String)
feedCompiler renderer =
renderer feedConfiguration feedCtx
=<< recentFirst
=<< loadAllSnapshots "posts/*" "content"
feedConfiguration :: FeedConfiguration
feedConfiguration =
FeedConfiguration
{ feedTitle = myFeedTitle
, feedDescription = myFeedDescription
, feedAuthorName = myFeedAuthorName
, feedAuthorEmail = myFeedAuthorEmail
, feedRoot = myFeedRoot
}
--------------------------------------------------------------------------------
-- CUSTOM ROUTE
fileNameFromTitle :: Metadata -> FilePath
fileNameFromTitle =
T.unpack . (`T.append` ".html") . Slugger.toSlug . T.pack . safeTitle
titleRoute :: Metadata -> Routes
titleRoute =
constRoute . fileNameFromTitle

29
ssg/ssg.cabal Normal file
View file

@ -0,0 +1,29 @@
cabal-version: 3.6
name: ssg
version: 0.1.0.0
build-type: Simple
license: BSD-3-Clause
license-file: LICENSE
executable hakyll-site
default-language: Haskell2010
main-is: Main.hs
hs-source-dirs: src
build-depends: base == 4.*
, hakyll == 4.15.*
, filepath >= 1.0
, pandoc >= 2.11
, slugger >= 0.1.0.2
, text >= 1 && < 3
ghc-options: -Wall
-Wcompat
-Widentities
-Wincomplete-record-updates
-Wincomplete-uni-patterns
-Wpartial-fields
-Wredundant-constraints
-O2
-threaded
-rtsopts
-with-rtsopts=-N