An AkashaCMS project is a melding together of content from four classes of directories. When showing how to create a configuration file, we showed configuring these directories:
assets
is a directory structure containing project assetsdocuments
is a directory structure containing the content for the projectlayouts
contains page layout templates used in the projectpartials
contains template snippets used in the project
We also mentioned that the actual reality is not as simple as this. We set up these directories using the following configuration file stanza:
config
.addAssetsDir('assets')
.addLayoutsDir('layouts')
.addDocumentsDir('documents')
.addPartialsDir('partials');
This means each of these directories are assigned into one category or another. The assets
directory is assigned to the Assets category, the layouts
directory to the Layouts category, the documents
directory to the Documents category, and the partials
directory to the Partials category.
There is an additional directory, the Rendering directory, which was not shown in the configuration file. This directory is where all rendered files land. Since we did not configure a name for this directory, AkashaRender gave it the name out
.
That means we have five classes of directories:
- Assets one or more directories containing project assets
- Layouts one or more directories containing page layout templates
- Documents one or more directories containing the content for the project
- Partials one or more directories containing template snippets
- Rendering is exactly one directory containing the rendered project
These three use cases drove the development of this feature:
- AkashaCMS plugins might provide prebaked Assets, Layouts or Partials.
- One AkashaCMS plugin, or an AkashaCMS project, might want to override a file in an Assets, Layouts or Partials directory.
- An AkashaCMS project might want to assemble content from several sources.
To satisfy these use cases, AkashaRender supports a directory stack for the Assets, Layouts, Documents, and Partials directory categories. By "stack" we mean that there are virtual directories, assets
, documents
, partials
, and layouts
, which are a sort of merging of the actual directories in the corresponding directory categories.
The basic rule is that the directories in each stack are listed in order. When AkashaRender looks for a file in a virtual directory stack, it searches the directory in order, using the first matching file it finds.
But, there is still more involved in fully describing this feature. For the rest of this discussion it will be useful to refer to the Configuration file for this website. It has a very complex directory stack structure pulling together content from multiple sources.
Stacked directories and overridable files
AkashaCMS plugins supply code that extends or modifies AkashaCMS behavior. A typical case is for a plugin to define a custom tag (using Mahabhuta), and for the tag to have a default template in the Partials directory. This means the plugin has its own partials
directory, while the AkashaCMS project also has a partials
directory. Since there are several plugins, each with its own partials
directory, how does AkashaRender decide which directory from which to retrieve the template?
Both the plugin definition, and the AkashaCMS project configuration, use the same method:
config.addPartialsDir('partials');
The addPartialsDir
method adds a directory the partialDirs
array in the configuration object. There are similar methods and arrays for Assets, Documents and Layouts, but lets focus on Partials. The config.addPartialsDir
method will be called multiple times, each one adding a directory name to the partialDirs
array.
It's helpful to think of the partialDirs
array as a stack of directories, one on top of the other. To look for a file, we look for which directory/ies in the stack contains this file, using the top-most file.
You can inspect the stack in an AkashaCMS project by running this command:
$ akasharender partialdirs config.js
[
'.../guide/partials',
'.../guide/node_modules/@akashacms/theme-bootstrap/partials',
'.../guide/node_modules/@akashacms/plugins-base/partials',
'.../guide/node_modules/@akashacms/plugins-breadcrumbs/partials',
'.../guide/node_modules/@akashacms/plugins-tagged-content/partials',
'.../guide/node_modules/@akashacms/plugins-blog-podcast/partials',
'.../guide/node_modules/akasharender/partials'
]
What is actually stored in this array is the full path-name, but I've edited out the prefix of each for the sake of space. The first in this list is in the project directory, the next is in the theme-bootstrap
plugin, and the last is associated with the buitl-in
plugin which is part of AkashaRender.
There are similar commands named akasharender docdirs
, akasharender assetdirs
, and akasharender layoutsdirs
, for the corresponding directory categories.
Consider what AkashaRender does when asked to render a partial template, like ak_toc_group_element.html.njk
. This template is used in constructing a table of contents for a given page, and there is an implementation in both the @akashacms/plugins-base
and @akashacms/theme-bootstrap
plugins. Further, an AkashaCMS project might have its own implementation of this template. What happens is, AkashaRender checks each directory in turn to see if the directory contains a matching file.
That's the key pattern: To search through an array of directories, returning the first matching file.
Stacked Directories module
The actual implementation is not that simple. It used to be implemented as just described, but implementing akashacms watch
changed a lot of things.
Today, there is a module, Stacked Directories (see blog post), that watches the contents of the directory stack. It continuously watches the files looking for changes, so that the watch
command can then cause files to be re-rendered.
There are four instances of the stacked directory module, one for each directory stack (assets
, documents
, partials
, and layouts
). Data collected through the module is then stored in an internal cache.
When AkashaRender is looking for a partial template, it calls the find
method on the cache. It looks in the Partials collection, and one of the parameters is the virtual path ak_toc_group_element.html.njk
.
The query response is guaranteed to have the same result as described above - that it will return the first match in the directory stack. It's just that the implementation is more complicated than a loop looking in one directory followed by another.
The simple answer is that it returns the first file it finds.
Again, the same algorithm is used for Assets, Documents, and Layouts.
Mounting directories
Some projects need to assemble content from multiple locations. For example a website for a software product might have content from the marketing, engineering, or tech-pubs teams. It's best if each team is able to work on its own, and that somehow the content be merged into one website. In this section lets discuss how that's done.
Refer back to the config.js
for the AkashaCMS website, and you'll find a long list of declarations like this:
config.addDocumentsDir({
src: 'node_modules/@akashacms/plugins-base/guide',
dest: 'plugins/base'
})
This is different from the config.addPartialsDir('partials')
case we just discussed. What's going on?
We've supplied an object with two fields, src
and dest
. The src
field refers to a directory within node_modules
and if you consult that directory you'll find index.html.md
and some other content files. The dest
field is interpreted as a location within the Documents directory structure.
Each module in the AkashaCMS project is in its own Github repository, and many of the modules are distributed through the npm repository. We decided that documentation for each plugin should be in a guide
directory. What we're doing is to assemble those guide
directories so they are used on the AkashaCMS website.
Some examples are:
Project | Path | Mounted to |
---|---|---|
@akashacms/plugins-base |
node_modules/@akashacms/plugins-base/guide |
plugins/base |
AkashaCMS Built-In | node_modules/akasharender/built-in-guide |
plugins/built-in |
@akashacms/plugins-authors |
node_modules/@akashacms/plugins-authors/guide |
plugins/authors |
@akashacms/plugins-booknav |
node_modules/@akashacms/plugins-booknav/guide |
plugins/booknav |
@akashacms/plugins-blog-podcast |
node_modules/@akashacms/plugins-blog-podcast/guide |
plugins/blog-podcast |
@akashacms/plugins-breadcrumbs |
node_modules/@akashacms/plugins-breadcrumbs/guide |
plugins/breadcrumbs |
There are a lot more of these.
The resulting rendered output directory contains this:
$ tree -d out/plugins
out/plugins
├── adblock-checker
├── affiliates
├── akasharender-epub
│ └── img
├── authors
├── base
├── blog-podcast
├── booknav
├── breadcrumbs
├── built-in
├── document-viewers
├── embeddables
├── external-links
├── footnotes
├── tagged-content
└── theme-bootstrap
└── img
17 directories
There is no single input directory structure. Instead there are several input directories, each coming from their own Github repository, and each virtually mounted into into the documents
directory.
In the scenario named earlier, the configuration might be:
config
.addDocumentsDir({
src: '/path/to/marketing/content',
dest: 'marketing'
})
.addDocumentsDir({
src: '/path/to/support/content',
dest: 'support'
})
.addDocumentsDir({
src: '/path/to/documentation/content',
dest: 'docs'
})
.addDocumentsDir({
src: '/path/to/blog/content',
dest: 'blog'
})
Each team would be in charge of the content stored in each corresponding directory. How you organize this is up to you. This demonstrates that AkashaCMS can be used to draw together content from any number of sources.
Configuring the Rendering directory
We have one last directory to discuss, out
, which we described as holding the rendered output from the project.
The final result of AkashaCMS is a collection of rendered files, and this directory is where those files land.
The directory can have any name you like. In config.js
simply call this method:
config.setRenderDestination('rendered-output');
Feel free to give it any name you like. If your config.js
does not call this method, the default name is out
.