make Prettier

This commit is contained in:
AJ ONeal 2020-11-08 20:01:26 -07:00
parent 0909c8e9cc
commit 9131fb9a42
24 changed files with 1649 additions and 1471 deletions

1
.prettierrc.json Normal file
View File

@ -0,0 +1 @@
{}

94
DATA.md
View File

@ -1,12 +1,10 @@
Data # Data
====
Every template gets an object with the exact same structure - whether it's a template or a widget or a page or a post. Every template gets an object with the exact same structure - whether it's a template or a widget or a page or a post.
Here we only document Desirae's default behavior, but there are many objects added for compatibility with Ruhoh that are not documented. Here we only document Desirae's default behavior, but there are many objects added for compatibility with Ruhoh that are not documented.
config.yml vs site.yml ## config.yml vs site.yml
------
site.yml is for anything that changes the content of the site (navigation, title, analytic and ad ids, default author, etc) site.yml is for anything that changes the content of the site (navigation, title, analytic and ad ids, default author, etc)
@ -16,66 +14,60 @@ config.yml is for anything that doesn't change the site (from where to read dire
desi = {} desi = {}
``` ```
desi # desi
====
* `config` - literally `config.yml`, parsed - `config` - literally `config.yml`, parsed
* `site` - literally `site.yml`, parsed - `site` - literally `site.yml`, parsed
* `authors` - literally the authors from `authors/*.yml`, parsed - `authors` - literally the authors from `authors/*.yml`, parsed
* `author` - the primary author of the site - `author` - the primary author of the site
* `env` - urls and paths for this build (be it production, development, staging, etc) - `env` - urls and paths for this build (be it production, development, staging, etc)
* `content` - pre-rendered content (i.e. content rendered into the post layout rendered into the default layout) - `content` - pre-rendered content (i.e. content rendered into the post layout rendered into the default layout)
* `collection` - config related to this collection - `collection` - config related to this collection
* `entity` - the page, post, article, etc that is the focus of the present template process - `entity` - the page, post, article, etc that is the focus of the present template process
* `themes` - all themes - `themes` - all themes
* `theme` - the default theme - `theme` - the default theme
* `layout` - the selected layout for this theme - `layout` - the selected layout for this theme
* `satch` - the selected swatch for this theme - `satch` - the selected swatch for this theme
* `categories` - all categories - `categories` - all categories
* `tags` - all tags - `tags` - all tags
* `styles` - ??? goes into the final template in the head - `styles` - ??? goes into the final template in the head
* `scripts` - ?? that goes into the final template just before the body close - `scripts` - ?? that goes into the final template just before the body close
desi.entity # desi.entity
===========
stuff stuff
* `uuid` - `uuid`
* `title` - `title`
* `disqus_url` - `disqus_url`
* `disqus_identifier` - `disqus_identifier`
more stuff more stuff
* `type` - `post`, `page`, etc - `type` - `post`, `page`, etc
* `authors` - literally the relevant authors from `authors/*.yml`, parsed - `authors` - literally the relevant authors from `authors/*.yml`, parsed
* `author` - the primary author of this entity - `author` - the primary author of this entity
* `theme` - null or a non-default theme - `theme` - null or a non-default theme
* `layout` - null or a non-default layout for this theme - `layout` - null or a non-default layout for this theme
* `swatch` - null or a non-default swatch for this theme - `swatch` - null or a non-default swatch for this theme
* `categories`: [] // *all* categories in all collections - `categories`: [] // _all_ categories in all collections
* `tags`: [] // *all* categories in all collections - `tags`: [] // _all_ categories in all collections
* `production_canonical_url` the PRODUCTION canonical_url for this entity - `production_canonical_url` the PRODUCTION canonical_url for this entity
* `production_url` the PRODUCTION url for this entity - `production_url` the PRODUCTION url for this entity
* `production_path` the PRODUCTION path for this entity - `production_path` the PRODUCTION path for this entity
* `url` the full url in the current environment (might be production, development, etc) - `url` the full url in the current environment (might be production, development, etc)
* `path` the non-host part (i.e. `/compiled_dev/articles/my-first-post.html`) - `path` the non-host part (i.e. `/compiled_dev/articles/my-first-post.html`)
* `previous` the previous entity in this collection - `previous` the previous entity in this collection
* `next` the next entitiy in this collection - `next` the next entitiy in this collection
NOTE: Plugins, widgets, etc SHOULD NOT modify config, site, authors, author, or env. NOTE: Plugins, widgets, etc SHOULD NOT modify config, site, authors, author, or env.
desi.posts # desi.posts
==========
, posts: { collated: desi.collated } , posts: { collated: desi.collated }
desi.config # desi.config
===========
desi.site # desi.site
===========
desi.env # desi.env
===========

View File

@ -2,61 +2,65 @@ This is what an entity looks like:
```yml ```yml
# inherited from File Entity # inherited from File Entity
path : My Posts/My-Old-Name.html path: My Posts/My-Old-Name.html
lastModifiedDate : 2015-07-04T13:56:01Z lastModifiedDate: 2015-07-04T13:56:01Z
createdDate : 2015-07-04T13:56:01Z createdDate: 2015-07-04T13:56:01Z
contents : '...' # whatever the file is contents: "..." # whatever the file is
# inherited from Collection Entity # inherited from Collection Entity
name : My-Old-Name.html name: My-Old-Name.html
relativePath : My Posts relativePath: My Posts
ext : .html ext: .html
collection : posts collection: posts
# inherited from Content Entity # inherited from Content Entity
frontmatter : '---\n...\n---' # frontmatter as a string frontmatter: '---\n...\n---' # frontmatter as a string
yml : {} # frontmatter, parsed yml: {} # frontmatter, parsed
body : 'I think ...' # body, after frontmatter body: "I think ..." # body, after frontmatter
# inherited from Normalized Entity # inherited from Normalized Entity
title : My Title # yml.title | titlize(entity.name) title: My Title # yml.title | titlize(entity.name)
slug : my-title # slugify(title) slug: my-title # slugify(title)
slug_path : my-posts # slugifyPath(relativePath) slug_path: my-posts # slugifyPath(relativePath)
year : 2014 year: 2014
month : 07 month: 07
day : 04 day: 04
hour : 13 hour: 13
twelve_hour : 1 twelve_hour: 1
meridian : pm meridian: pm
minute : 22 minute: 22
categories : ['tech'] categories: ["tech"]
tags : ['http','url','website'] tags:
["http", "url", "website"]
# includes index.html # includes index.html
relative_file : /posts/foo/index.html relative_file:
/posts/foo/index.html
# excludes index.html # excludes index.html
relative_href : /posts/foo/ relative_href:
/posts/foo/
# actual url of this file, even if redirect # actual url of this file, even if redirect
# excludes index.html # excludes index.html
url : http://dev.example.com/posts/foo/ url:
http://dev.example.com/posts/foo/
# the appropriate url, even in a redirect or duplicate # the appropriate url, even in a redirect or duplicate
# excludes index.html # excludes index.html
canonical_url : http://dev.example.com/posts/foo/ canonical_url:
http://dev.example.com/posts/foo/
# production url, even in development (for disqus, etc) # production url, even in development (for disqus, etc)
# excludes index.html # excludes index.html
production_url : http://example.com/posts/foo/ production_url: http://example.com/posts/foo/
``` ```
Note: The option `env.explicitIndexes` turns on `/index.html`. This option is automatically turned on when Dropbox is the host. Note: The option `env.explicitIndexes` turns on `/index.html`. This option is automatically turned on when Dropbox is the host.
TODO ## TODO
----
* path relative from / in the browser - path relative from / in the browser
* path relative from base_path on the file system - path relative from base_path on the file system

View File

@ -1,14 +1,12 @@
Glossary # Glossary
========
Canonical URL ## Canonical URL
--------
base\_url + base\_path + permalink
Base URL base_url + base_path + permalink
----
base\_url is the point of ownership ## Base URL
base_url is the point of ownership
In most cases that would be https://johndoe.com In most cases that would be https://johndoe.com
@ -16,27 +14,25 @@ In some cases that might be https://school.edu/~/johndoe
It does NOT include a trailing / It does NOT include a trailing /
Base Path ## Base Path
-----
base\_path is the blog directory base_path is the blog directory
In most cases that would be / or /blog/ In most cases that would be / or /blog/
It DOES include BOTH a LEADING and TRAILING slash. It DOES include BOTH a LEADING and TRAILING slash.
In the case of https://school.edu/~/johndoe/weblog, the base\_path would be /weblog/. In the case of https://school.edu/~/johndoe/weblog, the base_path would be /weblog/.
Permalink ## Permalink
------
The permalink is the permanent part of the URL, after the path to the blog. The permalink is the permanent part of the URL, after the path to the blog.
For example: For example:
* http://blog.johndoe.com/articles/first-post.html the permalink is articles/first-post.html - http://blog.johndoe.com/articles/first-post.html the permalink is articles/first-post.html
* http://johndoe.com/blog/articles/first-post.html the permalink is still articles/first-post.html - http://johndoe.com/blog/articles/first-post.html the permalink is still articles/first-post.html
* http://school.edu/~/johndoe/blog/articles/first-post.html the permalink is yet still articles/first-post.html - http://school.edu/~/johndoe/blog/articles/first-post.html the permalink is yet still articles/first-post.html
The permalink is ALWAYS RELATIVE (no leading slash) The permalink is ALWAYS RELATIVE (no leading slash)
@ -44,5 +40,5 @@ It is designed so that if you ever move your blog from one domain, point of owne
a very simple one-line redirect can be made to your webserver and all of the posts will end up in the right place a very simple one-line redirect can be made to your webserver and all of the posts will end up in the right place
once again. once again.
base\_url the base_url the
permalink refers to permalink refers to

201
README.md
View File

@ -1,5 +1,4 @@
Did you mean DearDesi? # Did you mean DearDesi?
======================
If you're looking for [DearDesi](http://dear.desi), the DIY blog platform for normal people If you're looking for [DearDesi](http://dear.desi), the DIY blog platform for normal people
you should go to <http://dear.desi>. you should go to <http://dear.desi>.
@ -7,8 +6,7 @@ you should go to <http://dear.desi>.
Desirae (this repo) is the code that programmers to use to make DearDesi better Desirae (this repo) is the code that programmers to use to make DearDesi better
for everyone and to make Desirae-compatible blog platforms. (it's blog-ception!) for everyone and to make Desirae-compatible blog platforms. (it's blog-ception!)
Desirae (v0.9) # Desirae (v0.9)
=======
Desirae is a static webpage compiler written in JavaScript. Desirae is a static webpage compiler written in JavaScript.
@ -19,18 +17,17 @@ It can also run entirely from the commandline (with io.js / node.js).
**Features:** **Features:**
* `JavaScript` - it's so stable it takes 10 years to get new any features. - `JavaScript` - it's so stable it takes 10 years to get new any features.
* Won't break every time you upgrade OS X and reinstall `brew` (*cough* ruby) - Won't break every time you upgrade OS X and reinstall `brew` (_cough_ ruby)
* Decent use of `try { ... } catch(e) ...` and `promise.catch()` - Decent use of `try { ... } catch(e) ...` and `promise.catch()`
* the idea is that it shouldn't blow up at the slightest parse error without telling you which page is to blame (*cough* ruhoh *cough* jekyll)... bless me - the idea is that it shouldn't blow up at the slightest parse error without telling you which page is to blame (_cough_ ruhoh _cough_ jekyll)... bless me
* Browser (optional) - Browser (optional)
* using your front-end templates to build in your front-end? Imagine that! - using your front-end templates to build in your front-end? Imagine that!
* io.js (node.js) (optional) - io.js (node.js) (optional)
* if you'd prefer to go headless, you can. - if you'd prefer to go headless, you can.
* The server is *very* minimal and could easily be implemented in any language (such as ruby or python). - The server is _very_ minimal and could easily be implemented in any language (such as ruby or python).
Installation # Installation
============
```bash ```bash
bower install --save desirae bower install --save desirae
@ -38,8 +35,7 @@ bower install --save desirae
npm install --save desirae npm install --save desirae
``` ```
Why # Why
===
Because I hate ruby. Because I hate ruby.
@ -51,14 +47,13 @@ more (last year I made dozens of gists and 0 blog posts) and I just couldn't get
versions and gems and whatnot... so I gave up and wrote my own (because I needed something versions and gems and whatnot... so I gave up and wrote my own (because I needed something
compatible with ruhoh). compatible with ruhoh).
Usage Overview # Usage Overview
==============
### Before we get started ### Before we get started
(disclaimers) (disclaimers)
**Browser**: The default fs adapter will request the config files from `/api/fs/`. **Browser**: The default fs adapter will request the config files from `/api/fs/`.
**Node**: Remember that desirae is built browser-first, and optimized to reduce the number of round-trips **Node**: Remember that desirae is built browser-first, and optimized to reduce the number of round-trips
(in case someone actually desides to host a Desi service, y'know?), so... the node adapter is built with (in case someone actually desides to host a Desi service, y'know?), so... the node adapter is built with
@ -70,11 +65,10 @@ It's an artifact of this project being born out of the ashes of my
### Getting Started ### Getting Started
First off you need to declare a state object that will be used in every *desirae* action. First off you need to declare a state object that will be used in every _desirae_ action.
```javascript ```javascript
var desi = {} var desi = {};
;
``` ```
After that you'll load any plugins you need. After that you'll load any plugins you need.
@ -156,26 +150,24 @@ Desi.registerRenderer(
); );
``` ```
And then you'll initialize Desirae with an *environment*. And then you'll initialize Desirae with an _environment_.
```javascript ```javascript
Desirae.init( Desirae.init(desi, {
desi url: "https://johndoe.exmaple.com/blog",
, { url: 'https://johndoe.exmaple.com/blog' base_url: "https://johndoe.exmaple.com",
, base_url: 'https://johndoe.exmaple.com' base_path: "/blog",
, base_path: '/blog' compiled_path: "compiled_dev",
, compiled_path: 'compiled_dev'
// default: continue when possible // default: continue when possible
, onError: function (e) { onError: function (e) {
return Promise.reject(e); return Promise.reject(e);
} },
// io.js / node.js only // io.js / node.js only
, working_path: './path/to/blog' working_path: "./path/to/blog",
} }).then(function () {
).then(function () { console.log("Desirae is initialized");
console.log('Desirae is initialized');
}); });
``` ```
@ -198,43 +190,38 @@ Desirae.write(desi, env).then(function () {
}); });
``` ```
### Plugins ### Plugins
You need to start every file with a wrapper that is browser and io.js/node.js compatible You need to start every file with a wrapper that is browser and io.js/node.js compatible
```javascript ```javascript
/*jshint -W054 */ /*jshint -W054 */
;(function (exports) { (function (exports) {
'use strict'; "use strict";
var DesiraeMyModule = {}
;
var DesiraeMyModule = {};
// ... a bunch of code ... // ... a bunch of code ...
DesiraeMyModule.doStuff = doStuff; DesiraeMyModule.doStuff = doStuff;
exports.DesiraeMyModule = DesiraeMyModule.DesiraeMyModule = DesiraeMyModule; exports.DesiraeMyModule = DesiraeMyModule.DesiraeMyModule = DesiraeMyModule;
}('undefined' !== typeof exports && exports || window)); })(("undefined" !== typeof exports && exports) || window);
``` ```
Other than that, just be mindful that your code needs to run in both iojs/node and browser environments Other than that, just be mindful that your code needs to run in both iojs/node and browser environments
so steer away from things that are super iojs/node-ish or super window-ish. so steer away from things that are super iojs/node-ish or super window-ish.
Configuration # Configuration
=============
There are a few configuration files: There are a few configuration files:
* `site.yml` is stuff that might be unique to your site, such as (title, url, adwords id, etc) - `site.yml` is stuff that might be unique to your site, such as (title, url, adwords id, etc)
* `authors/<<your-handle.yml>>` contains information about you (name, handle, facebook, etc) - `authors/<<your-handle.yml>>` contains information about you (name, handle, facebook, etc)
* `config.yml` contains directives that describe *how* the blog should be compiled - more technical stuff. - `config.yml` contains directives that describe _how_ the blog should be compiled - more technical stuff.
If any of these files change, the entire site needs to be retemplated. If any of these files change, the entire site needs to be retemplated.
API # API
===
I'd like to make the plugin system connect-style API for adding plugins or whatever so that, I'd like to make the plugin system connect-style API for adding plugins or whatever so that,
for example, so you could have a custom markdown preprocessor (that handles includes, perhaps) for example, so you could have a custom markdown preprocessor (that handles includes, perhaps)
@ -246,28 +233,26 @@ But here's what I've got so far:
### (html, markdown, jade) ### (html, markdown, jade)
* `Desirae.registerRenderer(ext, fn)` - `Desirae.registerRenderer(ext, fn)`
For example, if you want to add the ability to render from `slim` or `haml` For example, if you want to add the ability to render from `slim` or `haml`
instead of just `markdown` you could find the appropriate instead of just `markdown` you could find the appropriate
JavaScript module (or make requests to an API service that renders them for you) and do this JavaScript module (or make requests to an API service that renders them for you) and do this
```javascript ```javascript
var slim = exports.slimjs || require('slimjs') var slim = exports.slimjs || require("slimjs");
; function render(contentstr /*, desi*/) {
function render(contentstr/*, desi*/) {
return PromiseA.resolve(slim(contentstr)); return PromiseA.resolve(slim(contentstr));
} }
Desirae.registerRenderer('.slim', render); Desirae.registerRenderer(".slim", render);
``` ```
## Data Mapping ## Data Mapping
### (desirae, ruhoh, etc) ### (desirae, ruhoh, etc)
* `Desirae.registerDataMapper(ext, fn)` - `Desirae.registerDataMapper(ext, fn)`
If you want to use a non-desirae theme that uses attributes in a different than If you want to use a non-desirae theme that uses attributes in a different than
how Desirae creates the view object internally how Desirae creates the view object internally
@ -276,18 +261,18 @@ data mapper to accomplish this.
Please try not to modify the original object if you can avoid it. Please try not to modify the original object if you can avoid it.
*TODO: maybe pass in a two-level deep shallow copy ?* _TODO: maybe pass in a two-level deep shallow copy ?_
```javascript ```javascript
Desirae.registerDataMapper('ruhoh@3.0', function (view) { Desirae.registerDataMapper("ruhoh@3.0", function (view) {
return { return {
page: { page: {
name: view.entity.title name: view.entity.title,
} },
, author: { author: {
nickname: view.author.twitter nickname: view.author.twitter,
} },
// ... // ...
}; };
}); });
``` ```
@ -305,54 +290,54 @@ I'd love to work with anyone who is familiar with the Dropbox or similar APIs.
I think it would be awesome to support various means of storage. Perhaps github gists too. I think it would be awesome to support various means of storage. Perhaps github gists too.
# Server
Server
======
Obviously there has to be a server with some sort of storage and retrieval mechanism. Obviously there has to be a server with some sort of storage and retrieval mechanism.
I've implemented a very simple node server using the filesystem. I've implemented a very simple node server using the filesystem.
GET /api/fs/walk ## GET /api/fs/walk
------------
`GET http://local.dear.desi:8080/api/fs/walk?dir=posts&dotfiles=true&extensions=md,markdown,jade,htm,html` `GET http://local.dear.desi:8080/api/fs/walk?dir=posts&dotfiles=true&extensions=md,markdown,jade,htm,html`
* `dir` **must** be supplied. returns a flat list of all files, recursively - `dir` **must** be supplied. returns a flat list of all files, recursively
* `dotfiles` default to `false`. includes dotfiles when `true`. - `dotfiles` default to `false`. includes dotfiles when `true`.
* `extensions` defaults to `null`. inclode **only** the supplied extensions when `true`. - `extensions` defaults to `null`. inclode **only** the supplied extensions when `true`.
```json ```json
[ [
{ "name": "happy-new-year.md" {
, "createdDate": "2015-01-05T18:19:30.000Z" "name": "happy-new-year.md",
, "lastModifiedDate": "2015-01-05T18:19:30.000Z" "createdDate": "2015-01-05T18:19:30.000Z",
, "size": 2121 "lastModifiedDate": "2015-01-05T18:19:30.000Z",
, "relativePath": "posts/2015" "size": 2121,
} "relativePath": "posts/2015"
},
, { "name": "tips-for-the-ages.jade" {
, "createdDate": "2014-06-16T18:19:30.000Z" "name": "tips-for-the-ages.jade",
, "lastModifiedDate": "2014-06-16T18:19:30.000Z" "createdDate": "2014-06-16T18:19:30.000Z",
, "size": 389 "lastModifiedDate": "2014-06-16T18:19:30.000Z",
, "relativePath": "posts" "size": 389,
} "relativePath": "posts"
, { "name": "my-first-post.html" },
, "createdDate": "2013-08-01T22:47:37.000Z" {
, "lastModifiedDate": "2013-08-01T22:47:37.000Z" "name": "my-first-post.html",
, "size": 4118 "createdDate": "2013-08-01T22:47:37.000Z",
, "relativePath": "posts/2013" "lastModifiedDate": "2013-08-01T22:47:37.000Z",
"size": 4118,
"relativePath": "posts/2013"
} }
] ]
``` ```
To retrieve multiple dir listings at once: To retrieve multiple dir listings at once:
* for a few simple dirs without special chars just change `dir` to `dirs` and separate with commas - for a few simple dirs without special chars just change `dir` to `dirs` and separate with commas
`GET http://local.dear.desi:8080/api/fs/walk?dirs=posts/2015,posts/2013&dotfiles=true&extensions=md,markdown,jade,htm,html` `GET http://local.dear.desi:8080/api/fs/walk?dirs=posts/2015,posts/2013&dotfiles=true&extensions=md,markdown,jade,htm,html`
* for many dirs, or dirs with special chars, `POST` an object containing an array of `dirs` with `&_method=GET` appended to the url. - for many dirs, or dirs with special chars, `POST` an object containing an array of `dirs` with `&_method=GET` appended to the url.
``` ```
POST http://local.dear.desi:8080/api/fs/walk?dotfiles=true&extensions=md,markdown,jade,htm,html&_method=GET POST http://local.dear.desi:8080/api/fs/walk?dotfiles=true&extensions=md,markdown,jade,htm,html&_method=GET
@ -367,27 +352,27 @@ POST http://local.dear.desi:8080/api/fs/walk?dotfiles=true&extensions=md,markdow
} }
``` ```
GET /api/fs/files ## GET /api/fs/files
-------------
`GET http://local.dear.desi:8080/api/fs/files?path=posts/happy-new-year.md` `GET http://local.dear.desi:8080/api/fs/files?path=posts/happy-new-year.md`
```json ```json
{ "path": "posts/intro-to-http-with-netcat-node-connect.md" {
, "createdDate": "2013-08-01T22:47:37.000Z" "path": "posts/intro-to-http-with-netcat-node-connect.md",
, "lastModifiedDate": "2013-08-01T22:47:37.000Z" "createdDate": "2013-08-01T22:47:37.000Z",
, "contents": "..." "lastModifiedDate": "2013-08-01T22:47:37.000Z",
, "sha1": "6eae3a5b062c6d0d79f070c26e6d62486b40cb46" "contents": "...",
"sha1": "6eae3a5b062c6d0d79f070c26e6d62486b40cb46"
} }
``` ```
To retrieve multiple files at once: To retrieve multiple files at once:
* for a few simple files without special chars just change `path` to `paths` and separate with commas - for a few simple files without special chars just change `path` to `paths` and separate with commas
`GET http://local.dear.desi:8080/api/fs/files?paths=posts/foo.md,posts/bar.md` `GET http://local.dear.desi:8080/api/fs/files?paths=posts/foo.md,posts/bar.md`
* for many files, or files with special chars, `POST` an object containing an array of `pathss` with `&_method=GET` appended to the url. - for many files, or files with special chars, `POST` an object containing an array of `pathss` with `&_method=GET` appended to the url.
``` ```
POST http://local.dear.desi:8080/api/fs/files?dotfiles=true&extensions=md,markdown,jade,htm,html&_method=GET POST http://local.dear.desi:8080/api/fs/files?dotfiles=true&extensions=md,markdown,jade,htm,html&_method=GET
@ -406,8 +391,7 @@ POST http://local.dear.desi:8080/api/fs/files?dotfiles=true&extensions=md,markdo
] ]
``` ```
POST /api/fs/files ## POST /api/fs/files
------------------
By default this should assume that you intended to write to the compiled directory By default this should assume that you intended to write to the compiled directory
and return an error if you try to write to any other directory, unless `compiled=false` (not yet implemented). and return an error if you try to write to any other directory, unless `compiled=false` (not yet implemented).
@ -452,9 +436,8 @@ The response may include errors of all shapes and sizes.
} }
``` ```
POST /api/fs/copy ## POST /api/fs/copy
------------------
```json ```json
{ files: { "assets/logo.png": "compiled/assets/logo.png" } } { "files": { "assets/logo.png": "compiled/assets/logo.png" } }
``` ```

46
TODO.md
View File

@ -4,7 +4,6 @@ show file path
show prod url show prod url
show dev url show dev url
POST tests POST tests
create a title and delete it (no error) create a title and delete it (no error)
change the format. does the permalink change? (yes) change the format. does the permalink change? (yes)
@ -16,22 +15,17 @@ change the format in the frontmatter permalink. does the format change? (yes)
create a description and delete it (no error) create a description and delete it (no error)
create a description. does the frontmatter change? (yes) create a description. does the frontmatter change? (yes)
protection protection
Don't allow changing the uuid, original_url, or original_date Don't allow changing the uuid, original_url, or original_date
TODO ## TODO
---
check that no other post uses the same permalink check that no other post uses the same permalink
default data-model 'ruhoh@2.2' default data-model 'ruhoh@2.2'
other data-model 'desirae@1.0' other data-model 'desirae@1.0'
Widgets # Widgets
=======
All widgets should export an object with a `create(widgetConf, desiState)` function that returns a promise. All widgets should export an object with a `create(widgetConf, desiState)` function that returns a promise.
@ -42,7 +36,7 @@ widgets:
# stuff like google ad and disqus ids should go in config.yml or data.yml # stuff like google ad and disqus ids should go in config.yml or data.yml
config: config:
foobeep: boop foobeep: boop
handle: handle:
- html - html
- markdown - markdown
@ -52,11 +46,10 @@ widgets:
``` ```
```javascript ```javascript
'use strict'; "use strict";
module.exports.Foogizmo.create = function (foogizmoConf, desiState) { module.exports.Foogizmo.create = function (foogizmoConf, desiState) {
return new Promise(function (resolve) { return new Promise(function (resolve) {
function pager(desiPageState) { function pager(desiPageState) {
// Do processing // Do processing
@ -68,29 +61,28 @@ module.exports.Foogizmo.create = function (foogizmoConf, desiState) {
desiPostState.fooembedinator = function (fooval) { desiPostState.fooembedinator = function (fooval) {
// figure out what type of link fooval is and return iframe html // figure out what type of link fooval is and return iframe html
return '<iframe src="http://embedinator.com/"' + foovalProcessed + '></iframe>' return (
} '<iframe src="http://embedinator.com/"' +
foovalProcessed +
"></iframe>"
);
};
} }
resolve({ foopager: pager, fooposter: poster }); resolve({ foopager: pager, fooposter: poster });
}); });
} };
``` ```
Overlays ## Overlays
--------
For any config a widget uses, it should also check on post.fooconfig and theme.fooconfig to make sure that they don't override the foogizmo.config.fooconfig For any config a widget uses, it should also check on post.fooconfig and theme.fooconfig to make sure that they don't override the foogizmo.config.fooconfig
# Migrating from Ruhoh
Migrating from Ruhoh
====================
There are only a few things in Ruhoh that could only be done in ruby or were otherwise difficult to work around. There are only a few things in Ruhoh that could only be done in ruby or were otherwise difficult to work around.
config.yml ## config.yml
----------
Instead of having special names for some properties (`_root`) Instead of having special names for some properties (`_root`)
and `use` sub attributes for others (`twitter` theme, posts directory), and `use` sub attributes for others (`twitter` theme, posts directory),
@ -117,19 +109,15 @@ widgets [NO CHANGE]
All directories are ignored by default. If you want a directory to be interpreted as a collection of pages you need to specify it in the `collections` hash. All directories are ignored by default. If you want a directory to be interpreted as a collection of pages you need to specify it in the `collections` hash.
data.yml ## data.yml
--------
No changes No changes
## config.ru
config.ru
---------
REMOVED (ruby only) REMOVED (ruby only)
themes layout ## themes layout
-------------
TODO TODO

View File

@ -1,15 +1,10 @@
{ {
"name": "desirae", "name": "desirae",
"version": "0.11.2", "version": "0.11.2",
"authors": [ "authors": ["AJ ONeal <awesome@coolaj86.com>"],
"AJ ONeal <awesome@coolaj86.com>"
],
"description": "A blogging platform in the browser. Wow!", "description": "A blogging platform in the browser. Wow!",
"main": "desirae.js", "main": "desirae.js",
"moduleType": [ "moduleType": ["globals", "node"],
"globals",
"node"
],
"keywords": [ "keywords": [
"desirae", "desirae",
"dear", "dear",
@ -26,13 +21,7 @@
], ],
"license": "Apache2", "license": "Apache2",
"homepage": "http://github.com/DearDesi/desirae", "homepage": "http://github.com/DearDesi/desirae",
"ignore": [ "ignore": ["**/.*", "node_modules", "bower_components", "test", "tests"],
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": { "dependencies": {
"bluebird": "~2.6.2", "bluebird": "~2.6.2",
"escape-string-regexp": "~1.0.2", "escape-string-regexp": "~1.0.2",

1091
desirae.js

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,23 @@
/*jshint -W054 */ /*jshint -W054 */
;(function (exports) { (function (exports) {
'use strict'; "use strict";
var path = exports.path || require('path')
, months
, cores = {}
;
var path = exports.path || require("path"),
months,
cores = {};
months = { months = {
1: 'January' 1: "January",
, 2: 'February' 2: "February",
, 3: 'March' 3: "March",
, 4: 'April' 4: "April",
, 5: 'May' 5: "May",
, 6: 'June' 6: "June",
, 7: 'July' 7: "July",
, 8: 'August' 8: "August",
, 9: 'September' 9: "September",
, 10: 'October' 10: "October",
, 11: 'November' 11: "November",
, 12: 'December' 12: "December",
}; };
function byDate(a, b) { function byDate(a, b) {
@ -67,9 +65,7 @@
} }
function collate(entities, env) { function collate(entities, env) {
var yearsArr = [] var yearsArr = [];
;
entities.forEach(function (f) { entities.forEach(function (f) {
var set; var set;
var yindex = 3000 - f.year; var yindex = 3000 - f.year;
@ -85,10 +81,10 @@
if (!set.months[mindex]) { if (!set.months[mindex]) {
set.months[mindex] = { set.months[mindex] = {
month_name: monthName month_name: monthName,
, month_number: mindex month_number: mindex,
, month: monthName month: monthName,
, pages: [] pages: [],
}; };
} }
set = set.months[mindex]; set = set.months[mindex];
@ -125,4 +121,4 @@
}; };
exports.DesiraeAggregateCore = cores.DesiraeAggregateCore = cores; exports.DesiraeAggregateCore = cores.DesiraeAggregateCore = cores;
}('undefined' !== typeof exports && exports || window)); })(("undefined" !== typeof exports && exports) || window);

View File

@ -1,67 +1,66 @@
/*jshint -W054 */ /*jshint -W054 */
;(function (exports) { (function (exports) {
'use strict'; "use strict";
function create(Desi) { function create(Desi) {
// Chrome, Firefox, and even MSIE11+ all support crypto // Chrome, Firefox, and even MSIE11+ all support crypto
var crypto = window.crypto || window.msCrypto var crypto = window.crypto || window.msCrypto,
, PromiseA = window.Promise PromiseA = window.Promise,
, algos algos;
;
// convenience mappings for common digest algorithms // convenience mappings for common digest algorithms
algos = { algos = {
'sha1': 'SHA-1' sha1: "SHA-1",
, 'sha256': 'SHA-256' sha256: "SHA-256",
, 'sha512': 'SHA-512' sha512: "SHA-512",
}; };
// The function to generate a sha1sum is the same as generating any digest // The function to generate a sha1sum is the same as generating any digest
// but here's a shortcut function anyway // but here's a shortcut function anyway
function sha1sum(str) { function sha1sum(str) {
return hashsum('sha1', str); return hashsum("sha1", str);
} }
// a more general convenience function // a more general convenience function
function hashsum(hash, str) { function hashsum(hash, str) {
// you have to convert from string to array buffer // you have to convert from string to array buffer
var ab var ab,
// you have to represent the algorithm as an object // you have to represent the algorithm as an object
, algo = { name: algos[hash] } algo = { name: algos[hash] };
; if ("string" === typeof str) {
if ('string' === typeof str) {
ab = str2ab(str); ab = str2ab(str);
} else { } else {
ab = str; ab = str;
} }
// All crypto digest methods return a promise // All crypto digest methods return a promise
return crypto.subtle.digest(algo, ab).then(function (digest) { return crypto.subtle
// you have to convert the ArrayBuffer to a DataView and then to a hex String .digest(algo, ab)
return ab2hex(digest); .then(function (digest) {
}).catch(function (e) { // you have to convert the ArrayBuffer to a DataView and then to a hex String
// if you specify an unsupported digest algorithm or non-ArrayBuffer, you'll get an error return ab2hex(digest);
console.error('sha1sum ERROR'); })
console.error(e); .catch(function (e) {
throw e; // if you specify an unsupported digest algorithm or non-ArrayBuffer, you'll get an error
}); console.error("sha1sum ERROR");
console.error(e);
throw e;
});
} }
// convert from arraybuffer to hex // convert from arraybuffer to hex
function ab2hex(ab) { function ab2hex(ab) {
var dv = new DataView(ab) var dv = new DataView(ab),
, i i,
, len len,
, hex = '' hex = "",
, c c;
;
for (i = 0, len = dv.byteLength; i < len; i += 1) { for (i = 0, len = dv.byteLength; i < len; i += 1) {
c = dv.getUint8(i).toString(16); c = dv.getUint8(i).toString(16);
if (c.length < 2) { if (c.length < 2) {
c = '0' + c; c = "0" + c;
} }
hex += c; hex += c;
@ -72,36 +71,31 @@
// convert from string to arraybuffer // convert from string to arraybuffer
function str2ab(stringToEncode, insertBom) { function str2ab(stringToEncode, insertBom) {
stringToEncode = stringToEncode.replace(/\r\n/g,"\n"); stringToEncode = stringToEncode.replace(/\r\n/g, "\n");
var utftext = [] var utftext = [],
, n n,
, c c;
;
if (true === insertBom) { if (true === insertBom) {
utftext[0] = 0xef; utftext[0] = 0xef;
utftext[1] = 0xbb; utftext[1] = 0xbb;
utftext[2] = 0xbf; utftext[2] = 0xbf;
} }
for (n = 0; n < stringToEncode.length; n += 1) { for (n = 0; n < stringToEncode.length; n += 1) {
c = stringToEncode.charCodeAt(n); c = stringToEncode.charCodeAt(n);
if (c < 128) { if (c < 128) {
utftext[utftext.length]= c; utftext[utftext.length] = c;
} else if (c > 127 && c < 2048) {
utftext[utftext.length] = (c >> 6) | 192;
utftext[utftext.length] = (c & 63) | 128;
} else {
utftext[utftext.length] = (c >> 12) | 224;
utftext[utftext.length] = ((c >> 6) & 63) | 128;
utftext[utftext.length] = (c & 63) | 128;
} }
else if((c > 127) && (c < 2048)) {
utftext[utftext.length] = (c >> 6) | 192;
utftext[utftext.length] = (c & 63) | 128;
}
else {
utftext[utftext.length] = (c >> 12) | 224;
utftext[utftext.length] = ((c >> 6) & 63) | 128;
utftext[utftext.length] = (c & 63) | 128;
}
} }
return new Uint8Array(utftext).buffer; return new Uint8Array(utftext).buffer;
} }
@ -112,26 +106,21 @@
// //
// FSAPI // FSAPI
// //
var fsapi var fsapi;
;
function request() { function request() {}
} request.get = function (url /*, query*/) {
request.get = function (url/*, query*/) {
// Return a new promise. // Return a new promise.
return new PromiseA(function(resolve, reject) { return new PromiseA(function (resolve, reject) {
// Do the usual XHR stuff // Do the usual XHR stuff
var req = new XMLHttpRequest() var req = new XMLHttpRequest();
; req.onload = function () {
req.onload = function() {
// This is called even on 404 etc // This is called even on 404 etc
// so check the status // so check the status
if (200 === req.status) { if (200 === req.status) {
// Resolve the promise with the response text // Resolve the promise with the response text
resolve(req.response); resolve(req.response);
} } else {
else {
// Otherwise reject with the status text // Otherwise reject with the status text
// which will hopefully be a meaningful error // which will hopefully be a meaningful error
reject(Error(req.statusText)); reject(Error(req.statusText));
@ -139,30 +128,27 @@
}; };
// Handle network errors // Handle network errors
req.onerror = function() { req.onerror = function () {
reject(Error("Network Error")); reject(Error("Network Error"));
}; };
// Make the request // Make the request
req.open('GET', url); req.open("GET", url);
req.send(); req.send();
}); });
}; };
request.post = function (url/*, query*/, body) { request.post = function (url /*, query*/, body) {
// Return a new promise. // Return a new promise.
return new PromiseA(function(resolve, reject) { return new PromiseA(function (resolve, reject) {
// Do the usual XHR stuff // Do the usual XHR stuff
var req = new XMLHttpRequest() var req = new XMLHttpRequest();
; req.onload = function () {
req.onload = function() {
// This is called even on 404 etc // This is called even on 404 etc
// so check the status // so check the status
if (200 === req.status) { if (200 === req.status) {
// Resolve the promise with the response text // Resolve the promise with the response text
resolve(req.response); resolve(req.response);
} } else {
else {
// Otherwise reject with the status text // Otherwise reject with the status text
// which will hopefully be a meaningful error // which will hopefully be a meaningful error
reject(Error(req.statusText)); reject(Error(req.statusText));
@ -170,14 +156,14 @@
}; };
// Handle network errors // Handle network errors
req.onerror = function() { req.onerror = function () {
reject(Error("Network Error")); reject(Error("Network Error"));
}; };
req.open('POST', url); req.open("POST", url);
req.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
// Make the request // Make the request
if ('string' !== typeof body) { if ("string" !== typeof body) {
body = JSON.stringify(body); body = JSON.stringify(body);
} }
req.send(body); req.send(body);
@ -188,87 +174,93 @@
fsapi.getMeta = function (collections, opts) { fsapi.getMeta = function (collections, opts) {
opts = opts || {}; opts = opts || {};
var extensions = '' var extensions = "",
, dotfiles = '' dotfiles = "",
, contents = '' contents = "",
, sha1sum = '' sha1sum = "";
;
if (Array.isArray(opts.extensions)) { if (Array.isArray(opts.extensions)) {
extensions = '&extensions=' + opts.extensions.join(','); // md,markdown,jade,htm,html extensions = "&extensions=" + opts.extensions.join(","); // md,markdown,jade,htm,html
} }
if (opts.dotfiles) { if (opts.dotfiles) {
dotfiles = '&dotfiles=true'; dotfiles = "&dotfiles=true";
} }
if (opts.contents) { if (opts.contents) {
contents = '&contents=true'; contents = "&contents=true";
} }
if (false === opts.sha1sum) { if (false === opts.sha1sum) {
sha1sum = '&sha1sum=false'; sha1sum = "&sha1sum=false";
} }
return request.post('/api/fs/walk?_method=GET' + dotfiles + extensions + contents + sha1sum, { return request
dirs: collections .post(
}).then(function (resp) { "/api/fs/walk?_method=GET" +
return JSON.parse(resp); dotfiles +
}).catch(function (e) { extensions +
throw e; contents +
}); sha1sum,
{
dirs: collections,
}
)
.then(function (resp) {
return JSON.parse(resp);
})
.catch(function (e) {
throw e;
});
}; };
fsapi.getContents = function (filepaths) { fsapi.getContents = function (filepaths) {
return request.post('/api/fs/files?_method=GET', { return request
paths: filepaths .post("/api/fs/files?_method=GET", {
}).then(function (resp) { paths: filepaths,
return JSON.parse(resp); })
}); .then(function (resp) {
return JSON.parse(resp);
});
}; };
fsapi.getCache = function () { fsapi.getCache = function () {
return request.get('/api/fs/static/cache.json').then(function (resp) { return request
return JSON.parse(resp); .get("/api/fs/static/cache.json")
}).catch(function (/*e*/) { .then(function (resp) {
return {}; return JSON.parse(resp);
}).then(function (obj) { })
return obj; .catch(function (/*e*/) {
}); return {};
})
.then(function (obj) {
return obj;
});
}; };
fsapi.copy = function (files) { fsapi.copy = function (files) {
var body = { files: files }; var body = { files: files };
body = JSON.stringify(body); // this is more or less instant for a few MiB of posts body = JSON.stringify(body); // this is more or less instant for a few MiB of posts
return request.post('/api/fs/copy', body).then(function (resp) { return request.post("/api/fs/copy", body).then(function (resp) {
var response = JSON.parse(resp) var response = JSON.parse(resp);
;
// not accurate for utf8/unicode, but close enough // not accurate for utf8/unicode, but close enough
response.size = body.length; response.size = body.length;
return response; return response;
}); });
}; };
fsapi.putFiles = function (files) { fsapi.putFiles = function (files) {
var body = { files: files } var body = { files: files };
;
files.forEach(function (file) { files.forEach(function (file) {
if (!file.contents || 'string' === typeof file.contents) { if (!file.contents || "string" === typeof file.contents) {
return; return;
} }
if (/\.json$/i.test(file.path)) { if (/\.json$/i.test(file.path)) {
file.contents = JSON.stringify(file.contents); file.contents = JSON.stringify(file.contents);
} } else if (/\.ya?ml$/i.test(file.path)) {
else if (/\.ya?ml$/i.test(file.path)) { file.contents = exports.jsyaml.dump(file.contents);
file.contents = exports.jsyaml.dump(file.contents);
} }
}); });
body = JSON.stringify(body); // this is more or less instant for a few MiB of posts body = JSON.stringify(body); // this is more or less instant for a few MiB of posts
return request.post('/api/fs/files', body).then(function (resp) { return request.post("/api/fs/files", body).then(function (resp) {
var response = JSON.parse(resp) var response = JSON.parse(resp);
;
// not accurate for utf8/unicode, but close enough // not accurate for utf8/unicode, but close enough
response.size = body.length; response.size = body.length;
return response; return response;
@ -281,4 +273,4 @@
} else { } else {
exports.create = create; exports.create = create;
} }
}('undefined' !== typeof exports && exports || window)); })(("undefined" !== typeof exports && exports) || window);

View File

@ -1,7 +1,9 @@
'use strict'; "use strict";
module.exports.convert = function () { module.exports.convert = function () {
console.error("I haven't implemented a ruhoh -> nuhoh converter yet, but it's not very hard to do."); console.error(
"I haven't implemented a ruhoh -> nuhoh converter yet, but it's not very hard to do."
);
console.error("see https://github.com/coolaj86/nuhoh/tree/master/MIGRATE.md"); console.error("see https://github.com/coolaj86/nuhoh/tree/master/MIGRATE.md");
throw new Error('Not Implemented.'); throw new Error("Not Implemented.");
}; };

View File

@ -1,6 +1,6 @@
/*jshint -W054 */ /*jshint -W054 */
;(function (exports) { (function (exports) {
'use strict'; "use strict";
function desiMap(obj) { function desiMap(obj) {
obj.desi = obj; obj.desi = obj;
@ -8,4 +8,4 @@
} }
exports.DesiraeDatamapCore = desiMap.DesiraeDatamapCore = desiMap; exports.DesiraeDatamapCore = desiMap.DesiraeDatamapCore = desiMap;
}('undefined' !== typeof exports && exports || window)); })(("undefined" !== typeof exports && exports) || window);

View File

@ -1,19 +1,17 @@
/*jshint -W054 */ /*jshint -W054 */
;(function (exports) { (function (exports) {
'use strict'; "use strict";
function create(Desi) { function create(Desi) {
Desi.YAML = {}; Desi.YAML = {};
Desi.YAML.parse = (exports.jsyaml || require('js-yaml')).load; Desi.YAML.parse = (exports.jsyaml || require("js-yaml")).load;
Desi.YAML.stringify = (exports.jsyaml || require('js-yaml')).dump; Desi.YAML.stringify = (exports.jsyaml || require("js-yaml")).dump;
function readFrontMatter(text) { function readFrontMatter(text) {
var lines var lines,
, line line,
, padIndent = '' padIndent = "",
, ymllines = [] ymllines = [];
;
lines = text.split(/\n/); lines = text.split(/\n/);
line = lines.shift(); line = lines.shift();
@ -25,14 +23,14 @@
// that start without indentation, so // that start without indentation, so
// we can add it if this is the case // we can add it if this is the case
if (lines[0] && lines[0].match(/^\S/)) { if (lines[0] && lines[0].match(/^\S/)) {
padIndent = ''; padIndent = "";
} }
while (true) { while (true) {
line = lines.shift(); line = lines.shift();
// premature end-of-file (unsupported yaml) // premature end-of-file (unsupported yaml)
if (!line && '' !== line) { if (!line && "" !== line) {
ymllines = []; ymllines = [];
break; break;
} }
@ -44,24 +42,21 @@
if (line) { if (line) {
// supported yaml // supported yaml
ymllines.push(padIndent + line); ymllines.push(padIndent + line);
} }
} }
// XXX can't be sorted because arrays get messed up // XXX can't be sorted because arrays get messed up
//ymllines.sort(); //ymllines.sort();
if (ymllines) { if (ymllines) {
return '---\n' + ymllines.join('\n'); return "---\n" + ymllines.join("\n");
} }
return; return;
} }
function separateText(text, fm) { function separateText(text, fm) {
var len var len, yml;
, yml
;
yml = readFrontMatter(fm); yml = readFrontMatter(fm);
// strip frontmatter from text, if any // strip frontmatter from text, if any
@ -72,27 +67,26 @@
len = 0; len = 0;
} }
return text.split(/\n/).slice(len).join('\n'); return text.split(/\n/).slice(len).join("\n");
} }
function parseText(text) { function parseText(text) {
var fm = readFrontMatter(text) var fm = readFrontMatter(text),
, body = fm && separateText(text, fm) body = fm && separateText(text, fm),
, yml yml;
;
if (fm) { if (fm) {
try { try {
yml = Desi.YAML.parse(fm); yml = Desi.YAML.parse(fm);
} catch(e) { } catch (e) {
// //
} }
} }
return { return {
yml: yml yml: yml,
, frontmatter: fm frontmatter: fm,
, body: body body: body,
}; };
} }
@ -112,4 +106,4 @@
} else { } else {
exports.create = create; exports.create = create;
} }
}('undefined' !== typeof exports && exports || window)); })(("undefined" !== typeof exports && exports) || window);

View File

@ -1,51 +1,47 @@
'use strict'; "use strict";
var PromiseA = require('bluebird').Promise
, fs = PromiseA.promisifyAll(require('fs'))
;
var PromiseA = require("bluebird").Promise,
fs = PromiseA.promisifyAll(require("fs"));
function create(Desi, options) { function create(Desi, options) {
var fsapi = Desi.fsapi var fsapi = Desi.fsapi;
;
options.blogdir = options.blogdir || options.working_path; options.blogdir = options.blogdir || options.working_path;
fsapi.getMeta = function (dirnames, opts) { fsapi.getMeta = function (dirnames, opts) {
opts = opts || {}; opts = opts || {};
var extensions = '' var extensions = "",
, dotfiles = '' dotfiles = "",
, contents = '' contents = "",
, sha1sum = '' sha1sum = "";
;
if (Array.isArray(opts.extensions)) { if (Array.isArray(opts.extensions)) {
extensions = '&extensions=' + opts.extensions.join(','); // md,markdown,jade,htm,html extensions = "&extensions=" + opts.extensions.join(","); // md,markdown,jade,htm,html
} }
if (opts.dotfiles) { if (opts.dotfiles) {
dotfiles = '&dotfiles=true'; dotfiles = "&dotfiles=true";
} }
if (opts.contents) { if (opts.contents) {
contents = '&contents=true'; contents = "&contents=true";
} }
if (false === opts.sha1sum) { if (false === opts.sha1sum) {
sha1sum = '&sha1sum=false'; sha1sum = "&sha1sum=false";
} }
return fsapi.walk.walkDirs(options.blogdir, dirnames, opts); return fsapi.walk.walkDirs(options.blogdir, dirnames, opts);
}; };
fsapi.getContents = function (filepaths) { fsapi.getContents = function (filepaths) {
return fsapi.getfs(options.blogdir, filepaths); return fsapi.getfs(options.blogdir, filepaths);
}; };
fsapi.getCache = function () { fsapi.getCache = function () {
return fs.readFileAsync(options.blogdir, '/cache.json').catch(function (/*e*/) { return fs
return {}; .readFileAsync(options.blogdir, "/cache.json")
}).then(function (obj) { .catch(function (/*e*/) {
return obj; return {};
}); })
.then(function (obj) {
return obj;
});
}; };
fsapi.copy = function (files) { fsapi.copy = function (files) {
@ -55,14 +51,13 @@ function create(Desi, options) {
fsapi.putFiles = function (files, opts) { fsapi.putFiles = function (files, opts) {
files.forEach(function (file) { files.forEach(function (file) {
if (!file.contents || 'string' === typeof file.contents) { if (!file.contents || "string" === typeof file.contents) {
return; return;
} }
if (/\.json$/i.test(file.path)) { if (/\.json$/i.test(file.path)) {
file.contents = JSON.stringify(file.contents); file.contents = JSON.stringify(file.contents);
} } else if (/\.ya?ml$/i.test(file.path)) {
else if (/\.ya?ml$/i.test(file.path)) { file.contents = Desi.YAML.stringify(file.contents);
file.contents = Desi.YAML.stringify(file.contents);
} }
}); });

View File

@ -1,18 +1,18 @@
'use strict'; "use strict";
var PromiseA = require('bluebird').Promise
, fs = PromiseA.promisifyAll(require('fs'))
, forEachAsync = require('foreachasync').forEachAsync
, path = require('path')
, walk = require('walk')
, escapeRegExp = require('escape-string-regexp')
, safeResolve = require('../utils').safeResolve
, sha1sum = function (str) { return require('secret-utils').hashsum('sha1', str); }
, mkdirp = PromiseA.promisify(require('mkdirp'))
, fsExtra = PromiseA.promisifyAll(require('fs.extra'))
//, tmpdir = require('os').tmpdir()
;
var PromiseA = require("bluebird").Promise,
fs = PromiseA.promisifyAll(require("fs")),
forEachAsync = require("foreachasync").forEachAsync,
path = require("path"),
walk = require("walk"),
escapeRegExp = require("escape-string-regexp"),
safeResolve = require("../utils").safeResolve,
sha1sum = function (str) {
return require("secret-utils").hashsum("sha1", str);
},
mkdirp = PromiseA.promisify(require("mkdirp")),
fsExtra = PromiseA.promisifyAll(require("fs.extra"));
//, tmpdir = require('os').tmpdir()
function strip(prefix, pathname) { function strip(prefix, pathname) {
return pathname.substr(prefix.length + 1); return pathname.substr(prefix.length + 1);
} }
@ -23,24 +23,24 @@ function walkDir(parent, sub, opts) {
opts.sha1sum = true; opts.sha1sum = true;
} }
var prefix = path.resolve(parent) var prefix = path.resolve(parent),
, trueRoot = path.resolve(prefix, sub) trueRoot = path.resolve(prefix, sub),
, files = [] files = [];
;
function filter(name) { function filter(name) {
if (!name) { if (!name) {
return false; return false;
} }
if (!opts.dotfiles && ('.' === name[0])) { if (!opts.dotfiles && "." === name[0]) {
return false; return false;
} }
if (opts.extensions && opts.extensions.length) { if (opts.extensions && opts.extensions.length) {
if (!opts.extensions.some(function (ext) { if (
return new RegExp('\\.' + escapeRegExp(ext) + '$').test(name); !opts.extensions.some(function (ext) {
})) { return new RegExp("\\." + escapeRegExp(ext) + "$").test(name);
})
) {
return false; return false;
} }
} }
@ -49,10 +49,8 @@ function walkDir(parent, sub, opts) {
} }
return new PromiseA(function (resolve) { return new PromiseA(function (resolve) {
var walker = walk.walk(trueRoot) var walker = walk.walk(trueRoot);
; walker.on("nodeError", function (filepath, stat, next) {
walker.on('nodeError', function (filepath, stat, next) {
//stats.forEach(function (stat) { //stats.forEach(function (stat) {
if (!filter(stat.name)) { if (!filter(stat.name)) {
return; return;
@ -60,40 +58,37 @@ function walkDir(parent, sub, opts) {
stat.error.path = path.join(strip(prefix, filepath), stat.name); stat.error.path = path.join(strip(prefix, filepath), stat.name);
files.push({ files.push({
name: stat.name name: stat.name,
, relativePath: strip(prefix, filepath) relativePath: strip(prefix, filepath),
, path: path.join(strip(prefix, filepath), stat.name) path: path.join(strip(prefix, filepath), stat.name),
, type: undefined type: undefined,
, error: stat.error error: stat.error,
}); });
//}); //});
next(); next();
}); });
walker.on('files', function (root, stats, next) { walker.on("files", function (root, stats, next) {
var dirname = strip(prefix, root) var dirname = strip(prefix, root);
;
function eachFile(stat) { function eachFile(stat) {
var file var file;
;
if (!filter(stat.name)) { if (!filter(stat.name)) {
return PromiseA.resolve(); return PromiseA.resolve();
} }
file = { file = {
name: stat.name name: stat.name,
, relativePath: dirname relativePath: dirname,
, path: path.join(dirname, stat.name) path: path.join(dirname, stat.name),
, createdDate: (stat.birthtime||stat.ctime).toISOString() createdDate: (stat.birthtime || stat.ctime).toISOString(),
, lastModifiedDate: stat.mtime.toISOString() lastModifiedDate: stat.mtime.toISOString(),
, size: stat.size size: stat.size,
, type: undefined // TODO include mimetype type: undefined, // TODO include mimetype
}; };
files.push(file); files.push(file);
@ -102,17 +97,17 @@ function walkDir(parent, sub, opts) {
} }
// TODO stream sha1 (for assets) // TODO stream sha1 (for assets)
return fs.readFileAsync(path.join(root, stat.name), null).then(function (buffer) { return fs
var contents = buffer.toString('utf8') .readFileAsync(path.join(root, stat.name), null)
; .then(function (buffer) {
var contents = buffer.toString("utf8");
file.sha1 = sha1sum(contents);
file.type = undefined;
file.sha1 = sha1sum(contents); if (opts.contents) {
file.type = undefined; file.contents = contents;
}
if (opts.contents) { });
file.contents = contents;
}
});
} }
if (!opts.contents) { if (!opts.contents) {
@ -125,7 +120,7 @@ function walkDir(parent, sub, opts) {
} }
}); });
walker.on('end', function () { walker.on("end", function () {
resolve(files); resolve(files);
}); });
}); });
@ -134,9 +129,7 @@ function walkDir(parent, sub, opts) {
function walkDirs(parent, subs, opts) { function walkDirs(parent, subs, opts) {
opts = opts || {}; opts = opts || {};
var collections = {} var collections = {};
;
return forEachAsync(subs, function (sub) { return forEachAsync(subs, function (sub) {
return walkDir(parent, sub, opts).then(function (results) { return walkDir(parent, sub, opts).then(function (results) {
collections[sub] = results; collections[sub] = results;
@ -146,59 +139,53 @@ function walkDirs(parent, subs, opts) {
}); });
} }
function getfs(blogdir, filepaths) { function getfs(blogdir, filepaths) {
var files = [] var files = [];
;
return forEachAsync(filepaths, function (filepath) { return forEachAsync(filepaths, function (filepath) {
var pathname = safeResolve(blogdir, filepath) var pathname = safeResolve(blogdir, filepath);
; return fs
.lstatAsync(pathname)
.then(function (stat) {
return fs.readFileAsync(pathname, null).then(function (buffer) {
files.push({
name: path.basename(pathname),
relativePath: path.dirname(filepath),
path: filepath,
return fs.lstatAsync(pathname).then(function (stat) { createdDate: (stat.birthtime || stat.ctime).toISOString(),
return fs.readFileAsync(pathname, null).then(function (buffer) { lastModifiedDate: stat.mtime.toISOString(),
files.push({ contents: buffer.toString("utf8"),
name: path.basename(pathname) size: buffer.length,
, relativePath: path.dirname(filepath) sha1: sha1sum(buffer),
, path: filepath type: undefined,
});
, createdDate: (stat.birthtime||stat.ctime).toISOString()
, lastModifiedDate: stat.mtime.toISOString()
, contents: buffer.toString('utf8')
, size: buffer.length
, sha1: sha1sum(buffer)
, type: undefined
}); });
})
.catch(function (e) {
files.push({ path: filepath, error: e.message });
}); });
}).catch(function (e) {
files.push({ path: filepath, error: e.message });
});
}).then(function () { }).then(function () {
return files; return files;
}); });
} }
function makeAllDirs(dirpaths) { function makeAllDirs(dirpaths) {
var errors = [] var errors = [];
;
return forEachAsync(dirpaths, function (pathname) { return forEachAsync(dirpaths, function (pathname) {
return mkdirp(pathname).catch(function (e) { return mkdirp(pathname).catch(function (e) {
// TODO exclude attempting to write files to this dir? // TODO exclude attempting to write files to this dir?
errors.push({ errors.push({
type: 'directory' type: "directory",
, directory: pathname directory: pathname,
, message: e.message message: e.message,
, code: e.code code: e.code,
, errno: e.errno errno: e.errno,
, status: e.status status: e.status,
, syscall: e.syscall syscall: e.syscall,
}); });
}); });
}).then(function () { }).then(function () {
return errors; return errors;
@ -207,156 +194,164 @@ function makeAllDirs(dirpaths) {
function copyfs(blogdir, files) { function copyfs(blogdir, files) {
// TODO switch format to { source: ..., dest: ..., opts: ... } ? // TODO switch format to { source: ..., dest: ..., opts: ... } ?
var results = { errors: [] } var results = { errors: [] },
, dirpaths = {} dirpaths = {},
, sources = Object.keys(files) sources = Object.keys(files);
;
return forEachAsync(sources, function (source) { return forEachAsync(sources, function (source) {
/* /*
var nsource = safeResolve(blogdir, source) var nsource = safeResolve(blogdir, source)
; ;
*/ */
var dest = safeResolve(blogdir, files[source]) var dest = safeResolve(blogdir, files[source]),
, pathname = path.dirname(dest) pathname = path.dirname(dest);
//, filename = path.basename(dest) //, filename = path.basename(dest)
;
dirpaths[pathname] = true; dirpaths[pathname] = true;
return PromiseA.resolve(); return PromiseA.resolve();
}).then(function () { })
// TODO is it better to do this lazy-like or as a batch? .then(function () {
// I figure as batch when there may be hundreds of files, // TODO is it better to do this lazy-like or as a batch?
// likely within 2 or 3 directories // I figure as batch when there may be hundreds of files,
return makeAllDirs(Object.keys(dirpaths)).then(function (errors) { // likely within 2 or 3 directories
errors.forEach(function (e) { return makeAllDirs(Object.keys(dirpaths)).then(function (errors) {
results.errors.push(e); errors.forEach(function (e) {
}); results.errors.push(e);
});
}).then(function () {
// TODO allow delete?
return forEachAsync(sources, function (source) {
return fsExtra.copyAsync(
safeResolve(blogdir, source)
, safeResolve(blogdir, files[source])
, { replace: true }
).catch(function (e) {
results.errors.push({
type: 'file'
, source: source
, destination: files[source]
, message: e.message
, code: e.code
, errno: e.errno
, status: e.status
, syscall: e.syscall
}); });
}); });
})
.then(function () {
// TODO allow delete?
return forEachAsync(sources, function (source) {
return fsExtra
.copyAsync(
safeResolve(blogdir, source),
safeResolve(blogdir, files[source]),
{ replace: true }
)
.catch(function (e) {
results.errors.push({
type: "file",
source: source,
destination: files[source],
message: e.message,
code: e.code,
errno: e.errno,
status: e.status,
syscall: e.syscall,
});
});
});
})
.catch(function (e) {
results.error = {
message: e.message,
code: e.code,
errno: e.errno,
status: e.status,
syscall: e.syscall,
};
})
.then(function () {
return results;
}); });
}).catch(function (e) {
results.error = {
message: e.message
, code: e.code
, errno: e.errno
, status: e.status
, syscall: e.syscall
};
}).then(function () {
return results;
});
} }
function putfs(blogdir, files, options) { function putfs(blogdir, files, options) {
options = options || {}; options = options || {};
var putfsResults = { errors: [] } var putfsResults = { errors: [] },
, dirpaths = {} dirpaths = {};
;
return forEachAsync(files, function (file) { return forEachAsync(files, function (file) {
var filepath = safeResolve(blogdir, file.path || path.join(file.relativePath, file.name)) var filepath = safeResolve(
, pathname = path.dirname(filepath) blogdir,
, filename = file.name || path.basename(filepath) file.path || path.join(file.relativePath, file.name)
; ),
pathname = path.dirname(filepath),
filename = file.name || path.basename(filepath);
file.realPath = filepath; file.realPath = filepath;
file.name = filename; file.name = filename;
dirpaths[pathname] = true; dirpaths[pathname] = true;
return PromiseA.resolve(); return PromiseA.resolve();
}).then(function () { })
// TODO is it better to do this lazy-like or as a batch? .then(function () {
// I figure as batch when there may be hundreds of files, // TODO is it better to do this lazy-like or as a batch?
// likely within 2 or 3 directories // I figure as batch when there may be hundreds of files,
return forEachAsync(Object.keys(dirpaths), function (pathname) { // likely within 2 or 3 directories
return mkdirp(pathname).catch(function (e) { return forEachAsync(Object.keys(dirpaths), function (pathname) {
// TODO exclude attempting to write files to this dir? return mkdirp(pathname).catch(function (e) {
putfsResults.errors.push({ // TODO exclude attempting to write files to this dir?
type: 'directory' putfsResults.errors.push({
type: "directory",
, directory: pathname directory: pathname,
, message: e.message message: e.message,
, code: e.code code: e.code,
, errno: e.errno errno: e.errno,
, status: e.status status: e.status,
, syscall: e.syscall syscall: e.syscall,
}); });
});
});
}).then(function () {
// TODO sort deletes last
return forEachAsync(files, function (file) {
// TODO use lastModifiedDate as per client request?
// TODO compare sha1 sums for integrity
// NOTE existsAsync is backwards
return fs.existsAsync(file.realPath).then(function () {
return fs.writeFileAsync(file.realPath, file.contents, 'utf8');
}).catch(function (/*exists*/) {
if (file.delete || !file.contents) {
return fs.unlinkAsync(file.realPath);
}
if (false === options.replace || false === options.overwrite) {
throw new Error('EEXIST: the file already exists');
}
return fs.writeFileAsync(file.realPath, file.contents, 'utf8');
}).catch(function (e) {
putfsResults.errors.push({
type: 'file'
, file: file.realPath
, delete: !file.contents
, path: file.path
, relativePath: file.relativePath
, name: file.name
, message: e.message
, code: e.code
, errno: e.errno
, status: e.status
, syscall: e.syscall
}); });
}); });
})
.then(function () {
// TODO sort deletes last
return forEachAsync(files, function (file) {
// TODO use lastModifiedDate as per client request?
// TODO compare sha1 sums for integrity
// NOTE existsAsync is backwards
return fs
.existsAsync(file.realPath)
.then(function () {
return fs.writeFileAsync(file.realPath, file.contents, "utf8");
})
.catch(function (/*exists*/) {
if (file.delete || !file.contents) {
return fs.unlinkAsync(file.realPath);
}
if (false === options.replace || false === options.overwrite) {
throw new Error("EEXIST: the file already exists");
}
return fs.writeFileAsync(file.realPath, file.contents, "utf8");
})
.catch(function (e) {
putfsResults.errors.push({
type: "file",
file: file.realPath,
delete: !file.contents,
path: file.path,
relativePath: file.relativePath,
name: file.name,
message: e.message,
code: e.code,
errno: e.errno,
status: e.status,
syscall: e.syscall,
});
});
});
})
.catch(function (e) {
putfsResults.error = {
message: e.message,
code: e.code,
errno: e.errno,
status: e.status,
syscall: e.syscall,
};
})
.then(function () {
return putfsResults;
}); });
}).catch(function (e) {
putfsResults.error = {
message: e.message
, code: e.code
, errno: e.errno
, status: e.status
, syscall: e.syscall
};
}).then(function () {
return putfsResults;
});
} }
/* /*
walkDirs('blog', ['posts'], { contents: false }).then(function (stats) { walkDirs('blog', ['posts'], { contents: false }).then(function (stats) {

View File

@ -1,5 +1,5 @@
'use strict'; "use strict";
exports.fsapi = require('./fsapi'); exports.fsapi = require("./fsapi");
exports.sha1sum = require('./sha1sum').sha1sum; exports.sha1sum = require("./sha1sum").sha1sum;
exports.realFsapi = require('./fsapi-real'); exports.realFsapi = require("./fsapi-real");

View File

@ -1,9 +1,7 @@
'use strict'; "use strict";
var PromiseA = require('bluebird').Promise
, secretutils = require('secret-utils')
;
var PromiseA = require("bluebird").Promise,
secretutils = require("secret-utils");
module.exports.sha1sum = function (str) { module.exports.sha1sum = function (str) {
return PromiseA.resolve( secretutils.hashsum('sha1', str) ); return PromiseA.resolve(secretutils.hashsum("sha1", str));
}; };

View File

@ -1,38 +1,35 @@
/*jshint -W054 */ /*jshint -W054 */
;(function (exports) { (function (exports) {
'use strict'; "use strict";
var PromiseA = exports.Promise || require('bluebird').Promise
;
function renderMd(contentstr/*, desi*/) {
var markitdown = (exports.markdownit || require('markdown-it'))({ html: true, linkify: true })
;
var PromiseA = exports.Promise || require("bluebird").Promise;
function renderMd(contentstr /*, desi*/) {
var markitdown = (exports.markdownit || require("markdown-it"))({
html: true,
linkify: true,
});
return PromiseA.resolve( return PromiseA.resolve(
markitdown.render(contentstr) markitdown.render(contentstr)
//.replace('&quot;', '"') //.replace('&quot;', '"')
//.replace('&#39;', "'") //.replace('&#39;', "'")
//.replace('&#x2F;', '/') //.replace('&#x2F;', '/')
); );
} }
function renderNoop(contentstr/*, desi*/) { function renderNoop(contentstr /*, desi*/) {
// hmmm... that was easy // hmmm... that was easy
return PromiseA.resolve(contentstr); return PromiseA.resolve(contentstr);
} }
function renderJade(contentstr, desi, options) { function renderJade(contentstr, desi, options) {
options = options || {}; options = options || {};
if (!('pretty' in options)) { if (!("pretty" in options)) {
options.pretty = true; options.pretty = true;
} }
var jade = (exports.jade || require('jade')) var jade = exports.jade || require("jade"),
, fn = jade.compile(contentstr, options) fn = jade.compile(contentstr, options),
, html = fn(desi) html = fn(desi);
;
return PromiseA.resolve(html); return PromiseA.resolve(html);
} }
@ -41,4 +38,4 @@
exports.DesiraeRenderCss = renderNoop.DesiraeRenderCss = renderNoop; exports.DesiraeRenderCss = renderNoop.DesiraeRenderCss = renderNoop;
exports.DesiraeRenderJs = renderNoop.DesiraeRenderJs = renderNoop; exports.DesiraeRenderJs = renderNoop.DesiraeRenderJs = renderNoop;
exports.DesiraeRenderJade = renderJade.DesiraeRenderJade = renderJade; exports.DesiraeRenderJade = renderJade.DesiraeRenderJade = renderJade;
}('undefined' !== typeof exports && exports || window)); })(("undefined" !== typeof exports && exports) || window);

View File

@ -1,143 +1,153 @@
/*jshint -W054 */ /*jshint -W054 */
;(function (exports) { (function (exports) {
'use strict'; "use strict";
var cores = {}
, Desi = exports.Desirae || require('desirae').Desirae
, path = exports.path || require('path')
;
var cores = {},
Desi = exports.Desirae || require("desirae").Desirae,
path = exports.path || require("path");
cores.lint = function (desi, env, collection, entity) { cores.lint = function (desi, env, collection, entity) {
// TODO splice // TODO splice
//desi.content.collections = desi.content.collections.filter(function (entity) { //desi.content.collections = desi.content.collections.filter(function (entity) {
// TODO throw for any files that don't have a registered renderer // TODO throw for any files that don't have a registered renderer
if (!entity.yml) { if (!entity.yml) {
if (!desi.config.empty_frontmatter) { if (!desi.config.empty_frontmatter) {
throw new Error("no frontmatter for " + (entity.path || entity.name) + "." throw new Error(
+ "Set `config.yml.empty_frontmatter: include|skip` to ignore this error." "no frontmatter for " +
); (entity.path || entity.name) +
} "." +
"Set `config.yml.empty_frontmatter: include|skip` to ignore this error."
if ('include' === desi.config.empty_frontmatter) { );
entity.yml = {};
}
else if ('skip' === desi.config.empty_frontmatter) {
return false;
}
else {
throw new Error('unrecognize option ' + desi.config.empty_frontmatter +
' for `config.yml.empty_frontmatter: include|skip`.');
}
} }
if (!entity.body || !entity.body.trim()) { if ("include" === desi.config.empty_frontmatter) {
if (!desi.config.empty_body) { entity.yml = {};
throw new Error('empty content file ' + (entity.path || entity.name) } else if ("skip" === desi.config.empty_frontmatter) {
+ '. Set `config.yml.empty_body: include|skip` to ignore this error.' return false;
); } else {
} throw new Error(
"unrecognize option " +
desi.config.empty_frontmatter +
" for `config.yml.empty_frontmatter: include|skip`."
);
}
}
if ('include' === desi.config.empty_body) { if (!entity.body || !entity.body.trim()) {
entity.body = ''; if (!desi.config.empty_body) {
} throw new Error(
else if ('skip' === desi.config.empty_body) { "empty content file " +
return false; (entity.path || entity.name) +
} ". Set `config.yml.empty_body: include|skip` to ignore this error."
else { );
throw new Error('unrecognize option ' + desi.config.empty_frontmatter +
' for `config.yml.empty_body: include|skip`.');
}
} }
return true; if ("include" === desi.config.empty_body) {
entity.body = "";
} else if ("skip" === desi.config.empty_body) {
return false;
} else {
throw new Error(
"unrecognize option " +
desi.config.empty_frontmatter +
" for `config.yml.empty_body: include|skip`."
);
}
}
return true;
//}); //});
}; };
cores.root = function (desi, env, collection, entity) { cores.root = function (desi, env, collection, entity) {
entity.yml = entity.yml || {}; entity.yml = entity.yml || {};
entity.layout = entity.yml.layout || '__page__'; entity.layout = entity.yml.layout || "__page__";
// _root is not subject to the same permalink rules as collections, // _root is not subject to the same permalink rules as collections,
// so we just go ahead and define that here // so we just go ahead and define that here
if (/^index\.\w+$/.test(entity.path)) { if (/^index\.\w+$/.test(entity.path)) {
entity.permalink = '/'; entity.permalink = "/";
} else { } else {
entity.permalink = entity.yml.permalink || entity.path.replace(/\.\w+$/, '/'); entity.permalink =
entity.yml.permalink || entity.path.replace(/\.\w+$/, "/");
entity.redirects = entity.redirects || []; entity.redirects = entity.redirects || [];
entity.redirects.push(entity.permalink.replace(/\/$/, '/index.html')); entity.redirects.push(entity.permalink.replace(/\/$/, "/index.html"));
} }
}; };
cores.normalize = function (desi, env, collection, entity) { cores.normalize = function (desi, env, collection, entity) {
entity.title = entity.yml.title || Desi.firstCap(entity.name.replace(/\.\w+$/, '')); entity.title =
entity.date = entity.yml.date; entity.yml.title || Desi.firstCap(entity.name.replace(/\.\w+$/, ""));
entity.date = entity.yml.date;
if (!entity.date) { if (!entity.date) {
// TODO tell YAML parser to keep the date a string // TODO tell YAML parser to keep the date a string
entity.date = new Date(entity.yml.created_at entity.date = new Date(
|| entity.yml.time entity.yml.created_at ||
|| entity.yml.updated_at entity.yml.time ||
|| entity.createdDate entity.yml.updated_at ||
|| entity.lastModifiedDate entity.createdDate ||
entity.lastModifiedDate
).toISOString(); ).toISOString();
} }
if ('object' === typeof entity.date) { if ("object" === typeof entity.date) {
entity.date = entity.date.toISOString(); entity.date = entity.date.toISOString();
} }
entity.updated_at = entity.yml.updated_at || entity.lastModifiedDate; entity.updated_at = entity.yml.updated_at || entity.lastModifiedDate;
entity.published_at = Desi.fromLocaleDate(entity.date || entity.lastModifiedDate); entity.published_at = Desi.fromLocaleDate(
entity.year = entity.published_at.year; entity.date || entity.lastModifiedDate
entity.month = entity.published_at.month; );
entity.day = entity.published_at.day; entity.year = entity.published_at.year;
entity.hour = entity.published_at.hour; entity.month = entity.published_at.month;
entity.twelve_hour = entity.published_at.twelve_hour; entity.day = entity.published_at.day;
entity.meridian = entity.published_at.meridian; entity.hour = entity.published_at.hour;
entity.minute = entity.published_at.minute; entity.twelve_hour = entity.published_at.twelve_hour;
entity.meridian = entity.published_at.meridian;
entity.minute = entity.published_at.minute;
// let's just agree that that's too far // let's just agree that that's too far
//entity.second = entity.published_at.second; //entity.second = entity.published_at.second;
entity.slug = Desi.slugify(entity.title); entity.slug = Desi.slugify(entity.title);
entity.slug_path = Desi.slugifyPath(entity.relativePath); entity.slug_path = Desi.slugifyPath(entity.relativePath);
entity.slugPath = Desi.slugifyPath(entity.relativePath); entity.slugPath = Desi.slugifyPath(entity.relativePath);
// TODO type checking like below // TODO type checking like below
entity.redirects = Array.isArray(entity.yml.redirects) && entity.yml.redirects|| []; entity.redirects =
(Array.isArray(entity.yml.redirects) && entity.yml.redirects) || [];
// categories // categories
if (Array.isArray(entity.yml.categories)) { if (Array.isArray(entity.yml.categories)) {
entity.categories = entity.yml.categories; entity.categories = entity.yml.categories;
} } else if ("string" === typeof entity.yml.categories) {
else if ('string' === typeof entity.yml.categories) { entity.categories = [entity.yml.categories];
entity.categories = [entity.yml.categories]; } else if ("string" === typeof entity.yml.category) {
} entity.categories = [entity.yml.category];
else if ('string' === typeof entity.yml.category) { } else {
entity.categories = [entity.yml.category]; entity.categories = [];
}
else {
entity.categories = [];
} }
// tags // tags
if (Array.isArray(entity.yml.tags)) { if (Array.isArray(entity.yml.tags)) {
entity.tags = entity.yml.tags; entity.tags = entity.yml.tags;
} } else if ("string" === typeof entity.yml.tags) {
else if ('string' === typeof entity.yml.tags) { entity.tags = [entity.yml.tags];
entity.tags = [entity.yml.tags]; } else {
} entity.tags = [];
else {
entity.tags = [];
} }
entity.permalink = entity.permalink || entity.yml.permalink; entity.permalink = entity.permalink || entity.yml.permalink;
if (!entity.permalink) { if (!entity.permalink) {
// try the fallback_permalink first (because we're looking at files that don't have yml) // try the fallback_permalink first (because we're looking at files that don't have yml)
// then try the normal permalink (because :filename -> :title and whatnot, so it'll work) // then try the normal permalink (because :filename -> :title and whatnot, so it'll work)
entity.permalink = Desi.permalinkify(desi, collection.fallback_permalink || collection.permalink, entity); entity.permalink = Desi.permalinkify(
desi,
collection.fallback_permalink || collection.permalink,
entity
);
} }
/* /*
if (!/\.x?html?$/.test(entity.permalink)) { if (!/\.x?html?$/.test(entity.permalink)) {
entity.htmllink = path.join(entity.permalink, 'index.html'); entity.htmllink = path.join(entity.permalink, 'index.html');
@ -145,38 +155,51 @@
*/ */
// relative to the site // relative to the site
entity.relative_file = path.join(env.base_path, entity.permalink) entity.relative_file = path
.replace(/\/$/, '/index.html'); .join(env.base_path, entity.permalink)
entity.relative_href = path.join(env.base_path, entity.permalink) .replace(/\/$/, "/index.html");
.replace(/\/index\.html$/, '/'); entity.relative_href = path
entity.relative_link = entity.relative_href; .join(env.base_path, entity.permalink)
.replace(/\/index\.html$/, "/");
entity.relative_link = entity.relative_href;
entity.url = env.base_url + path.join(env.base_path, entity.permalink) entity.url =
.replace(/\/index\.html$/, '/'); env.base_url +
entity.canonical_url = env.base_url + path.join(env.base_path, entity.permalink) path.join(env.base_path, entity.permalink).replace(/\/index\.html$/, "/");
.replace(/\/index\.html$/, '/'); entity.canonical_url =
entity.production_url = desi.site.base_url + path.join(desi.site.base_path, entity.permalink) env.base_url +
.replace(/\/index\.html$/, '/'); path.join(env.base_path, entity.permalink).replace(/\/index\.html$/, "/");
entity.relative_url = path.join(env.base_path, entity.permalink) entity.production_url =
.replace(/\/index\.html$/, '/'); desi.site.base_url +
path
.join(desi.site.base_path, entity.permalink)
.replace(/\/index\.html$/, "/");
entity.relative_url = path
.join(env.base_path, entity.permalink)
.replace(/\/index\.html$/, "/");
if (env.explicitIndexes || env.explicitIndices || env.explicit_indexes || env.explicit_indices) { if (
env.explicitIndexes ||
env.explicitIndices ||
env.explicit_indexes ||
env.explicit_indices
) {
// NOTE: file_url is NOT replaced // NOTE: file_url is NOT replaced
['url', 'canonical_url', 'production_url', 'relative_url'].forEach(function (url) { ["url", "canonical_url", "production_url", "relative_url"].forEach(
entity[url] = entity[url].replace(/\/$/, '/index.html'); function (url) {
}); entity[url] = entity[url].replace(/\/$/, "/index.html");
}
);
} }
// i.e. bootstrap, hero page, darkly // i.e. bootstrap, hero page, darkly
entity.theme = entity.theme || entity.yml.theme; entity.theme = entity.theme || entity.yml.theme;
entity.layout = entity.layout || entity.yml.layout; entity.layout = entity.layout || entity.yml.layout;
entity.swatch = entity.swatch || entity.yml.swatch; entity.swatch = entity.swatch || entity.yml.swatch;
}; };
cores.disqus = function (desi, env, collection, entity) { cores.disqus = function (desi, env, collection, entity) {
var yml = entity.yml var yml = entity.yml;
;
if (yml.uuid) { if (yml.uuid) {
entity.disqus_identifier = yml.uuid; entity.disqus_identifier = yml.uuid;
} }
@ -184,4 +207,4 @@
}; };
exports.DesiraeTransformCore = cores.DesiraeTransformCore = cores; exports.DesiraeTransformCore = cores.DesiraeTransformCore = cores;
}('undefined' !== typeof exports && exports || window)); })(("undefined" !== typeof exports && exports) || window);

View File

@ -1,9 +1,7 @@
;(function (exports) { (function (exports) {
'use strict'; "use strict";
var path = exports.path || require('path')
;
var path = exports.path || require("path");
function escapeRegExp(str) { function escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
} }
@ -11,10 +9,8 @@
function safeResolve(basename, target) { function safeResolve(basename, target) {
basename = path.resolve(basename); basename = path.resolve(basename);
var targetname = path.resolve(basename, target) var targetname = path.resolve(basename, target),
, re = new RegExp('^' + escapeRegExp(basename) + '(/|$)') re = new RegExp("^" + escapeRegExp(basename) + "(/|$)");
;
return re.test(targetname) && targetname; return re.test(targetname) && targetname;
} }
@ -22,29 +18,24 @@
exports.escapeRegExp = escapeRegExp; exports.escapeRegExp = escapeRegExp;
function create(Desi) { function create(Desi) {
var fsapi = Desi.fsapi || require('./node-adapters').fsapi var fsapi = Desi.fsapi || require("./node-adapters").fsapi;
;
fsapi.getConfigs = function (confs) { fsapi.getConfigs = function (confs) {
var opts = { extensions: ['yml', 'yaml', 'json'], dotfiles: false, contents: true, sha1sum: true } var opts = {
; extensions: ["yml", "yaml", "json"],
dotfiles: false,
contents: true,
sha1sum: true,
};
return fsapi.getMeta(confs, opts).then(function (collections) { return fsapi.getMeta(confs, opts).then(function (collections) {
var obj = {} var obj = {};
;
Object.keys(collections).forEach(function (key) { Object.keys(collections).forEach(function (key) {
var files = collections[key] var files = collections[key],
, keyname = key.replace(/\.(json|ya?ml|\/)$/i, '') keyname = key.replace(/\.(json|ya?ml|\/)$/i, "");
;
obj[keyname] = obj[keyname] || {}; obj[keyname] = obj[keyname] || {};
files.forEach(function (file) { files.forEach(function (file) {
var filename = file.name.replace(/\.(json|ya?ml)$/i, '') var filename = file.name.replace(/\.(json|ya?ml)$/i, ""),
, data = {} data = {};
;
if (file.error) { if (file.error) {
console.error(file); console.error(file);
console.error(file.error); console.error(file.error);
@ -57,17 +48,16 @@
if ("undefined" === data) { if ("undefined" === data) {
data = {}; data = {};
} }
} catch(e) { } catch (e) {
data = { error: e }; data = { error: e };
console.error("Could not parse yaml for " + filename); console.error("Could not parse yaml for " + filename);
console.error(file); console.error(file);
console.error(e); console.error(e);
} }
} } else if (/\.(json)$/i.test(file.name)) {
else if (/\.(json)$/i.test(file.name)) {
try { try {
data = JSON.parse(file.contents) || {}; data = JSON.parse(file.contents) || {};
} catch(e) { } catch (e) {
data = { error: e }; data = { error: e };
console.error("Could not parse json for " + filename); console.error("Could not parse json for " + filename);
console.error(file); console.error(file);
@ -99,36 +89,34 @@
}; };
fsapi.getAllPartials = function () { fsapi.getAllPartials = function () {
return fsapi.getConfigs(['partials', 'partials.yml']).then(function (results) { return fsapi
var partials = {} .getConfigs(["partials", "partials.yml"])
; .then(function (results) {
var partials = {};
Object.keys(results.partials).forEach(function (key) {
var partial = results.partials[key];
Object.keys(partial).forEach(function (prop) {
if (partials[prop]) {
console.warn("partial '" + prop + "' overwritten by " + key);
}
Object.keys(results.partials).forEach(function (key) { partials[prop] = partial[prop];
var partial = results.partials[key] });
;
Object.keys(partial).forEach(function (prop) {
if (partials[prop]) {
console.warn('partial \'' + prop + '\' overwritten by ' + key);
}
partials[prop] = partial[prop];
}); });
});
return partials; return partials;
}); });
}; };
fsapi.getAllConfigFiles = function () { fsapi.getAllConfigFiles = function () {
return fsapi.getConfigs(['config.yml', 'site.yml', 'authors']).then(function (results) { return fsapi
var authors = results.authors .getConfigs(["config.yml", "site.yml", "authors"])
, config = results.config.config .then(function (results) {
, site = results.site.site var authors = results.authors,
; config = results.config.config,
site = results.site.site;
return { config: config, authors: authors, site: site }; return { config: config, authors: authors, site: site };
}); });
}; };
return exports; return exports;
@ -136,8 +124,7 @@
if (exports.Desirae) { if (exports.Desirae) {
create(exports.Desirae); create(exports.Desirae);
} } else {
else {
exports.create = create; exports.create = create;
} }
}('undefined' !== typeof exports && exports || window)); })(("undefined" !== typeof exports && exports) || window);

View File

@ -1,5 +1,5 @@
;(function (exports) { (function (exports) {
'use strict'; "use strict";
exports.verifyConfig = function (conf) { exports.verifyConfig = function (conf) {
if (!conf.NuhohSpec) { if (!conf.NuhohSpec) {
@ -28,14 +28,18 @@
if (!Array.isArray(conf.collections)) { if (!Array.isArray(conf.collections)) {
if (conf.posts) { if (conf.posts) {
console.error("Please indent and nest 'posts' under the key 'collection' to continue"); console.error(
"Please indent and nest 'posts' under the key 'collection' to continue"
);
} }
throw new Error("missing key 'collections'."); throw new Error("missing key 'collections'.");
} }
if (!conf.themes) { if (!conf.themes) {
if (conf.twitter) { if (conf.twitter) {
console.error("Please indent and nest 'twitter' under the key 'themes' to continue"); console.error(
"Please indent and nest 'twitter' under the key 'themes' to continue"
);
} }
throw new Error("missing key 'themes'"); throw new Error("missing key 'themes'");
} }
@ -55,4 +59,4 @@
throw new Error("missing key root"); throw new Error("missing key root");
} }
}; };
}('undefined' !== typeof exports && exports || window)); })(("undefined" !== typeof exports && exports) || window);

193
package-lock.json generated Normal file
View File

@ -0,0 +1,193 @@
{
"name": "desirae",
"version": "0.11.5",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"requires": {
"sprintf-js": "~1.0.2"
}
},
"bluebird": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz",
"integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE="
},
"crypto-rand": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/crypto-rand/-/crypto-rand-0.0.2.tgz",
"integrity": "sha1-Hn3CMQLhiRo+6zQPtwrElYCzLt0="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"foreachasync": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-5.1.3.tgz",
"integrity": "sha512-q3/l5D2pyZG8aYe9xZQDAnS/KyP7QJakLUNrU/qGkd4bQUTzCUmvVFjagRggoxxjtb1ZrJB+jQeG4ojOLWkztw=="
},
"fs-extra": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.6.4.tgz",
"integrity": "sha1-9G8MdbeEH40gCzNIzU1pHVoJnRU=",
"requires": {
"jsonfile": "~1.0.1",
"mkdirp": "0.3.x",
"ncp": "~0.4.2",
"rimraf": "~2.2.0"
},
"dependencies": {
"mkdirp": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz",
"integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc="
}
}
},
"fs.extra": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/fs.extra/-/fs.extra-1.3.2.tgz",
"integrity": "sha1-3QI/kwE77iRTHxszUUw3sg/ZM0k=",
"requires": {
"fs-extra": "~0.6.1",
"mkdirp": "~0.3.5",
"walk": "^2.3.9"
},
"dependencies": {
"mkdirp": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz",
"integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc="
}
}
},
"js-yaml": {
"version": "3.14.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
"integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"jsonfile": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.0.1.tgz",
"integrity": "sha1-6l7+QLg2kLmGZ2FKc5L8YOhCwN0="
},
"linkify-it": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-0.1.5.tgz",
"integrity": "sha1-OMWD0y+pPtcm2gDHrwAQeL+2uUU=",
"requires": {
"uc.micro": "^1.0.0"
},
"dependencies": {
"uc.micro": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
}
}
},
"markdown-it": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-3.1.0.tgz",
"integrity": "sha1-IKcejmexKXyWrEfQD3tuaQ1uDDY=",
"requires": {
"argparse": "~ 1.0.0",
"linkify-it": "~ 0.1.2",
"mdurl": "~ 1.0.0",
"uc.micro": "~ 0.1.0"
}
},
"mdurl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
"integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4="
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"requires": {
"minimist": "^1.2.5"
}
},
"mustache": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/mustache/-/mustache-2.3.2.tgz",
"integrity": "sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ=="
},
"ncp": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz",
"integrity": "sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ="
},
"node-uuid": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
"integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc="
},
"rimraf": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
"integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI="
},
"secret-utils": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/secret-utils/-/secret-utils-1.0.2.tgz",
"integrity": "sha1-88GhRhCpTH+gMX3RVqj+AJgDb6c=",
"requires": {
"crypto-rand": "0.0.2",
"urlsafe-base64": "0.0.2"
}
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"uc.micro": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-0.1.0.tgz",
"integrity": "sha1-7aESHR/blhVO1v3oJHu724MzCMo="
},
"urlsafe-base64": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/urlsafe-base64/-/urlsafe-base64-0.0.2.tgz",
"integrity": "sha1-+VqmedXqb86RQtY8tXCd4x7r7UI="
},
"walk": {
"version": "2.3.14",
"resolved": "https://registry.npmjs.org/walk/-/walk-2.3.14.tgz",
"integrity": "sha512-5skcWAUmySj6hkBdH6B6+3ddMjVQYH5Qy9QGbPmN8kVmLteXk+yVXg+yfk1nbX30EYakahLrr8iPcCxJQSCBeg==",
"requires": {
"foreachasync": "^3.0.0"
},
"dependencies": {
"foreachasync": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz",
"integrity": "sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY="
}
}
}
}
}

View File

@ -4,6 +4,7 @@
"description": "An in-browser static blog library and static site generator. Similar to Jekyll, Octopress, Nanoc, etc", "description": "An in-browser static blog library and static site generator. Similar to Jekyll, Octopress, Nanoc, etc",
"main": "desirae.js", "main": "desirae.js",
"scripts": { "scripts": {
"prettier": "prettier --write './**/*.{js,css,html,json,md,py,xml}'",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"repository": { "repository": {

View File

@ -1,4 +1,4 @@
'use strict'; "use strict";
// http://ruhoh.com/docs/2/pages/#toc_41 // http://ruhoh.com/docs/2/pages/#toc_41
/* /*
@ -6,32 +6,31 @@
Otherwise it should use the the permalink for that collection. Otherwise it should use the the permalink for that collection.
*/ */
var tags var tags,
, permalinkTransforms permalinkTransforms,
, cases cases,
, path = /*exports.path ||*/ require('path') path = /*exports.path ||*/ require("path");
;
tags = { tags = {
year: "Year from the pages filename" year: "Year from the pages filename",
, month: "Month from the pages filename" month: "Month from the pages filename",
, day: "Day from the pages filename" day: "Day from the pages filename",
, path: "The page file's path relative to the base of your website." path: "The page file's path relative to the base of your website.",
, relative_path: "The page file's path relative to its name-spaced directory." relative_path: "The page file's path relative to its name-spaced directory.",
, filename: "The page file's filename (path is not included)." filename: "The page file's filename (path is not included).",
, categories: "The specified categories for this page. If more than one category is set, only the first one is used. If no categories exist, the URL omits this parameter." categories:
, i_month: "Month from the pages filename without leading zeros." "The specified categories for this page. If more than one category is set, only the first one is used. If no categories exist, the URL omits this parameter.",
, i_day: "Day from the pages filename without leading zeros." i_month: "Month from the pages filename without leading zeros.",
, title: "The title, as a slug." i_day: "Day from the pages filename without leading zeros.",
, slug: "alias of title" title: "The title, as a slug.",
, name: "alias of title" slug: "alias of title",
, collection: "i.e. posts/ or essays/ or whatever/" name: "alias of title",
collection: "i.e. posts/ or essays/ or whatever/",
}; };
function pad(str, n) { function pad(str, n) {
str = str.toString(); str = str.toString();
if (str.length < n) { if (str.length < n) {
str = '0' + str; str = "0" + str;
} }
return str; return str;
@ -39,49 +38,51 @@ function pad(str, n) {
// https://www.youtube.com/watch?v=1NryFD9_hR0&list=RDOeLUK4a6Ojc&index=2 // https://www.youtube.com/watch?v=1NryFD9_hR0&list=RDOeLUK4a6Ojc&index=2
cases = { cases = {
"/:title.html" : "/my-title.html" "/:title.html": "/my-title.html",
, ":title/" : "/my-title/" ":title/": "/my-title/",
, "/:bad/:title/" : "/:bad/my-title/" "/:bad/:title/": "/:bad/my-title/",
, "/:slug/" : "/my-title/" "/:slug/": "/my-title/",
, "/:path/:name.html" : "/posts/fun/my-title.html" "/:path/:name.html": "/posts/fun/my-title.html",
, "/:relative_path/:name/" : "/fun/my-title/" "/:relative_path/:name/": "/fun/my-title/",
, "/:year-:month-:day/:name" : "/2015-07-04/my-title/" "/:year-:month-:day/:name": "/2015-07-04/my-title/",
, "/:year/:i_month/:i_day/:name" : "/2015/7/4/my-title/" "/:year/:i_month/:i_day/:name": "/2015/7/4/my-title/",
, "/:filename.html" : "/my-file-name.html" "/:filename.html": "/my-file-name.html",
, "/:filename" : "/my-file-name/" "/:filename": "/my-file-name/",
, "/:filename/" : "/my-file-name/" "/:filename/": "/my-file-name/",
, "/:collection/:title/" : "/posts/my-title/" "/:collection/:title/": "/posts/my-title/",
, "/:collection/:filename" : "/posts/my-file-name/" "/:collection/:filename": "/posts/my-file-name/",
, "/:something/:or/:other" : "/:something/:or/:other/" "/:something/:or/:other": "/:something/:or/:other/",
, "/:categories/:title/" : "/desi/my-title/" "/:categories/:title/": "/desi/my-title/",
}; };
Object.keys(cases).forEach(function (tpl) { Object.keys(cases).forEach(function (tpl) {
var entity var entity, tpld;
, tpld
;
entity = { entity = {
year : '2015' year: "2015",
, month : '07' month: "07",
, day : '04' day: "04",
, title : "My Title" title: "My Title",
, slug : "my-title" slug: "my-title",
, name : "My-File-Name.html" name: "My-File-Name.html",
, relativePath : "posts/fun" relativePath: "posts/fun",
, path : "posts/fun/My-File-Name.html" path: "posts/fun/My-File-Name.html",
, collection : "posts" collection: "posts",
, yml : { categories: ['desi'] } yml: { categories: ["desi"] },
}; };
tpld = permalinker(tpl, entity); tpld = permalinker(tpl, entity);
if (cases[tpl] !== tpld) { if (cases[tpl] !== tpld) {
console.error('[ERROR]'); console.error("[ERROR]");
console.error(tpl + ' ' + tpld + ' ' + cases[tpl]); console.error(tpl + " " + tpld + " " + cases[tpl]);
throw new Error( throw new Error(
"Did not template permalink correctly. " "Did not template permalink correctly. " +
+ tpl + ' ' + tpld + ' ' + cases[tpl] tpl +
" " +
tpld +
" " +
cases[tpl]
); );
} }
}); });