From 0a7e04f8442e26e8e18a3a83a891f43344fd0e33 Mon Sep 17 00:00:00 2001 From: Alexander Artemenko Date: Tue, 30 Apr 2024 15:55:30 +0300 Subject: [PATCH 1/4] More docs --- docs/index.lisp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/index.lisp b/docs/index.lisp index e97f908..3646e13 100644 --- a/docs/index.lisp +++ b/docs/index.lisp @@ -80,7 +80,8 @@ You can install this library from Quicklisp, but you want to receive updates qui (defsection @usage (:title "Usage") " -TODO: Write a library description. Put some examples here. +StatiCL is a static site generator. StatiCL as a modular architecture and is suitable for any kind of a site +be it a blog or a site with pages not included in the feeds. This project was created to overcome limitations of the Coleslaw. ") From f8c83cfe2019ead73db9d767f06c3c28906ce2a3 Mon Sep 17 00:00:00 2001 From: Alexander Artemenko Date: Sat, 4 May 2024 10:12:23 +0300 Subject: [PATCH 2/4] Made an introduction documentation. --- .gitignore | 4 +- ChangeLog.md | 14 +- README.md | 1381 +++++++++++++++++++++++++++++++- docs/index.lisp | 102 ++- docs/introduction.lisp | 83 ++ docs/tutorial/step1/.staticlrc | 8 + docs/tutorial/step1/index.page | 8 + docs/tutorial/step2/.staticlrc | 9 + docs/tutorial/step2/index.page | 8 + docs/tutorial/step3/.staticlrc | 10 + docs/tutorial/step3/index.page | 8 + src/content-pipeline.lisp | 3 +- src/content.lisp | 5 +- src/content/post.lisp | 3 +- src/core.lisp | 4 +- src/filter.lisp | 4 +- src/index/base.lisp | 1 - src/index/paginated.lisp | 4 +- src/links/link.lisp | 4 +- src/rsync.lisp | 9 +- src/site.lisp | 6 +- src/theme.lisp | 3 +- 22 files changed, 1623 insertions(+), 58 deletions(-) create mode 100644 docs/introduction.lisp create mode 100644 docs/tutorial/step1/.staticlrc create mode 100644 docs/tutorial/step1/index.page create mode 100644 docs/tutorial/step2/.staticlrc create mode 100644 docs/tutorial/step2/index.page create mode 100644 docs/tutorial/step3/.staticlrc create mode 100644 docs/tutorial/step3/index.page diff --git a/.gitignore b/.gitignore index 158176f..328ac45 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,6 @@ .#* .*.~undo-tree~ *.fasl -/example/stage/ -/stage/ +stage/ .DS_Store +roswell/staticl diff --git a/ChangeLog.md b/ChangeLog.md index c961348..fe5b0dc 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1 +1,13 @@ -This file will be autogenerated by GitHub action. + + +# ChangeLog + + + +## 0.1.0 (2023-02-05) + +* Initial version. + + +* * * +###### [generated by [40ANTS-DOC](https://40ants.com/doc/)] diff --git a/README.md b/README.md index c961348..d1d750e 100644 --- a/README.md +++ b/README.md @@ -1 +1,1380 @@ -This file will be autogenerated by GitHub action. + + +# staticl - Flexible static site generator. + + + +## STATICL ASDF System Details + +* Version: 0.1.0 +* Description: Flexible and customizable static site generator with a lot of plugins! +* Licence: Unlicense +* Author: Alexander Artemenko +* Homepage: [https://40ants.com/staticl/][0cf8] +* Bug tracker: [https://github.com/40ants/staticl/issues][5825] +* Source control: [GIT][2594] +* Depends on: [3bmd][cc3e], [3bmd-ext-code-blocks][d94a], [alexandria][8236], [cl-fad][1059], [cl-ppcre][49b9], [cl-sitemaps][dfc0], [closer-mop][61a4], [closure-template][2700], [feeder][0b13], [fuzzy-dates][ed60], [local-time][46a1], [log4cl][7f8b], [quri][2103], [serapeum][c41d], [str][ef7f], [utilities.print-items][52a0] + +[![](https://github-actions.40ants.com/40ants/staticl/matrix.svg?only=ci.run-tests)][ba40] + +![](http://quickdocs.org/badge/staticl.svg) + + + +## Installation + +You can install this library from Quicklisp, but you want to receive updates quickly, then install it from Ultralisp.org: + +``` +(ql-dist:install-dist "http://dist.ultralisp.org/" + :prompt nil) +(ql:quickload :staticl) +``` + + +## Usage + +`StatiCL` is a static site generator. `StatiCL` as a modular architecture and is suitable for any kind of a site +be it a blog or a site with pages not included in the feeds. This project was created to overcome limitations of the Coleslaw. + + + +## Processing Pipeline + +First of all, a `STATICL:SITE` object is created and filled with options from `.staticlrc` file. + +Then [`staticl:stage`][c5b7] function calls `STATICL/CONTENT:READ-CONTENT` generic-function which returns a list +of [`staticl/content:content`][a8cd] objects. On the next stage, initial list of content objects are passed to a +generic-function [`staticl/content:preprocess`][d70a] called with a preprocessor returned by a +generic-function `STATICL/PLUGINS:SITE-PLUGINS` as a first argument. Each preprocessor may +return additional [`staticl/content:content`][a8cd] objects such as index pages, `RSS` or `ATOM` feeds, sitemaps etc. + +When all content was preprocessed, a generic-function [`staticl/content:write-content`][c4c2] is called +on each [`staticl/content:content`][a8cd] object and a `SITE` object. Content objects are having a format slot, +so internally [`staticl/content:write-content`][c4c2] generic-function creates an object of corresponding format class +or takes it from the cache and then calls [`staticl/content:write-content-to-stream`][96e3] using this format object. + + + +## API + + + +### STATICL + + + +#### [package](592c) `staticl` + + + +#### Functions + + + +##### [function](c8d0) `staticl:stage` &KEY (ROOT-DIR \*DEFAULT-PATHNAME-DEFAULTS\*) (STAGE-DIR (MERGE-PATHNAMES (MAKE-PATHNAME :DIRECTORY '(:RELATIVE "stage")) + (UIOP/PATHNAME:ENSURE-DIRECTORY-PATHNAME ROOT-DIR))) + + + +### STATICL/CLEAN-URLS + + + +#### [package](0b1b) `staticl/clean-urls` + + + +#### Generics + + + +##### [generic-function](5ca0) `staticl/clean-urls:transform-filename` site filename + +Converts the pathname to the form that should be used to write content to the disk. + +If the site has the clean-urls setting enabled, then the filename like some/page.html will be converted +to some/page/index.html. If clean-urls is not enabled, the pathname will remain unchanged. + + + +##### [generic-function](1e0e) `staticl/clean-urls:transform-url` site url + +Converts the `URL` to the form that should be used on the site. + +If the site has the clean-urls setting enabled, then the `URL` like /some/page.html will be converted +to /some/page/. If clean-urls is not enabled, the `URL` will remain unchanged. + + + +### STATICL/CONTENT + + + +#### [package](72a2) `staticl/content` + + + +#### Classes + + + +##### CONTENT-FROM-FILE + + + +###### [class](f1f2) `staticl/content:content-from-file` (content-with-title-mixin content-with-tags-mixin content) + +**Readers** + + + +###### [reader](c43f) `staticl/content:content-created-at` (content-from-file) (:created-at) + + + +###### [reader](971f) `staticl/content:content-excerpt-separator` (content-from-file) (:excerpt) + + + +###### [reader](a0c4) `staticl/content:content-file` (content-from-file) (:file) + +Absolute pathname to the file read from disk or `NIL` for content objects which have no source file, like `RSS` feeds. + + + +###### [reader](5090) `staticl/content:content-format` (content-from-file) (:format) + + + +###### [reader](34b8) `staticl/content:content-template` (content-from-file) (:template) + + + +###### [reader](bdeb) `staticl/content:content-text` (content-from-file) (:text) + + + +##### CONTENT-TYPE + + + +###### [class](4c56) `staticl/content:content-type` () + +**Readers** + + + +###### [reader](94f6) `staticl/content:content-class` (content-type) (:content-class) + + + +###### [reader](8e61) `staticl/content:content-file-type` (content-type) (:type) + + + +##### CONTENT-WITH-TAGS-MIXIN + + + +###### [class](1a5e) `staticl/content:content-with-tags-mixin` () + +**Readers** + + + +###### [reader](0a5a) `staticl/content:content-tags` (content-with-tags-mixin) (:tags) + + + +##### CONTENT-WITH-TITLE-MIXIN + + + +###### [class](aa5f) `staticl/content:content-with-title-mixin` () + +**Readers** + + + +###### [reader](310a) `staticl/content:content-title` (content-with-title-mixin) (:title) + + + +##### CONTENT + + + +###### [class](cf13) `staticl/content:content` (print-items-mixin) + +**Readers** + + + +###### [reader](af10) `staticl/content:content-metadata` (content) (= (make-hash-table :test 'equal)) + +A hash with additional fields specified in the file's header. + + + +#### Generics + + + +##### [generic-function](ad75) `staticl/content:get-target-filename` site content stage-dir + +Should return an absolute pathname to a file where this content item should be rendered. + + + +##### [generic-function](69df) `staticl/content:preprocess` site plugin content-objects + +Returns an additional list content objects such as `RSS` feeds or sitemaps. + + + +##### [generic-function](956b) `staticl/content:read-content-from-disk` site content-type &key exclude + +Returns a list of [`content`][a8cd] objects corresponding to a given content type. + +`EXCLUDE` argument is a list of pathname prefixes to ignore. Pathnames should be given relative to the root dir of the site. + + + +##### [generic-function](61c2) `staticl/content:read-contents` site &key exclude + +Returns a list of [`content`][a8cd] objects loaded from files. + +`EXCLUDE` argument is a list of pathname prefixes to ignore. Pathnames should be given relative to the root dir of the site. + + + +##### [generic-function](8a1c) `staticl/content:supported-content-types` site + +Returns a list of [`content-type`][d535] objects. + + + +##### [generic-function](c9c3) `staticl/content:write-content` site content stage-dir + +Writes `CONTENT` object to the `STAGE-DIR`. + + + +##### [generic-function](7e3d) `staticl/content:write-content-to-stream` site content stream + +Writes `CONTENT` object to the `STREAM` using given `FORMAT`. + + + +#### Functions + + + +##### [function](98ba) `staticl/content:set-metadata` content key value &key override-slot + +Changes metadata dictionary by adding a new item with key `KEY`. + +Key should be a string and it is automatically downcased. + +Note, this way, you can override content's object slots. +To prevent accidential override, function will raise an error +in case if a slot named `KEY` exists in the object `CONTENT`. +To force override provide `OVERRIDE-SLOT` argument. + + + +### STATICL/CONTENT-PIPELINE + + + +#### [package](a4ef) `staticl/content-pipeline` + + + +#### Classes + + + +##### LOAD-CONTENT + + + +###### [class](a939) `staticl/content-pipeline:load-content` () + +**Readers** + + + +###### [reader](9413) `staticl/content-pipeline:exclude-patterns` (load-content) (:exclude) + + + +#### Functions + + + +##### [function](1558) `staticl/content-pipeline:load-content` &KEY (EXCLUDE (LIST ".qlot")) + + + +### STATICL/CONTENT/HTML-CONTENT + + + +#### [package](8d92) `staticl/content/html-content` + + + +#### Generics + + + +##### [generic-function](8bcb) `staticl/content/html-content:content-html` content + +Returns a content as `HTML` string. + + + +##### [generic-function](50ed) `staticl/content/html-content:content-html-excerpt` content + +Returns an excerpt of full content as `HTML` string. + + + +##### [generic-function](0a1a) `staticl/content/html-content:has-more-content-p` content + +Returns T if there is more content than was returned by [`content-html-excerpt`][3bdd] generic-function. + + + +### STATICL/CONTENT/PAGE + + + +#### [package](f4ab) `staticl/content/page` + + + +#### Classes + + + +##### PAGE-TYPE + + + +###### [class](62ea) `staticl/content/page:page-type` (content-type) + + + +##### PAGE + + + +###### [class](01d8) `staticl/content/page:page` (content-from-file) + + + +### STATICL/CONTENT/POST + + + +#### [package](7edc) `staticl/content/post` + + + +#### Classes + + + +##### POST-TYPE + + + +###### [class](7706) `staticl/content/post:post-type` (content-type) + + + +##### POST + + + +###### [class](62e1) `staticl/content/post:post` (content-from-file) + +This is the class for a page which will not be included into the feed and indices. + + + +#### Functions + + + +##### [function](1dff) `staticl/content/post:postp` content-item + +Returns T if given object is a content of [`post`][9964] class. + + + +### STATICL/CONTENT/READER + + + +#### [package](7230) `staticl/content/reader` + + + +#### Functions + + + +##### [function](0ca9) `staticl/content/reader:read-content-file` file &key (separator \*default-metadata-separator\*) + +Returns a plist of metadata from `FILE` with `:TEXT` holding the content going after the `SEPARATOR`. + + + +### STATICL/CURRENT-ROOT + + + +#### [package](37c7) `staticl/current-root` + + + +#### Functions + + + +##### [function](866e) `staticl/current-root:current-root` + + + +#### Macros + + + +##### [macro](5a11) `staticl/current-root:with-current-root` (root) &body body + + + +### STATICL/FEEDS/ATOM + + + +#### [package](e364) `staticl/feeds/atom` + + + +#### Classes + + + +##### ATOM + + + +###### [class](6475) `staticl/feeds/atom:atom` (feed) + + + +#### Functions + + + +##### [function](9c7f) `staticl/feeds/atom:atom` &KEY (TARGET-PATH #P"atom.xml") + + + +### STATICL/FEEDS/RSS + + + +#### [package](94af) `staticl/feeds/rss` + + + +#### Classes + + + +##### RSS + + + +###### [class](d13d) `staticl/feeds/rss:rss` (feed) + + + +#### Functions + + + +##### [function](8f10) `staticl/feeds/rss:rss` &KEY (TARGET-PATH #P"rss.xml") + + + +### STATICL/FILTER + + + +#### [package](07a9) `staticl/filter` + + + +#### Classes + + + +##### FILTER + + + +###### [class](fe46) `staticl/filter:filter` () + +**Readers** + + + +###### [reader](c67c) `staticl/filter:filter-fn` (filter) (:filter-fn) + + + +###### [reader](2eb7) `staticl/filter:pipeline-items` (filter) (:pipeline) + + + +#### Macros + + + +##### [macro](34bc) `staticl/filter:filter` (&key path invert) &rest pipeline + +Filters input content objects and processes them using steps given as a body. + +Arguments: + +* `PATH`: if given result will contain only items read from the given path. +* `INVERT`: inverts effect of the filter. + + + +### STATICL/FORMAT + + + +#### [package](adbc) `staticl/format` + + + +#### Generics + + + +##### [generic-function](7935) `staticl/format:to-html` text format + + + +### STATICL/INDEX/BASE + + + +#### [package](fd34) `staticl/index/base` + + + +#### Classes + + + +##### BASE-INDEX + + + +###### [class](2201) `staticl/index/base:base-index` () + +**Readers** + + + +###### [reader](f408) `staticl/content:content-template` (base-index) (:template) + + + +###### [reader](52a2) `staticl/index/base:index-target-path` (base-index) (:target-path) + +Relative pathname to a directory where all pages will be generated. + + + +###### [reader](5241) `staticl/index/base:page-size` (base-index) (:page-size) + + + +##### INDEX-PAGE + + + +###### [class](f94b) `staticl/index/base:index-page` (content) + +**Readers** + + + +###### [reader](07a2) `staticl/content:content-template` (index-page) (:template) + + + +###### [reader](521b) `staticl/index/base:next-page` (index-page) (:next-page) + + + +###### [reader](4209) `staticl/index/base:page-items` (index-page) (:items) + + + +###### [reader](cad5) `staticl/index/base:page-target-path` (index-page) (:target-path) + +Relative pathname to a file with page content. + + + +###### [reader](c10b) `staticl/index/base:page-title` (index-page) (:title) + +A title of the page. + + + +###### [reader](7e84) `staticl/index/base:prev-page` (index-page) (:prev-page) + +**Accessors** + + + +###### [accessor](521b) `staticl/index/base:next-page` (index-page) (:next-page) + + + +###### [accessor](7e84) `staticl/index/base:prev-page` (index-page) (:prev-page) + + + +### STATICL/INDEX/PAGINATED + + + +#### [package](5c41) `staticl/index/paginated` + + + +#### Classes + + + +##### PAGINATED-INDEX + + + +###### [class](e451) `staticl/index/paginated:paginated-index` (base-index) + +**Readers** + + + +###### [reader](e97f) `staticl/index/paginated:page-filename-fn` (paginated-index) (:page-filename-fn) + +A callback to change page titles. + +Accepts single argument - a page number and should return a pathname relative to the site's root. +By default, it returns index.html for the first page and page-2.html, page-3.html for others. + +If site has "clean urls" setting enabled, then additional transformation to the pathname will be +applied automatically. + + + +###### [reader](a4a1) `staticl/index/paginated:page-title-fn` (paginated-index) (:page-title-fn) + +A callback to change page titles. + +Accepts single argument - a page number and should return a string. + +For example, here is how you can translate page title into a russian: + +```lisp +(paginated-index :target-path #P"ru/" + :page-title-fn (lambda (num) + (fmt "Страница ~A" num))) +``` + + +#### Functions + + + +##### [function](4b28) `staticl/index/paginated:paginated-index` &rest initargs &key target-path page-size template page-title-fn page-filename-fn + + + +### STATICL/LINKS/LINK + + + +#### [package](9fff) `staticl/links/link` + + + +#### Classes + + + +##### LINK + + + +###### [class](2f55) `staticl/links/link:link` () + +**Readers** + + + +###### [reader](08b0) `staticl/links/link:link-content` (link) (:content) + + + +#### Functions + + + +##### [function](1d57) `staticl/links/link:link` content + +Creates a link to the given content piece. + +When such object is passed to the template, it is resolved to a +page `URL` and title. + + + +### STATICL/LINKS/PREV-NEXT + + + +#### [package](bfda) `staticl/links/prev-next` + + + +#### Classes + + + +##### PREV-NEXT-LINKS + + + +###### [class](b64b) `staticl/links/prev-next:prev-next-links` () + + + +#### Functions + + + +##### [function](810a) `staticl/links/prev-next:prev-next-links` + +Creates a links between pages. + + + +### STATICL/NAVIGATION + + + +#### [package](7ef9) `staticl/navigation` + + + +#### Classes + + + +##### ITEM + + + +###### [class](e7ae) `staticl/navigation:item` () + +**Readers** + + + +###### [reader](74a0) `staticl/navigation:menu-item-title` (item) (:title) + + + +###### [reader](0c86) `staticl/navigation:menu-item-url` (item) (:url) + + + +##### MENU + + + +###### [class](3021) `staticl/navigation:menu` () + +**Readers** + + + +###### [reader](03ac) `staticl/navigation:menu-items` (menu) (:items) + + + +#### Functions + + + +##### [function](e07d) `staticl/navigation:item` title url + + + +##### [function](affb) `staticl/navigation:menu` &rest items + + + +### STATICL/PIPELINE + + + +#### [package](a489) `staticl/pipeline` + + + +#### Generics + + + +##### [generic-function](ccdc) `staticl/pipeline:process-items` site pipeline-node content-items + +A method for this generic function should process `CONTENT-ITEMS` - a list of conten items +produced by a previous pipeline nodes. + +During the execution, method can call [`produce-item`][45ad] or [`remove-item`][4ba7] functions to add a new content +or to remove some content item. + + + +#### Functions + + + +##### [function](0a84) `staticl/pipeline:execute-pipeline` site + + + +##### [function](f3fa) `staticl/pipeline:produce-item` item + + + +##### [function](1d91) `staticl/pipeline:remove-item` item + + + +### STATICL/PLUGIN + + + +#### [package](169a) `staticl/plugin` + + + +#### Classes + + + +##### PLUGIN + + + +###### [class](f63f) `staticl/plugin:plugin` () + + + +#### Functions + + + +##### [function](c3a8) `staticl/plugin:make-plugin` name &rest initargs + + + +### STATICL/RSYNC + + + +#### [package](44b1) `staticl/rsync` + + + +#### Classes + + + +##### RSYNC + + + +###### [class](4994) `staticl/rsync:rsync` () + +**Readers** + + + +###### [reader](2b42) `staticl/rsync:rsync-host` (rsync) (:host) + + + +#### Functions + + + +##### [function](431f) `staticl/rsync:rsync` host + + + +### STATICL/SITE + + + +#### [package](a037) `staticl/site` + + + +#### Classes + + + +##### SITE + + + +###### [class](4f3b) `staticl/site:site` () + +**Readers** + + + +###### [reader](f77b) `staticl/site:clean-urls-p` (site) (:clean-urls) + +Generate some-page/index.html instead of some-page.html to make `URL`s look like https://my-site.com/some-page/ instead of https://my-site.com/some-page.html + + + +###### [reader](080d) `staticl/site:site-charset` (site) (:charset) + +Site's charset. By default it is `UTF-8`. + + + +###### [reader](cd08) `staticl/site:site-content-root` (site) (:root) + +A directory pathname where .staticlrc file can be found. + + + +###### [reader](742b) `staticl/site:site-description` (site) (:title) + +Site's description. + + + +###### [reader](c6a1) `staticl/site:site-navigation` (site) (:navigation) + +Site's navigation. + + + +###### [reader](f87d) `staticl/site:site-pipeline` (site) (:pipeline) + +A list of pipline nodes + + + +###### [reader](c3b2) `staticl/site:site-theme` (site) (:theme) + +A theme object for the site. + + + +###### [reader](4ef2) `staticl/site:site-title` (site) (:title) + +Site's title. + + + +###### [reader](98d2) `staticl/site:site-url` (site) (:url) + +Site's `URL`. + + + +#### Functions + + + +##### [function](0112) `staticl/site:make-site` root + + + +##### [function](ed38) `staticl/site:site` title &rest args + + + +### STATICL/TAG + + + +#### [package](d90f) `staticl/tag` + + + +#### Classes + + + +##### TAG + + + +###### [class](fca4) `staticl/tag:tag` () + +**Readers** + + + +###### [reader](3dfa) `staticl/tag:tag-name` (tag) (:name) + + + +### STATICL/THEME + + + +#### [package](a484) `staticl/theme` + + + +#### Classes + + + +##### THEME + + + +###### [class](60b1) `staticl/theme:theme` (print-items-mixin) + +**Readers** + + + +###### [reader](db4b) `staticl/theme:theme-path` (theme) (:path) + + + +#### Generics + + + +##### [generic-function](6417) `staticl/theme:copy-static` theme stage-dir + +Copies static files such as `CSS`, `JS`, images into the `STAGE-DIR`. + +Usually it is enough to define a method for [`list-static`][412e] generic-function. + + + +##### [generic-function](5fc1) `staticl/theme:list-static` theme + +Returns a list of static files such as `CSS`, `JS`, images. + +Each list item should be a list of two items where first +item is an absolute pathname and second is a pathname relative +to the root of the site. + + + +##### [generic-function](1699) `staticl/theme:render` theme template-name vars stream + +Renders fills template named `TEMPLATE-NAME` with given `VARS` and renders into a given `STREAM`. + +* `NAME` argument is a string. +* `VARS` argument is a hash table with string keys. + + + +##### [generic-function](4fb7) `staticl/theme:template-vars` site object &key hash + +Fills a hash-table given as `HASH` argument with variables for filling a template. + +If hash is `NIL`, then a new hash-table should be allocated with `EQUAL` `:TEST` argument. + +Returned hash-table will be used for rendering a template for an `OBJECT`. + + + +### STATICL/URL + + + +#### [package](2cb0) `staticl/url` + + + +#### Generics + + + +##### [generic-function](3922) `staticl/url:object-url` site obj &key full &allow-other-keys + +Returns a full object `URL`. +A method should return an relative `URL`, but if case if `FULL` argument was given, +the full url with schema and domain will be returned. + +Note a call to this method should happen in a context of the [`with-base-url`][888b] macro, +because it is always return a path from the site's root even if `FULL` is not given +(in this case return only the path without a domain). + +You may wonder: "Why does we bother to return a path without a domain?" +It is much easier to service such static site locally for debugging purpose, because +you don't have to setup a web server and dns resolver. + +Actually you will need to use `FULL` argument only in a rare case when you really need +and absolute `URL`, for example in an `RSS` feed. + + + +#### Macros + + + +##### [macro](0fe2) `staticl/url:with-base-url` (url) &body body + + + +### STATICL/UTILS + + + +#### [package](9ca9) `staticl/utils` + + + +#### Functions + + + +##### [function](b98e) `staticl/utils:absolute-url-p` url + + + +##### [function](5a5f) `staticl/utils:assert-absolute-url` url + + + +##### [function](3c28) `staticl/utils:normalize-plist` plist &rest normalizers-plist &key &allow-other-keys + +Returns a new list where each value is replaced with results of call of normalizing functions. + +For example: + +``` +CL-USER> (normalize-plist '(:foo "Bar" :blah 123) + :foo (lambda (value) + (alexandria:make-keyword (string-upcase value)))) +(LIST :FOO :BAR :BLAH 123) +``` + + +#### Macros + + + +##### [macro](d944) `staticl/utils:do-files` (filename root-path &key file-type) &body body + +For each file under `ROOT-PATH`, run `BODY`. If `FILE-TYPE` is provided, only run +`BODY` on files that match the given extension. + + +[0cf8]: https://40ants.com/staticl/ +[3bdd]: https://40ants.com/staticl/#x-28STATICL-2FCONTENT-2FHTML-CONTENT-3ACONTENT-HTML-EXCERPT-20GENERIC-FUNCTION-29 +[9964]: https://40ants.com/staticl/#x-28STATICL-2FCONTENT-2FPOST-3APOST-20CLASS-29 +[a8cd]: https://40ants.com/staticl/#x-28STATICL-2FCONTENT-3ACONTENT-20CLASS-29 +[d535]: https://40ants.com/staticl/#x-28STATICL-2FCONTENT-3ACONTENT-TYPE-20CLASS-29 +[d70a]: https://40ants.com/staticl/#x-28STATICL-2FCONTENT-3APREPROCESS-20GENERIC-FUNCTION-29 +[c4c2]: https://40ants.com/staticl/#x-28STATICL-2FCONTENT-3AWRITE-CONTENT-20GENERIC-FUNCTION-29 +[96e3]: https://40ants.com/staticl/#x-28STATICL-2FCONTENT-3AWRITE-CONTENT-TO-STREAM-20GENERIC-FUNCTION-29 +[45ad]: https://40ants.com/staticl/#x-28STATICL-2FPIPELINE-3APRODUCE-ITEM-20FUNCTION-29 +[4ba7]: https://40ants.com/staticl/#x-28STATICL-2FPIPELINE-3AREMOVE-ITEM-20FUNCTION-29 +[412e]: https://40ants.com/staticl/#x-28STATICL-2FTHEME-3ALIST-STATIC-20GENERIC-FUNCTION-29 +[888b]: https://40ants.com/staticl/#x-28STATICL-2FURL-3AWITH-BASE-URL-20-2840ANTS-DOC-2FLOCATIVES-3AMACRO-29-29 +[c5b7]: https://40ants.com/staticl/#x-28STATICL-3ASTAGE-20FUNCTION-29 +[2594]: https://github.com/40ants/staticl +[ba40]: https://github.com/40ants/staticl/actions +[0b1b]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/clean-urls.lisp#L1 +[1e0e]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/clean-urls.lisp#L50 +[5ca0]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/clean-urls.lisp#L63 +[a4ef]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content-pipeline.lisp#L1 +[a939]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content-pipeline.lisp#L15 +[9413]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content-pipeline.lisp#L16 +[1558]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content-pipeline.lisp#L23 +[72a2]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L1 +[aa5f]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L104 +[310a]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L105 +[1a5e]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L114 +[0a5a]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L115 +[f1f2]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L122 +[5090]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L125 +[34b8]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L128 +[c43f]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L131 +[a0c4]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L134 +[bdeb]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L141 +[971f]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L144 +[8a1c]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L191 +[956b]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L230 +[61c2]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L250 +[c9c3]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L261 +[ad75]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L273 +[7e3d]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L299 +[69df]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L313 +[98ba]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L402 +[4c56]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L78 +[8e61]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L79 +[94f6]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L82 +[cf13]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L97 +[af10]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content.lisp#L98 +[8d92]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content/html-content.lisp#L1 +[50ed]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content/html-content.lisp#L12 +[0a1a]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content/html-content.lisp#L15 +[8bcb]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content/html-content.lisp#L9 +[f4ab]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content/page.lisp#L1 +[01d8]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content/page.lisp#L11 +[62ea]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content/page.lisp#L18 +[7edc]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content/post.lisp#L1 +[62e1]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content/post.lisp#L14 +[7706]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content/post.lisp#L22 +[1dff]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content/post.lisp#L32 +[7230]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content/reader.lisp#L1 +[0ca9]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/content/reader.lisp#L48 +[592c]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/core.lisp#L1 +[c8d0]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/core.lisp#L32 +[37c7]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/current-root.lisp#L1 +[866e]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/current-root.lisp#L21 +[5a11]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/current-root.lisp#L35 +[e364]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/feeds/atom.lisp#L1 +[6475]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/feeds/atom.lisp#L11 +[9c7f]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/feeds/atom.lisp#L17 +[94af]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/feeds/rss.lisp#L1 +[d13d]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/feeds/rss.lisp#L10 +[8f10]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/feeds/rss.lisp#L16 +[07a9]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/filter.lisp#L1 +[fe46]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/filter.lisp#L24 +[c67c]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/filter.lisp#L25 +[2eb7]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/filter.lisp#L28 +[34bc]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/filter.lisp#L35 +[adbc]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/format.lisp#L1 +[7935]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/format.lisp#L11 +[fd34]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/index/base.lisp#L1 +[2201]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/index/base.lisp#L39 +[52a2]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/index/base.lisp#L40 +[5241]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/index/base.lisp#L44 +[f408]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/index/base.lisp#L47 +[f94b]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/index/base.lisp#L56 +[cad5]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/index/base.lisp#L57 +[c10b]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/index/base.lisp#L61 +[4209]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/index/base.lisp#L65 +[7e84]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/index/base.lisp#L68 +[521b]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/index/base.lisp#L71 +[07a2]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/index/base.lisp#L74 +[5c41]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/index/paginated.lisp#L1 +[e451]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/index/paginated.lisp#L48 +[e97f]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/index/paginated.lisp#L49 +[a4a1]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/index/paginated.lisp#L59 +[4b28]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/index/paginated.lisp#L79 +[9fff]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/links/link.lisp#L1 +[2f55]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/links/link.lisp#L19 +[08b0]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/links/link.lisp#L20 +[1d57]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/links/link.lisp#L28 +[bfda]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/links/prev-next.lisp#L1 +[b64b]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/links/prev-next.lisp#L17 +[810a]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/links/prev-next.lisp#L21 +[7ef9]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/navigation.lisp#L1 +[e7ae]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/navigation.lisp#L14 +[0c86]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/navigation.lisp#L15 +[74a0]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/navigation.lisp#L18 +[e07d]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/navigation.lisp#L26 +[3021]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/navigation.lisp#L32 +[03ac]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/navigation.lisp#L33 +[affb]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/navigation.lisp#L41 +[a489]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/pipeline.lisp#L1 +[ccdc]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/pipeline.lisp#L18 +[f3fa]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/pipeline.lisp#L27 +[1d91]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/pipeline.lisp#L33 +[0a84]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/pipeline.lisp#L40 +[169a]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/plugin.lisp#L1 +[f63f]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/plugin.lisp#L12 +[c3a8]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/plugin.lisp#L19 +[44b1]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/rsync.lisp#L1 +[431f]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/rsync.lisp#L16 +[4994]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/rsync.lisp#L8 +[2b42]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/rsync.lisp#L9 +[a037]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/site.lisp#L1 +[0112]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/site.lisp#L132 +[4f3b]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/site.lisp#L44 +[cd08]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/site.lisp#L45 +[4ef2]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/site.lisp#L49 +[742b]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/site.lisp#L53 +[c6a1]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/site.lisp#L57 +[080d]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/site.lisp#L61 +[98d2]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/site.lisp#L65 +[f77b]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/site.lisp#L69 +[c3b2]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/site.lisp#L73 +[f87d]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/site.lisp#L77 +[ed38]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/site.lisp#L91 +[d90f]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/tag.lisp#L1 +[fca4]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/tag.lisp#L14 +[3dfa]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/tag.lisp#L15 +[a484]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/theme.lisp#L1 +[6417]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/theme.lisp#L106 +[60b1]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/theme.lisp#L23 +[db4b]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/theme.lisp#L24 +[4fb7]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/theme.lisp#L33 +[1699]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/theme.lisp#L41 +[5fc1]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/theme.lisp#L98 +[2cb0]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/url.lisp#L1 +[3922]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/url.lisp#L25 +[0fe2]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/url.lisp#L66 +[9ca9]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/utils.lisp#L1 +[b98e]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/utils.lisp#L164 +[5a5f]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/utils.lisp#L174 +[d944]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/utils.lisp#L76 +[3c28]: https://github.com/40ants/staticl/blob/0a7e04f8442e26e8e18a3a83a891f43344fd0e33/src/utils.lisp#L98 +[5825]: https://github.com/40ants/staticl/issues +[cc3e]: https://quickdocs.org/3bmd +[d94a]: https://quickdocs.org/3bmd-ext-code-blocks +[8236]: https://quickdocs.org/alexandria +[1059]: https://quickdocs.org/cl-fad +[49b9]: https://quickdocs.org/cl-ppcre +[dfc0]: https://quickdocs.org/cl-sitemaps +[61a4]: https://quickdocs.org/closer-mop +[2700]: https://quickdocs.org/closure-template +[0b13]: https://quickdocs.org/feeder +[ed60]: https://quickdocs.org/fuzzy-dates +[46a1]: https://quickdocs.org/local-time +[7f8b]: https://quickdocs.org/log4cl +[2103]: https://quickdocs.org/quri +[c41d]: https://quickdocs.org/serapeum +[ef7f]: https://quickdocs.org/str +[52a0]: https://quickdocs.org/utilities.print-items + +* * * +###### [generated by [40ANTS-DOC](https://40ants.com/doc/)] diff --git a/docs/index.lisp b/docs/index.lisp index 3646e13..7683002 100644 --- a/docs/index.lisp +++ b/docs/index.lisp @@ -15,6 +15,10 @@ #:docs-config) (:import-from #:40ants-doc/autodoc #:defautodoc) + (:import-from #:staticl-docs/introduction + #:@introduction) + (:import-from #:serapeum + #:eval-always) (:export #:@index #:@readme #:@changelog)) @@ -23,6 +27,33 @@ (in-readtable pythonic-string-syntax) +(eval-always + (defparameter *ignore-words* + '("JSON" + "HTTP" + "HTML" + "TODO" + "StatiCL" + "Unlicense" + "REPL" + "ASDF:PACKAGE-INFERRED-SYSTEM" + "ASDF" + "40A" + "API" + "JS" + "CSS" + "UTF-8" + "RSS" + "CL" + "URL" + "URI" + "RPC" + "CDN" + "XML" + "SEO" + "GIT"))) + + (defmethod docs-config ((system (eql (asdf:find-system "staticl-docs")))) ;; 40ANTS-DOC-THEME-40ANTS system will bring ;; as dependency a full 40ANTS-DOC but we don't want @@ -34,24 +65,13 @@ (list :theme (find-symbol "40ANTS-THEME" - (find-package "40ANTS-DOC-THEME-40ANTS"))) - ) + (find-package "40ANTS-DOC-THEME-40ANTS")) + :root-sections '(@index + @api))) (defsection @index (:title "staticl - Flexible static site generator." - :ignore-words ("JSON" - "HTTP" - "TODO" - "Unlicense" - "REPL" - "ASDF:PACKAGE-INFERRED-SYSTEM" - "ASDF" - "40A" - "API" - "URL" - "URI" - "RPC" - "GIT")) + :ignore-words *ignore-words*) (staticl system) " [![](https://github-actions.40ants.com/40ants/staticl/matrix.svg?only=ci.run-tests)](https://github.com/40ants/staticl/actions) @@ -59,8 +79,11 @@ ![Quicklisp](http://quickdocs.org/badge/staticl.svg) " (@installation section) - (@usage section) - (@api section)) + (@introduction section) + ;; (@usage section) + ;; (@processing-pipeline section) + ;; (@api section) + ) (defsection-copy @readme @index) @@ -78,28 +101,35 @@ You can install this library from Quicklisp, but you want to receive updates qui """) -(defsection @usage (:title "Usage") - " -StatiCL is a static site generator. StatiCL as a modular architecture and is suitable for any kind of a site -be it a blog or a site with pages not included in the feeds. This project was created to overcome limitations of the Coleslaw. -") +;; (defsection @usage (:title "Usage") +;; " +;; `StatiCL` is a static site generator. `StatiCL` as a modular architecture and is suitable for any kind of a site +;; be it a blog or a site with pages not included in the feeds. This project was created to overcome limitations of the Coleslaw. +;; ") -(defsection @processing-pipeline (:title "Processing Pipeline") - " -First of all, a SITE object is created and filled with options from `.staticlrc` file. +;; (defsection @idea (:title "Idea behind the StatiCL") +;; " +;; Whereas `Coleslaw` has a monilite architecture, `StatiCL` introduces a modular way of building a pipeline for re +;; ") + + +;; (defsection @processing-pipeline (:title "Processing Pipeline") +;; " +;; First of all, a STATICL/SITE:SITE object is created and filled with options from `.staticlrc` file. -Then STATICL:STAGE function calls STATICL/CONTENT:READ-CONTENT generic-function which returns a list -of STATICL/CONTENT:CONTENT objects. On the next stage, initial list of content objects are passed to a -generic-function STATICL/CONTENT:PREPROCESS called with a preprocessor returned by a -generic-function STATICL/PLUGINS:SITE-PLUGINS as a first argument. Each preprocessor may -return additional STATICL/CONTENT:CONTENT objects such as index pages, RSS or ATOM feeds, sitemaps etc. +;; Then STATICL:STAGE function calls STATICL/CONTENT:READ-CONTENT generic-function which returns a list +;; of STATICL/CONTENT:CONTENT objects. On the next stage, initial list of content objects are passed to a +;; generic-function STATICL/CONTENT:PREPROCESS called with a preprocessor returned by a +;; generic-function STATICL/PLUGINS:SITE-PLUGINS as a first argument. Each preprocessor may +;; return additional STATICL/CONTENT:CONTENT objects such as index pages, RSS or ATOM feeds, sitemaps etc. -When all content was preprocessed, a generic-function STATICL/CONTENT:WRITE-CONTENT is called -on each STATICL/CONTENT:CONTENT object and a SITE object. Content objects are having a format slot, -so internally STATICL/CONTENT:WRITE-CONTENT generic-function creates an object of corresponding format class -or takes it from the cache and then calls STATICL/CONTENT:WRITE-CONTENT-TO-STREAM using this format object. -") +;; When all content was preprocessed, a generic-function STATICL/CONTENT:WRITE-CONTENT is called +;; on each STATICL/CONTENT:CONTENT object and a SITE object. Content objects are having a format slot, +;; so internally STATICL/CONTENT:WRITE-CONTENT generic-function creates an object of corresponding format class +;; or takes it from the cache and then calls STATICL/CONTENT:WRITE-CONTENT-TO-STREAM using this format object. +;; ") -(defautodoc @api (:system "staticl")) +(defautodoc @api (:system "staticl" + :ignore-words *ignore-words*)) diff --git a/docs/introduction.lisp b/docs/introduction.lisp new file mode 100644 index 0000000..b230863 --- /dev/null +++ b/docs/introduction.lisp @@ -0,0 +1,83 @@ +(uiop:define-package #:staticl-docs/introduction + (:use #:cl) + (:import-from #:named-readtables + #:in-readtable) + (:import-from #:40ants-doc + #:defsection) + (:import-from #:pythonic-string-reader + #:pythonic-string-syntax)) +(in-package #:staticl-docs/introduction) + +(in-readtable pythonic-string-syntax) + + +(defsection @introduction (:title "Introduction") + """ +# Why do we need static website generators? + +In the era of high technology and fast access to information, when every second of page loading is worth its weight in gold, static website generators come to the fore. But why are they so important? And which tool should I choose to create the perfect static website? + +We present to your attention StatiCL — one of the most promising tools in this niche. But before we dive into the specifics of StatiCL, let's figure out what static site generators are for and what advantages they bring. + +## The main advantages of static site generators + +— no movement, no problem. Static sites are much more resistant to hacker attacks, because they simply do not have a server that can be hacked. + +— Instant page loading becomes a reality, as static content is easily cached and does not require additional processing by the server. + +— has your website suddenly gained popularity? Static sites can easily withstand traffic growth without the need for complex infrastructure configuration. + +— using version control systems such as Git, you can always roll back to a previous version of the site or make changes without risking the current operating environment. + +— static pages do not require complex server solutions, which significantly reduces the cost of hosting. + +## How static site generators work + +Generators convert content from a simple Markdown markup language to HTML and CSS. They allow you to create beautiful and readable web pages without requiring the developer to write complex code. Generators greatly simplify the process of website development by providing fast and efficient formatting of text, inserting images, videos and other multimedia components. + +You can use various methods to host a static site, including Github Pages, CDN, or other affordable cheap hosting services. Github Pages is a free service provided by Github that allows you to host static sites directly from the repository on Github. This is a convenient way to host small projects or personal pages. CDN (Content Delivery Network) is a network of servers distributed around the world that helps speed up the loading of content on a site due to the proximity of servers to end users. This is especially useful for sites with a large number of visitors and traffic. If you have a budget, you can also consider other cheap hosting providers that offer good conditions for hosting static sites. It is important to consider the requirements of your project and choose the appropriate option that meets your needs in terms of performance, reliability and price. + +## Why was StatiCL created + +At 40, we believe in the power of Common Lisp and use it as the basis of all our projects. We used to work with the Coleslaw static blog generator, but we encountered some of its limitations. For example, it was difficult for us to create a website in several languages and set up the main page in a different way than just a list of articles. Coleslaw is more suitable for blogs than for sites with a diverse structure. Therefore, we have developed a StatiCL tool with even more flexibility. With StatiCL, you can create a static website of any complexity, without limiting yourself to blog templates. + +In StatiCL, you can easily create extensions, as well as use any template engine, not limited to Clozure Templates. This gives you more freedom in choosing tools to work with your static sites and allows you to use those technologies that are more convenient and familiar to you. The flexibility of StatiCL makes it an excellent choice for developers who want to create high-quality static websites optimized for their needs and preferences. + +## The basic principles underlying StatiCL + +StatiCL is an innovative content processing system based on the pipeline concept. The pipeline consists of various nodes, each of which receives all the content objects generated by the previous parts of the pipeline. Each node has the ability to modify existing content or add new elements to it. This allows you to create unique and high-quality content enriched with a variety of data and information. In addition, thanks to the use of a content processing pipeline, StatiCL provides efficient and fast information processing. Each stage of the pipeline is performed sequentially, which allows you to optimize the process of creating content and improve its quality. This approach allows users to easily manage the content processing process and create unique materials for various purposes. Thanks to the flexible pipeline structure and the ability to add new nodes, StatiCL provides a high degree of personalization and customization of the content processing process for specific user needs. Thus, the system allows you to create content that meets the individual requirements and tasks of users, ensuring high efficiency and effectiveness of work. + +The pipeline describing the site generation is written in Lisp and is a series of nested function calls. Each site using StatiCL must contain a .staticlrc file in its root directory, which contains a description of the pipeline. This is how the simplest description of the site looks like: + + (site "Trivial Site" + :description "A trivial staticl site." + :url "https://example.com" + :pipeline (list (load-content)) + :theme "readable") + +When you run the staticl generate command, the engine will start processing content from files on disk. It will read all the files with the post and page extensions, and then create the corresponding html files. This way, you will receive ready-made files that can be easily placed on the site for visitors to view. + +To create an RSS feed for all blog posts, you need to add a new step in the StatiCL pipeline. Let's create an element (rss) that will be responsible for generating the RSS feed. This element will extract data about blog posts and generate an appropriate XML file containing information about the titles, publication date and content of each post. This way users will be able to subscribe to blog updates via RSS readers. + + (site "Trivial Site" + :description "A trivial staticl site." + :url "https://example.com" + :pipeline (list (load-content) + (rss)) + :theme "readable") + +When creating sitemap.xml In addition to RSS, we can add another step to our pipeline - creating a sitemap. This file is a special format that allows search engines to index pages of our site more efficiently. Creation sitemap.xml It will help to improve the SEO optimization of the site, as search engines will be able to detect and index new content faster. In addition, sitemap.xml allows you to tell search engines which pages are the most important for indexing, which can also affect the ranking of the site in search results. + + (site "Trivial Site" + :description "A trivial staticl site." + :url "https://example.com" + :pipeline (list (load-content) + (rss) + (sitemap)) + :theme "readable") + +The sitemap step will receive all the content created by the previous steps and create a file at the output sitemap.xml . + +In addition to sequentially executing the pipeline steps, you can split the content into different "streams". For example, you can filter them by the language in which the texts are written, and perform different pipeline steps for each language. For example, you can create separate RSS feeds for each language. You can read more about this in the tutorial. +""" + ) diff --git a/docs/tutorial/step1/.staticlrc b/docs/tutorial/step1/.staticlrc new file mode 100644 index 0000000..34f82f6 --- /dev/null +++ b/docs/tutorial/step1/.staticlrc @@ -0,0 +1,8 @@ +;;; -*- mode : lisp -*- +(in-package #:staticl-user) + +(site "Trivial Site" + :description "A trivial staticl site." + :url "https://example.com" + :pipeline (list (load-content)) + :theme "readable") diff --git a/docs/tutorial/step1/index.page b/docs/tutorial/step1/index.page new file mode 100644 index 0000000..e966591 --- /dev/null +++ b/docs/tutorial/step1/index.page @@ -0,0 +1,8 @@ +;;;;; +title: An index page. +created-at: 2024-03-30 +format: md +;;;;; + +This is a front page of example site. The site uses StatiCL generator written in Common Lisp. + diff --git a/docs/tutorial/step2/.staticlrc b/docs/tutorial/step2/.staticlrc new file mode 100644 index 0000000..f84143f --- /dev/null +++ b/docs/tutorial/step2/.staticlrc @@ -0,0 +1,9 @@ +;;; -*- mode : lisp -*- +(in-package #:staticl-user) + +(site "Trivial Site" + :description "A trivial staticl site." + :url "https://example.com" + :pipeline (list (load-content) + (rss)) + :theme "readable") diff --git a/docs/tutorial/step2/index.page b/docs/tutorial/step2/index.page new file mode 100644 index 0000000..e966591 --- /dev/null +++ b/docs/tutorial/step2/index.page @@ -0,0 +1,8 @@ +;;;;; +title: An index page. +created-at: 2024-03-30 +format: md +;;;;; + +This is a front page of example site. The site uses StatiCL generator written in Common Lisp. + diff --git a/docs/tutorial/step3/.staticlrc b/docs/tutorial/step3/.staticlrc new file mode 100644 index 0000000..611843b --- /dev/null +++ b/docs/tutorial/step3/.staticlrc @@ -0,0 +1,10 @@ +;;; -*- mode : lisp -*- +(in-package #:staticl-user) + +(site "Trivial Site" + :description "A trivial staticl site." + :url "https://example.com" + :pipeline (list (load-content) + (rss) + (sitemap)) + :theme "readable") diff --git a/docs/tutorial/step3/index.page b/docs/tutorial/step3/index.page new file mode 100644 index 0000000..e966591 --- /dev/null +++ b/docs/tutorial/step3/index.page @@ -0,0 +1,8 @@ +;;;;; +title: An index page. +created-at: 2024-03-30 +format: md +;;;;; + +This is a front page of example site. The site uses StatiCL generator written in Common Lisp. + diff --git a/src/content-pipeline.lisp b/src/content-pipeline.lisp index cb1957e..81254fc 100644 --- a/src/content-pipeline.lisp +++ b/src/content-pipeline.lisp @@ -7,7 +7,8 @@ (:import-from #:serapeum #:soft-list-of #:->) - (:export #:load-content)) + (:export #:load-content + #:exclude-patterns)) (in-package #:staticl/content-pipeline) diff --git a/src/content.lisp b/src/content.lisp index d86b7a4..3d5c600 100644 --- a/src/content.lisp +++ b/src/content.lisp @@ -69,8 +69,9 @@ #:content-title #:content-excerpt-separator #:set-metadata - #:load-content - #:content-tags)) + #:content-tags + #:content-metadata + #:content-file-type)) (in-package #:staticl/content) diff --git a/src/content/post.lisp b/src/content/post.lisp index 635eb1c..18615ad 100644 --- a/src/content/post.lisp +++ b/src/content/post.lisp @@ -13,6 +13,7 @@ (defclass post (content-from-file) () + (:documentation "This is the class for a page which will not be included into the feed and indices.") (:default-initargs ;; In coleslaw page and post share the same template :template "post")) @@ -29,5 +30,5 @@ (values boolean &optional)) (defun postp (content-item) - "Returns T if given object is a content of type POST." + "Returns T if given object is a content of POST class." (typep content-item 'post)) diff --git a/src/core.lisp b/src/core.lisp index f3f98a2..e890a55 100644 --- a/src/core.lisp +++ b/src/core.lisp @@ -3,7 +3,6 @@ (:import-from #:staticl/site #:site-content-root #:site-theme - #:site-plugins #:site-url #:make-site) (:import-from #:staticl/content @@ -21,8 +20,7 @@ (:import-from #:staticl/url #:with-base-url) (:nicknames #:staticl/core) - (:export #:generate - #:stage)) + (:export #:stage)) (in-package #:staticl) diff --git a/src/filter.lisp b/src/filter.lisp index fbe780a..1a454ed 100644 --- a/src/filter.lisp +++ b/src/filter.lisp @@ -10,7 +10,9 @@ #:content) (:import-from #:staticl/current-root #:current-root) - (:export #:filter)) + (:export #:filter + #:filter-fn + #:pipeline-items)) (in-package #:staticl/filter) diff --git a/src/index/base.lisp b/src/index/base.lisp index 3ed8fef..44abd07 100644 --- a/src/index/base.lisp +++ b/src/index/base.lisp @@ -20,7 +20,6 @@ #:page-items #:prev-page #:next-page - #:index-page-template #:base-index #:index-target-path #:page-size diff --git a/src/index/paginated.lisp b/src/index/paginated.lisp index 626e180..b2fe8ca 100644 --- a/src/index/paginated.lisp +++ b/src/index/paginated.lisp @@ -12,7 +12,9 @@ #:fmt) (:import-from #:staticl/content/post #:postp) - (:export #:paginated-index)) + (:export #:paginated-index + #:page-filename-fn + #:page-title-fn)) (in-package #:staticl/index/paginated) diff --git a/src/links/link.lisp b/src/links/link.lisp index 1969a21..a7cf3e1 100644 --- a/src/links/link.lisp +++ b/src/links/link.lisp @@ -11,8 +11,8 @@ #:object-url) (:import-from #:staticl/site #:site) - (:export - #:link)) + (:export #:link + #:link-content)) (in-package #:staticl/links/link) diff --git a/src/rsync.lisp b/src/rsync.lisp index a0441bb..755a5fe 100644 --- a/src/rsync.lisp +++ b/src/rsync.lisp @@ -1,6 +1,7 @@ (uiop:define-package #:staticl/rsync (:use #:cl) - (:export #:rsync)) + (:export #:rsync + #:rsync-host)) (in-package #:staticl/rsync) @@ -13,5 +14,7 @@ (defun rsync (host) - (make-instance 'rsync - :host host)) + (error "This node is not supported yet.") + ;; (make-instance 'rsync + ;; :host host) + ) diff --git a/src/site.lisp b/src/site.lisp index 0dc25ed..e8a0c98 100644 --- a/src/site.lisp +++ b/src/site.lisp @@ -31,11 +31,13 @@ #:site-content-root #:site-title #:make-site - #:site-plugins #:site-theme #:site-pipeline #:site-description - #:clean-urls-p)) + #:clean-urls-p + #:site-navigation + #:site-charset + #:site-url)) (in-package #:staticl/site) diff --git a/src/theme.lisp b/src/theme.lisp index 0c38bec..b222a4f 100644 --- a/src/theme.lisp +++ b/src/theme.lisp @@ -15,7 +15,8 @@ #:template-vars #:render #:list-static - #:copy-static)) + #:copy-static + #:theme-path)) (in-package #:staticl/theme) From c626fb63f8af4e68e102df43427f7f92e0393821 Mon Sep 17 00:00:00 2001 From: Alexander Artemenko Date: Sat, 4 May 2024 10:14:29 +0300 Subject: [PATCH 3/4] Use the latest CCL-BIN in CI. --- .github/workflows/ci.yml | 2 +- src/ci.lisp | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b5eb327..5966e9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ "matrix": { "lisp": [ "sbcl-bin", - "ccl-bin/1.12.0" + "ccl-bin" ] } }, diff --git a/src/ci.lisp b/src/ci.lisp index 0118a18..9c88baa 100644 --- a/src/ci.lisp +++ b/src/ci.lisp @@ -36,7 +36,5 @@ :jobs ((run-tests :asdf-system "staticl" :lisp ("sbcl-bin" - ;; Issue https://github.com/roswell/roswell/issues/534 - ;; is still reproduces on 2023-02-06: - "ccl-bin/1.12.0") + "ccl-bin") :coverage t))) From 68e0937a747c21d40805cf7ed64d44667227670e Mon Sep 17 00:00:00 2001 From: Alexander Artemenko Date: Sat, 4 May 2024 10:20:06 +0300 Subject: [PATCH 4/4] Ignore host. --- src/rsync.lisp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rsync.lisp b/src/rsync.lisp index 755a5fe..103912b 100644 --- a/src/rsync.lisp +++ b/src/rsync.lisp @@ -14,6 +14,7 @@ (defun rsync (host) + (declare (ignore host)) (error "This node is not supported yet.") ;; (make-instance 'rsync ;; :host host)