$ npm install sweeney -g
Getting Started
Sweeney has very few built in assumptions. What it tends to do is let the user create the ideal environmnet to fit their use case.
It all starts with a .sweeney
file.
module.exports = async function() { return { render: (type, content) => { return content; }, source: './', output: './site', include: ['./file.css', './path/to/directory'] }; };
render: This is function that will be called before sweeney applies the template and can be used to convert markdown to html or any other templating engine you wish to use.
source: This is the relative source directory from this file, by default it will be the current working directory.
output: This is the relative output directroy from this file, by default it will be the directory site in the current working directory.
include: This is an array of files or directories that will be copied over that contain static assets that you want to include in your project but do not wish to inject using the include template. (This could include, but not limited to, fonts, images, documents, etc)
That's all! Anything else that is added to this config object will be injected at the top level of every component that is rendered.
These options can be accessible via the CLI options.
Usage: sweeney [options] Commands: new Bootstrap a new project within the current working directory build Build and output static files to site directory serve Generates a http server to serve content from the site directory Options: -h, --help Displays this screen -v, --version Display the current version of sweeney -p, --port [port] Overrides the randomized port for serve -s, --source [path] Overrides the default path which is the current working directory -o, --output [path] Overrides the output path -w, --watch Will watch the directory used to generate site and build when changes are made. If this is used in tandem with serve, it will inject javascript to reload the page when changes were made
The CLI has a number of interoperable commands, such as sweeney serve --watch
Which will not only serve the content, but also watch and build for changes.
When wanting to override an argument such as port, source
or output
simply add it to the end of the CLI invocation like sweeney build --source ./this/is/source --output ./site
It's important to note that files with an .sy
extension will only be interpreted as a renderable by sweeney. This is for the primary purpose of making sure template files do not overwrite or interpret unecessary files.
Instead of learning a template language, anything you can do with javascript you can do with sweeney. So what do I mean?
Looping through an array of objects is pretty simple:
['earth', 'saturn', 'pluto'].forEach(() => {...});Using the same syntax you can create html that you can inject in your templates with the following markup:
<ul> {{ ['earth', 'saturn', 'pluto'].map((planet) => `<li>${planet}</li>`).join('') }} </ul>Or if
planets
was defined in the .sweeney
file or in the page options the template would look like:
<ul> {{ planets.map((planet) => `<li>${planet}</li>`).join('') }} </ul>
Thejoin
is necessary because by default the to string method for a string will add,
by default.
Another powerful thing about sweeney is the idea of page options.
--- { layout: 'posts', collection: 'posts', tags: ['hello', 'world'], title: 'bool', slug: 'testing-this-out' } ---This is just
JSON
! What this example shows is that:
<ul> {{ posts.map((post) => `<li>${post.title}</li>`).join('') }} </ul>
<div> {{ editable options.title string}} </div>
Editing is only contained to string and number for right now more options will be coming soon
When starting the site up in serve
mode using the --watch
option you will see a sidebar in the corner of your screen such like:
Pressing save will issue a rebuild with the file that will trigger the page to refresh with the new content.
If the value is a top level key, meaning it was retrieved from a key that wasn't from options (i.e a key that doesn't originate from that file) it will write the key value to .sweeney
file.
While sweeney might not look like much out of the box, that is purposeful. In order to maintain speed and extensibility, the core static site generator comes with very few prestablished patterns.
Writing a plugin for sweeney is extremely easy and can be done in the .sweeney
file or imported through node.
This is an example of what it would look like to import a plugin via node. in your .sweeney
file.
Please note that sweeney-optomize-image
is not a real node module.
module.exports = async function() { return { ... plugins: { optimizeImage: require('sweeney-optomize-image') }, site: { author: 'Me!' } }; }
Writing a plugin has two components, the parse
method. This function usually makes sure everything is properly formatted and if anything that needs to be exposed to the render method. The second part being the render
method. The render method is where the content is altered or the desired effect takes place.
I am going to walk you through what a simple include plugin might do when faced with a css entity. To see a fully working include function, go checkout the defaultPlugins in the source
const path = require('path'); const Plugin = require('sweeney/lib/plugin'); module.exports.include = class IncludePlugin extends Plugin { static async parse(filePath, content) { // we are looking for the pattern {{-- includes ... }} in the template // this could be any pattern, it does not need to look like the handlebars syntax it could be [[== includes ... ==]] // that is the extensibility of the plugin architecture const reg = /{{-- includes (.+?) --}}/g; if(content.match(reg)) { let found = []; let block; while ((block = reg.exec(content)) != null) { let oldArgument = block[1]; let newArgument = path.resolve(path.dirname(filePath), oldArgument); content = content.replace(`{{-- includes ${oldArgument} --}}`, `{{-- includes ${newArgument} --}}`); found.push(newArgument); } // we pass comtent and found to parseString method (which calls this) to update the content string // we changed and to add the items to the global object to be used for later. return { content, found }; } return false; } static async render(plugins, filePath, content, templates, data, found) { const start = process.hrtime(); let ext = found.substring(found.lastIndexOf('.') + 1, found.length); let name = found.substring(found.lastIndexOf('/') + 1, found.length - 3); let depends = []; if(ext === 'css') { let _content = await readFile(found, 'utf8'); content = content.replace(`{{-- includes ${found} --}}`, ``); // ensure this content gets added to the dependency tree // the depends object is a little more involved then this internally // but for the sake of the plugin, sweeney only expects filePath and time to be passed back depends.push({ filePath: found, time: process.hrtime(start)[1]/1000000 }); } return { // the depends object is something that is maintained internally to sweeney // but templates can add other items to this to show the // user what has effected the render time and when it happened. depends, content }; } }
That is a simple include plugin!
Plugins don't necessarily have to alter the content. In the future plugins will be able to happen during certain life cycle events.
The data passed back in found, will be set to the config['
name of plugin']
for example, if you have a plugin named include
then config['include']
would have the information for the found data during parse
. This can be used in templates or even in other plugins to be able to chain plugins together. Using other data gathered through parse
and altered during render
.
So we have been through the basics of what sweeney
can do, how can we build a site? There are a few ways that one can build a site, simply build off the existing base template by running sweeney new
, which will bootstrap a new site in the current working directory or build one from scratch.
├── .sweeney ├── index.sy
This is the bare minium you need to make a site!
In your .sweeney
file put the following config
module.exports = { source: './', output: './', site: { title: 'Sweeney Example', heading: 'A static site generator that cuts the way you want it to' } };
Wait! We have put the source and output directory to be the same thing!? Yes, we can do this because of the .sy
extension 🎉.
In your index.sy
file put the following code
--- { "collection": "pages" } --- <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>{{ site.title }}</title> <style> * { font-family: Baskerville; } html, body, .container { height: 100%; width: 100%; overflow: hidden; background-color: white; } .container { display: flex; flex-direction: column; justify-content: center; } h3 { text-align: center; width: 100%; color: #3e3e3e; } </style> </head> <body> <div class="container"> <h3>{{ site.heading }}</h3> </div> </body> </html>
Now that all that is left is to run sweeney build
.