Files

1791 lines
50 KiB
Org Mode

* one.el
:PROPERTIES:
:ONE: one-default-with-sidebar
:CUSTOM_ID: /
:END:
** Static Site Generator for Emacs Lisp programmers
[[youtube:GGP2mxZn4mY]]
Have you ever wanted to write a blog:
- contained in a unique org file,
- rendered with only one Emacs command,
- that can be modified by writing Emacs Lisp code (and CSS too),
- with "html templates" that are plain Emacs Lisp data,
- with no config file,
- and no dependencies on external static site generators?
If so, you might be interested in ~one.el~ a simple *Static Site
Generator* for *Emacs Lisp* programmers and *org-mode* users.
To get started right away check [[#/docs/install-one-el/][Install one.el]] and [[#/docs/getting-started/][Getting started]]
pages.
You can find the code here: https://github.com/tonyaldon/one.el.
Athough ~one.el~ uses org-mode not all the org elements are useful to
build technical blog sites (see [[#/#why][Why one.el?]]). So only a few org
elements have an transcoder function implemented in [[#/docs/one-ox/][one-ox]], the org
backend used by ~one.el~ to build the default website (see [[#/docs/one-default-render-function/][one-default
render function]]). Please check [[#/docs/one-ox/#org-elements-not-supported][Org elements not supported]] before
relying on ~one.el~.
In ~one.el~, the following org document defines a website with 3 pages
that we build by calling ~one-build~ command while we are visiting it:
#+BEGIN_SRC org
,* My website
:PROPERTIES:
:ONE: one-default-home
:CUSTOM_ID: /
:END:
Welcome to my website!
,* Blog post 1
:PROPERTIES:
:ONE: one-default
:CUSTOM_ID: /blog/page-1/
:END:
My first blog post!
,* Blog post 2
:PROPERTIES:
:ONE: one-default
:CUSTOM_ID: /blog/page-2/
:END:
My second blog post!
#+END_SRC
Note that if we want to use the default css style sheet we can add it
by calling ~one-default-add-css-file~ before building the website.
The path ~/~ in the first ~CUSTOM_ID~ org property tells ~one.el~ that the
page "My website" is the home page. That page is rendered using
~one-default-home~ render function, value of ~ONE~ org property of the
same headline.
The path ~/blog/page-1/~ in the second ~CUSTOM_ID~ org property tells
~one.el~ that we want to render "Blog post 1" page in such a way
that when we serve our website locally at ~http://localhost:3000~ for
instance, that page is served at ~http://localhost:3000/blog/page-1/~.
How that page is rendered is determined by the value of ~ONE~ org
property of the same headline which is ~one-default~, a render
function.
The same goes for the last page "Blog post 2".
As you might have noticed, a ~one.el~ website is an org file where the
pages are the headlines of level 1 with the org properties ~ONE~ and
~CUSTOM_ID~ set. Nothing more!
~ONE~ is the only org property added by ~one.el~. Its value, an Emacs Lisp
function which returns an HTML string, for a given page determines how
~one.el~ renders that page.
Paths of pages are set using ~CUSTOM_ID~ org property.
With that said, if you want to try it you can check [[#/docs/install-one-el/][Install one.el]] and
[[#/docs/getting-started/][Getting started]] pages.
** Why one.el?
:PROPERTIES:
:CUSTOM_ID: /#why
:END:
I wrote ~one.el~ because I didn't find an existing static site generator
with the following requirements:
- I'm not looking for a solution for every type of websites, only for
technical blog sites which are basically chunks of code surrounded
by text,
- I want something simple that I understand and that I can modify
only by writting some Emacs Lisp,
- I want websites to be written to a single org file,
- I want something with no dependencies other than emacs packages
that are not bridges to feed other static site frameworks,
- I want something with no configuration options, if you want to
modify something you write Emacs Lisp code and
- Finally, I want an Emacs solution for an Emacs user.
Following those requirements led me to ~one.el~, an opiniated static
site generator for Emacs Lisp programmers and Org mode users that
works well if you want to build websites like
- [[https://minibuffer.tonyaldon.com][minibuffer]] ([[https://github.com/tonyaldon/minibuffer.tonyaldon.com][source]]): learn Emacs Lisp one sexp at a time,
- [[https://posts.tonyaldon.com][Elisp posts]]: some articles about Emacs Lisp,
- [[https://jack.tonyaldon.com][jack]]: HTML generator library for Emacs Lisp,
- [[https://one.tonyaldon.com][one.el]]: documentation of one.el package,
- [[https://lnroom.live][LNROOM]]: learn how to hack on Core Lightning and
- https://tonyaldon.com.
all built with ~one.el~.
* Install one.el
:PROPERTIES:
:ONE: one-default-doc
:CUSTOM_ID: /docs/install-one-el/
:END:
** Manually
~one.el~ depends on [[https://jack.tonyaldon.com/][jack]] and [[https://github.com/hniksic/emacs-htmlize][htmlize]] packages that are available on
[[https://melpa.org/][Melpa]]. Once you have them installed you can add ~one.el~ to your
~load-path~ and require it like this:
#+BEGIN_SRC emacs-lisp
(add-to-list 'load-path "/path/to/one.el/")
(require 'one)
#+END_SRC
** With package-install
~one.el~ is also available on [[https://melpa.org/][Melpa]] so you can install it like this:
#+BEGIN_SRC text
M-x package-install <RET> one <RET>
#+END_SRC
** With straight.el
If you're using [[https://github.com/radian-software/straight.el][straight.el]], to install ~one.el~ you just have to add
this sexp to your init file:
#+BEGIN_SRC emacs-lisp
(straight-use-package
'(one :type git :host github :repo "tonyaldon/one.el"
:build (:not compile)))
#+END_SRC
Note that ~:build (:not compile)~ is important. It tells ~straight.el~
not to byte compile ~one.el~. Something happened in ~straight.el~
between commits ~3eca39d~ and ~b3760f5~ which broke byte compilation of
~one.el~ if done by ~straight.el~.
** Let's go
Now you can create a new website by calling ~one-default-new-project~
(preferably in an empty directory) and you can build it by calling
~one-build~ command.
If this is the first time you try ~one.el~ reading [[#/docs/getting-started/][Getting started]]
page might be helpful.
* Getting started
:PROPERTIES:
:ONE: one-default-doc
:CUSTOM_ID: /docs/getting-started/
:END:
** Start a new project
By calling ~one-default-new-project~ command (preferably in an empty
directory) we produce a new ~one.el~ project with the following
structure:
#+BEGIN_SRC text
.
├── assets
│ └── one.css
└── one.org
#+END_SRC
Once done we can build the website under the directory ~./public/~
by calling ~one-build~ command while we are in the file ~one.org~. Our
project's structure is now:
#+BEGIN_SRC text
.
├── assets
│ └── one.css
├── one.org
└── public
├── blog
│ ├── default
│ │ └── index.html
│ ├── default-home-list-pages
│ │ └── index.html
│ ├── one-default-doc
│ │ └── index.html
│ ├── one-default-with-sidebar
│ │ └── index.html
│ └── one-default-with-toc
│ └── index.html
├── index.html
└── one.css
#+END_SRC
** Modify the content with live reloading
To get our website up and running, we serve the files in ~./public/~
subdirectory using [[https://browsersync.io][brower-sync]] (any webserver serving files is OK).
Once we have it installed, to start a webserver with live reloading,
we run the following commands (in a terminal):
#+BEGIN_SRC text
$ cd public
$ browser-sync start -s -w --files "*"
#+END_SRC
Assuming the port ~3000~ isn't used we have our website served at
~http://localhost:3000~.
Now we can modify the content of ~one.org~ file and see the changes
reflected in the browser after we rebuild/re-render the whole website
or part of it using the following commands ~one-build~, ~one-render-pages~
and ~one-render-page-at-point~ or the asynchronous version of those
commands ~one-build-async~, ~one-render-pages-async~ and
~one-render-page-at-point-async~.
** CSS style sheet
When we call ~one-build~ (or ~one-build-async~) command the pages of the
website are rendered in the directory ~./public/~ and the files in
~./assets/~ directory are copied into ~./public/~ subdirectory.
When we build a ~one.el~ website with the default render functions and
the default CSS style sheet (this is the case if we used
~one-default-new-project~ as we did above) the style sheet that applies
is ~./public/one.css~ file which is a copy of ~./assets/one.css~ file.
So in that case, to modify the website's layout we just have to modify
the file ~./assets/one.css~ and copy it in ~./public/~ directory either
with ~one-build~, ~one-build-async~ or ~one-copy-assets-to-public~.
*** Modify the CSS style sheet with live reloading
To get the file ~./assets/one.css~ copied into ~./public/~ directory each
time we modify it we can use [[https://eradman.com/entrproject/][entr]] utility like this (being at the root
of our project):
#+BEGIN_SRC text
$ ls assets/one.css | entr -s 'cp assets/one.css public/'
#+END_SRC
Combined with ~browser-sync~ live reloading I think we get a decent
programmer experience.
*** Source blocks
When we use the default render functions and the default CSS style
sheet, the org content is exported into HTML strings using ~one-ox~ org
export backend. Consequently, ~src-block~ elements are highlighted
using [[https://github.com/hniksic/emacs-htmlize][htmlize]].
See [[#/docs/one-ox-src-block/][one-ox | src-block]] for more information.
* How does one.el work?
:PROPERTIES:
:ONE: one-default-doc
:CUSTOM_ID: /docs/how-does-one-el-work/
:END:
In an org file containing all the pages of our website we can build
the website under ~./public/~ subdirectory by calling either ~one-build~
or ~one-render-pages~ commands.
The only difference between those two commands is that before
producing the HTML pages calling ~one-render-pages~, ~one-build~ command
cleans the subdirectory ~./public/~ and copies the content of ~./assets/~
subdirectory into ~./public/~ subdirectory.
So let's focus on ~one-render-pages~ command.
For each page of our website, the function ~one-render-pages~ uses
the render function set in ~ONE~ org property of the page to produce the
HTML string representing the page and stores it in an ~index.html~ file
whom path is determined by ~CUSTOM_ID~ org property of the page.
Render functions are at the heart of ~one.el~ mechanism. They
determined how pages are rendered. Specifically, render functions are
regular Elisp functions that takes 3 arguments
- ~page-tree~: corresponding to the parsed tree of the org entry defining
the page,
- ~pages~: the list of pages,
- ~global~: a plist of global informations that are computed once
in ~one-render-pages~ (see ~one-add-to-global~) before rendering the
pages
and return HTML strings.
For instance, the following ~hello-world~ function
#+BEGIN_SRC emacs-lisp
(defun hello-world (page-tree pages global)
"<h1>Hello world!</h1>")
#+END_SRC
defines a valid render function. We can use it to build a website
like this. In an empty directory, we create a file named ~one.org~ with
the following content:
#+BEGIN_SRC org
,* The home page
:PROPERTIES:
:ONE: hello-world
:CUSTOM_ID: /
:END:
,* Blog post 1
:PROPERTIES:
:ONE: hello-world
:CUSTOM_ID: /blog/page-1/
:END:
#+END_SRC
We visit that file and call ~one-build~ command. It produces the
following files
#+BEGIN_SRC text
.
├── one.org (already there)
└── public
├── blog
│ └── page-1
│ └── index.html
└── index.html
#+END_SRC
and both files ~./public/blog/page-1/index.html~ and
~./public/index.html~ have the same content:
#+BEGIN_SRC html
<h1>Hello world!</h1>
#+END_SRC
Therefore if we serve the website in ~./public/~ directory at
~http://localhost:3000~ we can access the two "Hello world!" pages
at ~http://localhost:3000/blog/page-1/~ and ~http://localhost:3000~.
That's it! This is how ~one.el~ works under the hood.
~one.el~ comes with predefined render functions, a custom CSS style
sheet and a custom [[#/docs/one-ox/][org export backend]] which are used all together to
build that documentation for instance.
See [[#/docs/getting-started/][Getting started]] to start a new project with those defaults.
See [[#/docs/one-default-render-function/][one-default render function]] to take inspiration and write your own
render functions.
* one-default render function
:PROPERTIES:
:ONE: one-default-doc
:CUSTOM_ID: /docs/one-default-render-function/
:END:
In [[#/docs/how-does-one-el-work/][How does one.el work?]] page we saw that render functions are at
the heart of ~one.el~ mechanism. They determine how pages are
rendered.
We saw that
#+BEGIN_SRC emacs-lisp
(defun hello-world (page-tree pages global)
"<h1>Hello world!</h1>")
#+END_SRC
defines a valid render function that can be used to render pages of a
~one.el~ website by setting ~ONE~ org property to ~hello-world~ like this
for instance:
#+BEGIN_SRC org
,* The home page
:PROPERTIES:
:ONE: hello-world
:CUSTOM_ID: /
:END:
,* Blog post 1
:PROPERTIES:
:ONE: hello-world
:CUSTOM_ID: /blog/page-1/
:END:
#+END_SRC
~one.el~ comes with several default render functions that can be used
instead of the dummy ~hello-world~ function:
- ~one-default-home~: org content,
- ~one-default-home-list-pages~: org content followed by the list in
reverse order of the pages of the website,
- ~one-default~: org content with navigation buttons at the bottom to go
to the previous page, the next page or a random one,
- ~one-default-with-toc~: same as ~one-default~ but with a table of
content at the top of the page and
- ~one-default-with-sidebar~: same as ~one-default~ but with a sidebar
listing all the pages in the website,
- ~one-default-doc~: same as ~one-default-with-sidebar~ but with a table
of content at the top of the page.
Those default render functions use [[#/docs/one-ox/][one-ox]] custom org export backend and
~one-default-css~ custom CSS style sheet.
If we want to start a new project using these defaults, we can use
~one-default-new-project~ command (see [[#/docs/getting-started/][Getting started]]).
If you plan to write your own render functions you may find the
following sections interesting.
*** The org document
Let's consider the following org document in a file named ~one.org~ for
instance:
#+BEGIN_SRC org
,* Home
:PROPERTIES:
:ONE: one-default-home
:CUSTOM_ID: /
:END:
,* Page 1
:PROPERTIES:
:ONE: one-default
:CUSTOM_ID: /blog/page-1/
:END:
,** Headline foo 1
[[#/blog/page-2/][Link to Page 2]]
,** Headline foo 2
,*** Headline bar
Some content.
,*** Headline baz
:PROPERTIES:
:CUSTOM_ID: /blog/page-1/#baz
:END:
,#+BEGIN_SRC emacs-lisp
(message "foo bar baz")
,#+END_SRC
,* Page 2
:PROPERTIES:
:ONE: one-default
:CUSTOM_ID: /blog/page-2/
:END:
[[#/blog/page-1/#baz][Link to Headline baz in Page 1]]
#+END_SRC
Let's generate the file ~./assets/one.css~ that contains the content of
~one-default-css~ string by calling ~one-default-add-css-file~ command.
Our project structure is now:
#+BEGIN_SRC text
.
├── assets
│ └── one.css
└── one.org
#+END_SRC
*** Build the website
Now, while vising the file ~one.org~ we call ~one-build~ which builds
"Home", "Page 1" and "Page 2" pages under the directory ~./public/~ such
that our project tree is now:
#+BEGIN_SRC text
.
├── assets
│ └── one.css
├── one.org
└── public
├── blog
│ ├── page-1
│ │ └── index.html
│ └── page-2
│ └── index.html
├── index.html
└── one.css
#+END_SRC
*** Home
The page "Home" has been generated:
- in the file ~./public/index.html~ respecting the path information ~/~ in
~CUSTOM_ID~ org property and
- its HTML content has been created using ~one-default-home~ render
function specified in ~ONE~ org property.
~./public/index.html~ (pretty printed for the demonstration):
#+BEGIN_SRC html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="stylesheet" type="text/css" href="/one.css" />
<title>Home</title>
</head>
<body>
<div class="header">Home</div>
<div class="content">
<div id="home"><div></div></div>
</div>
</body>
</html>
#+END_SRC
*** Page 1
The page "Page 1" has been generated:
- in the file ~./public/blog/page-1/index.html~ respecting the path
information ~/blog/page-1/~ in ~CUSTOM_ID~ org property and
- its HTML content has been created using ~one-default~ render function
specified in ~ONE~ org property.
~./public/blog/page-1/index.html~ (pretty printed for the demonstration):
#+BEGIN_SRC html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="stylesheet" type="text/css" href="/one.css" />
<title>Page 1</title>
</head>
<body>
<div class="header"><a href="/">Home</a></div>
<div class="content">
<div class="title">
<div class="title"><h1>Page 1</h1></div>
</div>
<div></div>
<div>
<h2 id="one-df8f0f16cc">Headline foo 1</h2>
<div>
<p><a href="/blog/page-2/">Link to Page 2</a></p>
</div>
</div>
<div>
<h2 id="one-9c2f3b8536">Headline foo 2</h2>
<div>
<h3 id="one-fe469dd578">Headline bar</h3>
<div><p>Some content.</p></div>
</div>
<div>
<h3 id="baz">Headline baz</h3>
<div>
<pre><code class="one-hl one-hl-block">(message <span class="one-hl-string">"foo bar baz"</span>)</code></pre>
</div>
</div>
</div>
<div class="nav">
<a href="/">PREV</a><a href="/">RANDOM</a
><a href="/blog/page-2/">NEXT</a>
</div>
</div>
</body>
</html>
#+END_SRC
*** Page 2
The page "Page 2" has been generated:
- in the file ~./public/blog/page-2/index.html~ respecting the path
information ~/blog/page-2/~ in ~CUSTOM_ID~ org property and
- its HTML content has been created using ~one-default~ render function
specified in ~ONE~ org property.
~./public/blog/page-2/index.html~ (pretty printed for the demonstration):
#+BEGIN_SRC html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="stylesheet" type="text/css" href="/one.css" />
<title>Page 2</title>
</head>
<body>
<div class="header"><a href="/">Home</a></div>
<div class="content">
<div class="title">
<div class="title"><h1>Page 2</h1></div>
</div>
<div>
<p><a href="/blog/page-1/#baz">Link to Headline baz in Page 1</a></p>
</div>
<div class="nav">
<a href="/blog/page-1/">PREV</a><a href="/">RANDOM</a>
</div>
</div>
</body>
</html>
#+END_SRC
*** How was "Page 1" built?
When we called ~one-build~ in ~one.org~ buffer, the whole buffer was
parsed with the function ~one-parse-buffer~ and a list of pages was
built from that parsed tree and looked like this:
#+BEGIN_SRC emacs-lisp
((:one-title "Home"
:one-path "/"
:one-render-page-function one-default-home
:one-page-tree (headline (:raw-value "Home" ...) ...))
(:one-title "Page 1"
:one-path "/blog/page-1/"
:one-render-page-function one-default
:one-page-tree (headline (:raw-value "Page 1" ...) ...))
(:one-title "Page 2"
:one-path "/blog/page-2/"
:one-render-page-function one-default
:one-page-tree (headline (:raw-value "Page 2" ...) ...)))
#+END_SRC
Let's call ~pages~ that list of pages.
Then for each ~page~ in ~pages~ the function ~one-render-page~ was called
with ~page~, ~pages~ and ~global~ (see ~one-add-to-global~ variable) as
arguments.
Finally, in ~one-render-page~ the function ~one-default~ or
~one-default-home~ was called with the arguments ~page-tree~, ~pages~ and
~global~ to create the HTML content of each page whom path under the
directory ~./public/~ was determined by the value of ~:one-path~ property
in ~page~ and ~page-tree~ was the value of ~:one-page-tree~ property in
~page~.
Focusing on "Page 1", the function ~one-default~ was called with the
arguments ~page-tree~, ~page~ and ~global~ with ~page-tree~ being the
following parsed tree of the headline defining "Page 1":
#+BEGIN_SRC emacs-lisp
(headline
(:raw-value "Page 1"
:CUSTOM_ID "/blog/page-1/"
:ONE "one-default"
:parent (org-data ...)
:one-internal-id "one-9c81c230b6"
...)
(section (...) (property-drawer ...))
(headline
(:raw-value "Headline foo 1"
:one-internal-id "one-4df8d962d9"
...)
(section (...) (paragraph ...)))
(headline
(:raw-value "Headline foo 2"
:one-internal-id "one-9d89da8271"
...)
(headline
(:raw-value "Headline bar"
:one-internal-id "one-95fa001487"
...)
(section
(...)
(paragraph (...) #("Some content. " 0 14 (:parent #4)))))
(headline
(:raw-value "Headline baz"
:CUSTOM_ID "/blog/page-1/#baz"
:one-internal-id "baz"
...)
(section
(...)
(property-drawer ...)
(src-block
(:language "emacs-lisp"
:value "(message \"foo bar baz\")"
...))))))
#+END_SRC
In ~one-default~ the org content of "Page 1" was exported into a HTML
string using ~org-export-data-with-backend~ and [[#/docs/one-ox/][one-ox]] custom org export
backend. Then this HTML string was used in a data structure
representing the HTML page. Finally, ~jack-html~ (see [[https://jack.tonyaldon.com/][jack]]) transformed
that data structure into a HTML string which was written on the file
~./public/blog/page-1/index.html~:
#+BEGIN_SRC emacs-lisp
(defun one-default (page-tree pages _global)
"Default render function.
See `one-is-page', `one-render-pages' and `one-default-css'."
(let* ((title (org-element-property :raw-value page-tree))
(path (org-element-property :CUSTOM_ID page-tree))
(content (org-export-data-with-backend
(org-element-contents page-tree)
'one-ox nil))
(website-name (one-default-website-name pages))
(nav (one-default-nav path pages)))
(jack-html
"<!DOCTYPE html>"
`(:html
(:head
(:meta (@ :name "viewport" :content "width=device-width,initial-scale=1"))
(:link (@ :rel "stylesheet" :type "text/css" :href "/one.css"))
(:title ,title))
(:body
(:div.header (:a (@ :href "/") ,website-name))
(:div.content
(:div.title
,(if (not (string= path "/"))
`(:div.title (:h1 ,title))
'(:div.title-empty)))
,content
,nav))))))
#+END_SRC
* Miscellaneous
:PROPERTIES:
:ONE: one-default-doc
:CUSTOM_ID: /docs/miscellaneous/
:END:
** Page at point
If we need to render only the page at point, meaning the headline of
level 1 with ~ONE~ and ~CUSTOM_ID~ org properties set, we can use the
commands ~one-render-page-at-point~ and ~one-render-page-at-point-async~.
** onerc.el file
We can use an Emacs Lisp file called ~onerc.el~ to customize our
website. It must be in the same directory of the org file containing
the content of our website.
This file is loaded first in ~one-render-pages~ before rendering the
webpages.
This is a good place to set ~one-add-to-global~ and ~one-hook~ variables
or to define our own render functions.
** one-add-to-global
:PROPERTIES:
:CUSTOM_ID: /docs/miscellaneous/#one-add-to-global
:END:
Render functions takes 3 arguments:
- ~page-tree~: the parsed tree of the page being rendered,
- ~pages~: the list of pages,
- ~global~: a plist of global informations that are computed once
in ~one-render-pages~ before rendering the pages using
~one-add-to-global~ variable.
That means that if a render function needs extra informations, we can
use ~one-add-to-global~ variable to pass those informations to the
render function.
Specifically, elements in ~one-add-to-global~ list are plist with the
following properties:
- ~:one-global-property~: a keyword that is used as proprety
in the ~global~ argument passed to the render functions,
- ~:one-global-function~: a function that takes two arguments ~pages~
(list of pages, see ~one-list-pages~) and ~tree~
(see ~one-parse-buffer~). That function is called once in
~one-render-pages~ and its result is used as the value of
the property ~:one-global-property~ in the ~global~ argument
passed to the render functions.
For instance, if ~one-add-to-global~ is set to
#+BEGIN_SRC emacs-lisp
((:one-global-property :one-tree
:one-global-function (lambda (pages tree) tree)))
#+END_SRC
then ~global~ local variable will be set to
#+BEGIN_SRC emacs-lisp
((:one-tree tree))
#+END_SRC
where ~tree~ is the value returned by ~one-parse-buffer~ function.
** one-hook
Each function in ~one-hook~ is called once in ~one-render-pages~.
Those functions take three arguments:
- ~pages~: list of pages (see ~one-list-pages~),
- ~tree~: see ~one-parse-buffer~,
- ~global~: see [[#/docs/miscellaneous/#one-add-to-global][one-add-to-global]].
As those functions take ~global~ argument they are called after
that argument has been let binded using ~one-add-to-global~.
*** feed.xml example
This hook is used to build ~feed.xml~ file of [[https://minibuffer.tonyaldon.com][minibuffer.tonyaldon.com]]
website. You can check ~onerc.el~ file of
[[https://github.com/tonyaldon/minibuffer.tonyaldon.com][tonyaldon/minibuffer.tonyaldon.com]] repository to see how it is done.
*** robot.txt and sitemap.txt
If we want to add a ~sitemap.txt~ file to our website we can do so using
~one-hook~.
**** robot.txt
First we need to indicate in a ~robots.txt~ where our ~sitemap.txt~ is
located.
Assuming our website is ~https://example.com~ and our ~sitemap.txt~ file
is at the root of it, we can add the following ~robots.txt~ file in the
~assets~ directory (~./assets/robots.txt~):
#+BEGIN_SRC text
User-Agent: *
Allow: /
Sitemap: https://domain.com/sitemap.txt
#+END_SRC
**** sitemap.txt
Now in ~onerc.el~ file:
1) we set our domain with protocol in the variable ~domain~,
2) then we define ~make-sitemap~ function which will create the file
~sitemap.txt~ in the ~public~ directory (~./public/sitemap.txt~) each
time be build our website,
3) Finally, to tell ~one.el~ to actually create ~sitemap.txt~ file using
~make-sitemap~ function each time be build our website, we add it
to ~one-hook~:
#+BEGIN_SRC emacs-lisp
(defvar domain "https://example.com"
"Domain with protocol to be used to produce sitemap file.
See `make-sitemap'.")
(defun make-sitemap (pages tree global)
"Produce file ./public/sitemap.txt
Global variable `domain' is used as domain with protocol.
This function is meant to be added to `one-hook'."
(with-temp-file "./public/sitemap.txt"
(insert
(mapconcat 'identity
(mapcar
(lambda (page)
(let* ((path (plist-get page :one-path))
(link (concat domain path)))
link))
pages)
"\n"))))
(add-hook 'one-hook 'make-sitemap)
#+END_SRC
Thanks [[https://github.com/tanrax][@tanrax]] for the code snippet (see [[https://github.com/tonyaldon/one.el/issues/6][issue #6]]).
** Async commands
The function ~one-render-pages-async~ and ~one-build-async~ spawn an
~emacs~ subprocess in order to build html pages asynchronously. The
arguments passed to ~emacs~ depends on ~one-emacs-cmd-line-args-async~ value.
By default, when ~one-emacs-cmd-line-args-async~ is ~nil~, we run ~emacs~
in "batch mode", we load the user's initialization file and we
evaluate a specific sexp that builds html pages. Specifically, we
pass the following ~command~ (~emacs~ file name followed by command line
arguments) to ~make-process~ function like this:
#+BEGIN_SRC emacs-lisp
(let* ((emacs (file-truename
(expand-file-name invocation-name invocation-directory)))
(command `(,emacs "--batch"
"-l" ,user-init-file
"--eval" ,sexp))
(sexp ...))
(make-process
:name ...
:buffer ...
:command command))
#+END_SRC
If ~one-emacs-cmd-line-args-async~ is non-nil, we no longer load the user's
initialization file and replace ~"-l" ,user-init-file~ in ~command~ above
by the elements of ~one-emacs-cmd-line-args-async~. For instance, if
~one-emacs-cmd-line-args-async~ is equal to
#+BEGIN_SRC emacs-lisp
'("-l" "/path/to/some-elisp-file.el")
#+END_SRC
then ~command~ becomes
#+BEGIN_SRC emacs-lisp
(let* (...
(command `(,emacs "--batch"
"-l" "/path/to/some-elisp-file.el"
"--eval" ,sexp))
...)
...)
#+END_SRC
** Extend one-ox org backend
:PROPERTIES:
:CUSTOM_ID: /docs/miscellaneous/#extend-one-ox-org-backend
:END:
When we use the default render functions, the org content of the
webpages is exported using [[#/docs/one-ox/][one-ox]] org backend like this
#+BEGIN_SRC emacs-lisp
(org-export-data-with-backend
(org-element-contents page-tree)
'one-ox nil)
#+END_SRC
where ~page-tree~ is the parsed tree of the headline containing the page
being rendered (see [[#/docs/one-default-render-function/][one-default render function]]).
While ~one-ox~ exports enough org elements for my use cases (see [[#/#why][Why
one.el?]]) this might not be the case for you.
I think this is not a big problem because we can extend ~one-ox~
(precisely we can derive a new org backend from ~one-ox~ org backend)
with other transcoder functions for the org elements that miss
transcoder functions.
Let's see how we can do that with an example.
*** Extend one-ox with horizontal-rule org elements
Lines consisting of only dashes (at least 5) are parsed by the org
parser as ~horizontal-rule~ org elements. ~one-ox~ doesn't provide a
transcoder function for ~horizontal-rule~ so we can't use it directly if
we want to have them exported as ~<hr>~ tags in our website.
In that section we see how to derived an org backend ~one-ox-with-hr~
from ~one-ox~ org backend that exports ~horizontal-rule~ org elements
with ~<hr>~ tags.
To do that we define a transcoder function ~my-horizontal-rule~ which
takes 3 arguments (not used) and return the string ~"<hr>"~:
#+BEGIN_SRC emacs-lisp
(defun my-horizontal-rule (_ _ _) "<hr>")
#+END_SRC
Then we use that function in the ~:translate-alist~ alist in the body of
the function ~org-export-define-derived-backend~ to define ~one-ox-with-hr~
org backend:
#+BEGIN_SRC emacs-lisp
(org-export-define-derived-backend 'one-ox-with-hr 'one-ox
:translate-alist
'((horizontal-rule . my-horizontal-rule)))
#+END_SRC
Then we can export the org content of the webpages (including the
~horizontal-rule~) using ~one-ox-with-hr~ org backend like this
#+BEGIN_SRC emacs-lisp
(org-export-data-with-backend
(org-element-contents page-tree)
'one-ox-with-hr nil)
#+END_SRC
where ~page-tree~ is the parsed tree of the headline containing the page
being rendered.
Now that we saw how to derive ~one-ox-with-hr~ org backend and use it,
let's build a website with only a home page with two ~horizontal-rule~.
In an empty directory let's add the following files:
- ~one.org~:
#+BEGIN_SRC org
,* Home page
:PROPERTIES:
:ONE: my-render-function
:CUSTOM_ID: /
:END:
foo
-----
bar
-----
baz
#+END_SRC
- ~onerc.el~:
#+BEGIN_SRC emacs-lisp
(defun my-horizontal-rule (_ _ _) "<hr>")
(org-export-define-derived-backend 'one-ox-with-hr 'one
:translate-alist
'((horizontal-rule . my-horizontal-rule)))
(defun my-render-function (page-tree pages _global)
""
(let* ((title (org-element-property :raw-value page-tree))
(content (org-export-data-with-backend
(org-element-contents page-tree)
'one-ox-with-hr
nil)))
(jack-html
"<!DOCTYPE html>"
`(:html
(:head (:title ,title))
(:body
(:h1 ,title)
,content)))))
#+END_SRC
Now while visiting ~one.org~ file we call ~one-build~ to build our website
with ~<hr>~ tags.
* one-ox
:PROPERTIES:
:ONE: one-default-doc
:CUSTOM_ID: /docs/one-ox/
:END:
** Org export backend used by the default render functions
~one.el~ (specifically the default render functions) uses its own org
export backend called ~one-ox~ to export the org content of the pages
into HTML strings.
For instance, the render function ~one-default~ takes as first argument
~page-tree~ which is the current page being rendered (~page-tree~ is the
org parsed data structure representing the page) and exports it as an
HTML string using ~org-export-data-with-backend~ function and ~one-ox~
export backend and uses it to render the HTML page:
#+BEGIN_SRC emacs-lisp
(defun one-default (page-tree pages _global)
"..."
(let* (...
(content (org-export-data-with-backend
(org-element-contents page-tree)
'one-ox nil))
...)
(jack-html
"<!DOCTYPE html>"
`(:html
(:head ...)
(:body ... (:div.content ... ,content ,nav))))))
#+END_SRC
This org backend is taylor for ~one.el~ usage. So it doesn't try to
export all the org elements unlike ~html~ backend and when the org
elements are exported they differ from what we can expect from ~html~
backend.
For instance ~headline~ elements don't take into account markups
neither links.
Another example are the ~link~ elements. They don't support org fuzzy
links and links to local files that are not in the subdirectories
~./public/~ or ~./assets/~ raise errors.
You can read how the supported org elements are exported by ~one-ox~ org
backend in the following page:
- [[#/docs/one-ox-headline/][one-ox | headline]],
- [[#/docs/one-ox-src-block/][one-ox | src-block]],
- [[#/docs/one-ox-quote-block/][one-ox | quote-block]],
- [[#/docs/one-ox-fixed-width-and-example-block/][one-ox | fixed-width and example-block]],
- [[#/docs/one-ox-links/][one-ox | links]] and
- [[#/docs/one-ox-plain-list/][one-ox | plain-list]].
** Org elements not supported
:PROPERTIES:
:CUSTOM_ID: /docs/one-ox/#org-elements-not-supported
:END:
The org elements that are not supported are the following:
~center-block~, ~clock~, ~drawer~, ~dynamic-block~, ~entity~, ~export-block~,
~export-snippet~, ~footnote-reference~, ~horizontal-rule~, ~inline-src-block~,
~inlinetask~, ~keyword~, ~latex-environment~, ~latex-fragment~, ~line-break~,
~node-property~, ~planning~, ~property-drawer~, ~radio-target~, ~special-block~,
~statistics-cookie~, ~table~, ~table-cell~, ~table-row~, ~target~, ~timestamp~,
~verse-block~.
Note that "not supported" means they are not rendered by default by
~one.el~ but we can still use them or even extend ~one-ox~ org export
backend to take some of them into account.
Why doesn't ~one.el~ support all org elements?
1. I don't need those org elements to write my technical blogs:
- I don't do math. No support for Latex,
- I don't use table. No support for tables,
- etc.
2. ~one-ox~ org backend is used only by the default render functions, so
if you need more org elements you can either use another org
backend or extend ~one-ox~ org backend and use this other org backend
in your own render functions (See [[#/docs/miscellaneous/#extend-one-ox-org-backend][Extend one-ox org backend]]).
* one-ox | headline
:PROPERTIES:
:ONE: one-default-doc
:CUSTOM_ID: /docs/one-ox-headline/
:END:
Note that markups and links are not exported if used in headlines, only
the raw value string.
So don't use them in headlines.
* one-ox | src-block
:PROPERTIES:
:ONE: one-default-doc
:CUSTOM_ID: /docs/one-ox-src-block/
:END:
** Code highlighting with htmlize
*** Description
~one-ox~ highlights code via the function ~one-ox-htmlize~ that uses
[[https://github.com/hniksic/emacs-htmlize][htmlize]] to do the work.
For a given piece of code ~X~ in a certain language ~Y~, ~X~ will be
highlighted as it would be in the emacs mode ~Z~ used to edit ~Y~ code.
For instance, ~clojure-mode~ is used to highlight Clojure code and
~sh-mode~ is used to highlight Bash code.
Attributes of a face (like ~background-color~ or ~foreground-color~)
are not taken directly. A generated name for the face is produced and
used as the CSS class for the parts of the code ~X~ that are highlighted
with that face.
For instance, in ~sh-mode~, the word ~echo~ is highlighted with the face
~font-lock-builtin-face~. So, the word ~echo~ in a piece of Shell (or
Bash) code will be transformed into:
#+BEGIN_SRC html
<span class="one-hl-builtin">echo</span>
#+END_SRC
The whole piece of code ~X~, once the previously described operations
have been done, is wrapped:
1) for a normal block with the component:
#+BEGIN_SRC html
<pre><code class="one-hl one-hl-block">...</code></pre>
#+END_SRC
2) for a result block with the component:
#+BEGIN_SRC html
<pre><code class="one-hl one-hl-results">...</code></pre>
#+END_SRC
See section [[#/docs/one-ox-src-block/#org-keywords-results-and-attr_one_results][org keyword RESULTS]].
*** Example with Bash code
For instance, the following org src-block, containing some ~bash~ code:
#+BEGIN_SRC org
,#+BEGIN_SRC bash
echo "list file's extensions in current dir:"
for f in `ls`; do
echo ${f##*.}
done
,#+END_SRC
#+END_SRC
is exported as follow:
#+BEGIN_SRC html
<pre><code class="one-hl one-hl-block"><span class="one-hl-builtin">echo</span> <span class="one-hl-string">"list file's extensions in current dir:"</span>
<span class="one-hl-keyword">for</span> f<span class="one-hl-keyword"> in</span> <span class="one-hl-sh-quoted-exec">`ls`</span>; <span class="one-hl-keyword">do</span>
<span class="one-hl-builtin">echo</span> ${<span class="one-hl-variable-name">f</span>##*.}
<span class="one-hl-keyword">done</span></code></pre>
</div>
#+END_SRC
and rendered like this:
#+BEGIN_SRC bash
echo "list file's extensions in current dir:"
for f in `ls`; do
echo ${f##*.}
done
#+END_SRC
Note that ~one-ox-htmlize~ has produced and used the following CSS
classes (listed with their corresponding emacs faces):
#+BEGIN_SRC text
# from font-lock
one-hl-builtin --> font-lock-builtin-face
one-hl-keyword --> font-lock-keyword-face
one-hl-string --> font-lock-string-face
one-hl-variable-name --> font-lock-variable-name-face
# specific to sh-mode
one-hl-sh-quoted-exec --> sh-quoted-exec
#+END_SRC
You might have notice the pattern used for ~font-lock~ faces and the one
used for mode specific faces.
~one.el~ provides a default style sheet (~one-default-css~) that has the
CSS classes defined for all the ~font-lock~ faces (faces starting by
~font-lock-~) but not the specific faces used by each prog mode.
You can add the CSS classes specific to the prog modes you use as you
go and need them.
** Org keyword RESULTS
:PROPERTIES:
:CUSTOM_ID: /docs/one-ox-src-block/#org-keywords-results-and-attr_one_results
:END:
Result blocks are preceded by a line starting with ~#+RESULTS:~. Blocks
that are not result blocks are normal blocks.
When exported, normal blocks and result blocks differ only by their
CSS classes:
- ~one-hl one-hl-block~ for normal blocks,
- ~one-hl one-hl-results~ for result blocks.
This way result blocks can be rendered with a different style
than normal blocks as we can see in the following example.
*** Example using org keyword 'RESULTS'
The following org snippet:
#+BEGIN_SRC org
,#+BEGIN_SRC bash :results output
ls
,#+END_SRC
,#+RESULTS:
: assets
: docs.org
: public
#+END_SRC
is exported by ~one-ox~ as follow:
#+BEGIN_SRC html
<pre><code class="one-hl one-hl-block">ls</code></pre>
<pre><code class="one-hl one-hl-results">assets
docs.org
public</code></pre>
#+END_SRC
and is rendered by ~one-ox~ with the first block (normal block) having a
different style from second block (result block):
#+BEGIN_SRC bash :results output
ls
#+END_SRC
#+RESULTS:
: assets
: docs.org
: public
** Code blocks inside list
Lists can contain source blocks as we can see in the following org
snippet
#+BEGIN_SRC org
1. item 1
,#+BEGIN_SRC emacs-lisp
(message "src-block in item 1")
,#+END_SRC
2. item 2
3. item 3
#+END_SRC
which is exported by ~one~ as follow
#+BEGIN_SRC html
<ol>
<li>
<p>item 1</p>
<pre><code class="one-hl one-hl-block">(message <span class="one-hl-string">"src-block in item 1"</span>)</code></pre>
</li>
<li><p>item 2</p></li>
<li><p>item 3</p></li>
</ol>
#+END_SRC
and is rendered by ~one-ox~ like this:
1. item 1
#+BEGIN_SRC emacs-lisp
(message "src-block in item 1")
#+END_SRC
2. item 2
3. item 3
* one-ox | quote-block
:PROPERTIES:
:ONE: one-default-doc
:CUSTOM_ID: /docs/one-ox-quote-block/
:END:
Blocks defined with ~#+BEGIN_QUOTE ... #+END_QUOTE~ pattern are
quote-block.
They are exported by ~one-ox~ in a ~<blockquote>...</blockquote>~
component with the CSS class ~one-blockquote~.
The following org snippet:
#+BEGIN_SRC org
,#+BEGIN_QUOTE
A quitter never wins and a winner never quits. —Napoleon Hill
,#+END_QUOTE
#+END_SRC
defines a quote and is exported by ~one-ox~ as follow
#+BEGIN_SRC html
<blockquote class="one-blockquote"><p>A quitter never wins and a winner never quits. —Napoleon Hill</p></blockquote>
#+END_SRC
and looks like this
#+BEGIN_QUOTE
A quitter never wins and a winner never quits. —Napoleon Hill
#+END_QUOTE
* one-ox | fixed-width and example-block
:PROPERTIES:
:ONE: one-default-doc
:CUSTOM_ID: /docs/one-ox-fixed-width-and-example-block/
:END:
** Description
A line starting with a colon ~:~ followed by a space defines a
~fixed-width~ element. A ~fixed-width~ element can span several
lines.
Blocks defined with ~#+BEGIN_EXAMPLE ... #+END_EXAMPLE~ pattern are
~example-block~ elements.
Both ~fixed-width~ and ~example-block~ blocks are treated as [[#/docs/one-ox-src-block/][src-block]] in
~text-mode~. So:
1. they are highlighted as ~text-mode~ would do,
2. they are exported in ~<pre><code>...</code></pre>~ components
(indentation and newlines are respected) and
3. the CSS classes used depend on the block's type:
- normal blocks use ~one-hl one-hl-block~ CSS classes and
- result blocks use ~one-hl one-hl-results~ CSS classes (see [[#/docs/one-ox-src-block/#org-keywords-results-and-attr_one_results][org
keyword RESULTS]]).
** Example
The following org snippet
#+BEGIN_SRC org
Here is a ~fixed-width~ element (one line):
: I'm a fixed-width element
~fixed-width~ elements can also be used within lists:
- item 1
: fixed-width element
- item 2
,#+BEGIN_SRC bash :results output
printf 'multiline fixed-width element\nthat is also a result block,\nso has a different style.'
,#+END_SRC
,#+RESULTS:
: multiline fixed-width element
: that is also a result block,
: so has a different style.
Although I don't often use ~example-block~ elements, here is one:
,#+BEGIN_EXAMPLE
This is
an example!
,#+END_EXAMPLE
#+END_SRC
is exported by ~one~ as follow
#+BEGIN_SRC html
<p>Here is a <code class="one-hl one-hl-inline">fixed-width</code> element (one line):
</p>
<pre><code class="one-hl one-hl-block">I'm a fixed-width element</code></pre>
<p><code class="one-hl one-hl-inline">fixed-width</code> elements can also be used within lists:
</p>
<ul><li><p>item 1
</p>
<pre><code class="one-hl one-hl-block">fixed-width element</code></pre>
</li>
<li><p>item 2
</p>
<pre><code class="one-hl one-hl-block"><span class="one-hl-builtin">printf</span> <span class="one-hl-string">'multiline fixed-width element\nthat is also a result block,\nso has a different style.'</span></code></pre>
<pre><code class="one-hl one-hl-results">multiline fixed-width element
that is also a result block,
so has a different style.</code></pre>
</li>
</ul>
<p>Although I don&apos;t often use <code class="one-hl one-hl-inline">example-block</code> elements, here is one:
</p>
<pre><code class="one-hl one-hl-block">This is
an example!</code></pre>
#+END_SRC
and looks like this:
Here is a ~fixed-width~ element (one line):
: I'm a fixed-width element
~fixed-width~ elements can also be used within lists:
- item 1
: fixed-width element
- item 2
#+BEGIN_SRC bash :results output
printf 'multiline fixed-width element\nthat is also a result block,\nso has a different style.'
#+END_SRC
#+RESULTS:
: multiline fixed-width element
: that is also a result block,
: so has a different style.
Although I don't often use ~example-block~ elements, here is one:
#+BEGIN_EXAMPLE
This is
an example!
#+END_EXAMPLE
* one-ox | links
:PROPERTIES:
:ONE: one-default-doc
:CUSTOM_ID: /docs/one-ox-links/
:END:
** http, https, mailto links
Web links (starting by ~http~ or ~https~) and links to message
composition (starting by ~mailto~) are exported as we expect.
For instance the following link
#+BEGIN_SRC org
http://tonyaldon.com
#+END_SRC
is exported as follow
#+BEGIN_SRC html
<a href="http://tonyaldon.com">http://tonyaldon.com</a>
#+END_SRC
and rendered like this: http://tonyaldon.com.
This following link with a description
#+BEGIN_SRC org
[[https://tonyaldon.com][Tony Aldon (https)]]
#+END_SRC
is exported as follow
#+BEGIN_SRC html
<a href="https://tonyaldon.com">Tony Aldon (https)</a>
#+END_SRC
and rendered like this: [[https://tonyaldon.com][Tony Aldon (https)]].
This ~mailto~ link
#+BEGIN_SRC org
[[mailto:tony@tonyaldon.com][send me an email]]
#+END_SRC
is exported as follow
#+BEGIN_SRC html
<a href="mailto:tony@tonyaldon.com">send me an email</a>
#+END_SRC
and rendered like this: [[mailto:tony@tonyaldon.com][send me an email]].
** Custom ID links
In ~one.el~, ~CUSTOM_ID~ org property is used to defined the path of pages
or the path to specific heading in pages.
Considering the following org document
#+BEGIN_SRC org
,* Home Page
:PROPERTIES:
:ONE: one-default-home
:CUSTOM_ID: /
:END:
- [[#/blog/page-1/]]
- [[#/blog/page-1/#headline-1]]
,* Page 1
:PROPERTIES:
:ONE: one-default
:CUSTOM_ID: /blog/page-1/
:END:
,** headline 1 in Page 1
:PROPERTIES:
:CUSTOM_ID: /blog/page-1/#headline-1
:END:
#+END_SRC
the link ~[[#/blog/page-1/]]~ in "Home Page" targets "Page 1" page
and the link ~[[#/blog/page-1/#headline-1]]~ in "Home Page" targets the
heading "headline 1 in page Page 1" in the "Page 1" page.
Those paths define valid web urls starting at the root of the website
if we respect the following rules for ~CUSTOM_ID~ values:
1. we use only url-encoded characters,
2. we start them with a ~/~ and end them with ~/~ excepted for the home
page which is a single ~/~,
3. we use ~#~ character to start the last part of the path when we are
targeting a heading tag with its ~id~ being the last part after the ~#~
character.
The benefits of these "rules/conventions" are:
1. when we export ~custom-id~ links using ~one-ox~ org backend we can
leave them as they are and
2. the navigation between pages inside emacs using ~custom-id~ links
works out-of-the-box.
*** Example of a link to a page
The following link
#+BEGIN_SRC org
[[#/docs/one-ox-plain-list/][one-ox | plain-list]]
#+END_SRC
is exported to this anchor tag that links to the page ~/docs/one-ox-plain-list/~:
#+BEGIN_SRC html
<a href="/docs/one-ox-plain-list/">one-ox | plain-list</a>
#+END_SRC
and is rendered like this [[#/docs/one-ox-plain-list/][one-ox | plain-list]].
*** Example of a link to a heading in a page
The following link
#+BEGIN_SRC org
[[#/docs/one-ox-plain-list/#unordered-lists][unordered lists heading in the page about plain-list]]
#+END_SRC
is exported to this anchor tag that links to the heading with the ~id~
set to ~unordered-lists~ on the page ~/docs/one-ox-plain-list/~:
#+BEGIN_SRC html
<a href="/docs/one-ox-plain-list/#unordered-lists">unordered lists heading in the page about plain-list</a>
#+END_SRC
and is rendered like this [[#/docs/one-ox-plain-list/#unordered-lists][unordered lists heading in the page about
plain-list]].
** Fuzzy links
I don't use ~fuzzy~ links. So, if there is a ~fuzzy~ link
in the document, that means I wrote the link wrong.
Broken links are bad user experience. I don't like them.
So I decided that ~one-ox~ raises an error (hard-coded) when we try to
export a fuzzy link to HTML.
For instance, the following ~fuzzy~ link:
#+BEGIN_SRC org
[[fuzzy search]]
#+END_SRC
raise an error like the following:
#+BEGIN_SRC emacs-lisp
(one-link-broken "fuzzy search" "fuzzy links not supported" "goto-char: 5523")
#+END_SRC
** File links
*** Links to local files in assets and public directories
Links to local files in ~./assets/~ and ~./public/~ directories like
#+BEGIN_SRC org
[[./assets/foo/bar.txt][Bar file]]
[[./public/foo/baz.txt][Baz file]]
#+END_SRC
are exported with the prefixes ~./assets~ and ~./public~ of the path
removed like this:
#+BEGIN_SRC html
<a href="/foo/bar.txt">Bar file</a>
<a href="/foo/baz.txt">Baz file</a>
#+END_SRC
*** Local file links that raise one-link-broken error
Any file link that doesn't point to a file in ~./assets/~ or ~./public/~
subdirectories raises an ~one-link-broken~ error when we try to
export it with ~one-ox~ org backend
For instance if we try to export using ~one-ox~ org backend the
following link to the file ~foo.txt~ in the directory ~/tmp/~
#+BEGIN_SRC org
[[/tmp/foo.txt]]
#+END_SRC
which is not in ~./public/~ subdirectory nor in ~./assets/~ subdirectory
we will get an error like the following:
#+BEGIN_SRC emacs-lisp
(one-link-broken "/tmp/" "goto-char: 26308")
#+END_SRC
*** Links to images
Links to local files in ~./assets/~ and ~./public/~ directories whom path
matches ~one-ox-link-image-extensions~ regexp are exported with an ~img~
tag.
For instance the following link to an image in ~./assets/img/~ directory
#+BEGIN_SRC org
[[./assets/img/keep-learning.png][Keep Learning]]
#+END_SRC
is exported as follow
#+BEGIN_SRC html
<img href="/img/keep-learning.png" alt="Keep Learning"></a>
#+END_SRC
and rendered like this
[[./assets/img/keep-learning.png][Keep Learning]]
* one-ox | plain-list and item
:PROPERTIES:
:ONE: one-default-doc
:CUSTOM_ID: /docs/one-ox-plain-list/
:END:
Only unordered and ordered lists are supported.
** Unordered lists
:PROPERTIES:
:CUSTOM_ID: /docs/one-ox-plain-list/#unordered-lists
:END:
The following org snippet (unordered list):
#+BEGIN_SRC org
- a thing,
- another thing,
- and the last one.
#+END_SRC
is exported by ~one-ox~ as follow
#+BEGIN_SRC html
<ul>
<li>
<p>a thing,</p>
</li>
<li>
<p>another thing,</p>
</li>
<li>
<p>and the last one.</p>
</li>
</ul>
#+END_SRC
and is rendered like this:
- a thing,
- another thing,
- and the last one.
** Ordered list
The following org snippet (unordered list):
#+BEGIN_SRC org
1. first,
2. second,
3. third.
#+END_SRC
is exported by ~one-ox~ as follow
#+BEGIN_SRC html
<ol>
<li>
<p>a thing,</p>
</li>
<li>
<p>another thing,</p>
</li>
<li>
<p>and the last one.</p>
</li>
</ol>
#+END_SRC
and is rendered like this:
1. first,
2. second,
3. third.