Compare commits
13 Commits
month_name
...
master
Author | SHA1 | Date |
---|---|---|
|
706111f8ba | |
|
b0a56bec64 | |
|
5ee8ea9b60 | |
|
215cef976f | |
|
ceb90c2bfa | |
|
b5223b1053 | |
|
a9cb7a58a3 | |
|
c81ff7a441 | |
|
9131fb9a42 | |
|
0909c8e9cc | |
|
60e2764a1b | |
|
d77b91e27a | |
|
eaf0c9bfba |
|
@ -0,0 +1 @@
|
|||
{}
|
94
DATA.md
94
DATA.md
|
@ -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.
|
||||
|
||||
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)
|
||||
|
||||
|
@ -16,66 +14,60 @@ config.yml is for anything that doesn't change the site (from where to read dire
|
|||
desi = {}
|
||||
```
|
||||
|
||||
desi
|
||||
====
|
||||
# desi
|
||||
|
||||
* `config` - literally `config.yml`, parsed
|
||||
* `site` - literally `site.yml`, parsed
|
||||
* `authors` - literally the authors from `authors/*.yml`, parsed
|
||||
* `author` - the primary author of the site
|
||||
* `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)
|
||||
* `collection` - config related to this collection
|
||||
* `entity` - the page, post, article, etc that is the focus of the present template process
|
||||
* `themes` - all themes
|
||||
* `theme` - the default theme
|
||||
* `layout` - the selected layout for this theme
|
||||
* `satch` - the selected swatch for this theme
|
||||
* `categories` - all categories
|
||||
* `tags` - all tags
|
||||
* `styles` - ??? goes into the final template in the head
|
||||
* `scripts` - ?? that goes into the final template just before the body close
|
||||
- `config` - literally `config.yml`, parsed
|
||||
- `site` - literally `site.yml`, parsed
|
||||
- `authors` - literally the authors from `authors/*.yml`, parsed
|
||||
- `author` - the primary author of the site
|
||||
- `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)
|
||||
- `collection` - config related to this collection
|
||||
- `entity` - the page, post, article, etc that is the focus of the present template process
|
||||
- `themes` - all themes
|
||||
- `theme` - the default theme
|
||||
- `layout` - the selected layout for this theme
|
||||
- `satch` - the selected swatch for this theme
|
||||
- `categories` - all categories
|
||||
- `tags` - all tags
|
||||
- `styles` - ??? goes into the final template in the head
|
||||
- `scripts` - ?? that goes into the final template just before the body close
|
||||
|
||||
desi.entity
|
||||
===========
|
||||
# desi.entity
|
||||
|
||||
stuff
|
||||
|
||||
* `uuid`
|
||||
* `title`
|
||||
* `disqus_url`
|
||||
* `disqus_identifier`
|
||||
- `uuid`
|
||||
- `title`
|
||||
- `disqus_url`
|
||||
- `disqus_identifier`
|
||||
|
||||
more stuff
|
||||
|
||||
* `type` - `post`, `page`, etc
|
||||
* `authors` - literally the relevant authors from `authors/*.yml`, parsed
|
||||
* `author` - the primary author of this entity
|
||||
* `theme` - null or a non-default theme
|
||||
* `layout` - null or a non-default layout for this theme
|
||||
* `swatch` - null or a non-default swatch for this theme
|
||||
* `categories`: [] // *all* categories in all collections
|
||||
* `tags`: [] // *all* categories in all collections
|
||||
* `production_canonical_url` the PRODUCTION canonical_url for this entity
|
||||
* `production_url` the PRODUCTION url for this entity
|
||||
* `production_path` the PRODUCTION path for this entity
|
||||
* `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`)
|
||||
* `previous` the previous entity in this collection
|
||||
* `next` the next entitiy in this collection
|
||||
- `type` - `post`, `page`, etc
|
||||
- `authors` - literally the relevant authors from `authors/*.yml`, parsed
|
||||
- `author` - the primary author of this entity
|
||||
- `theme` - null or a non-default theme
|
||||
- `layout` - null or a non-default layout for this theme
|
||||
- `swatch` - null or a non-default swatch for this theme
|
||||
- `categories`: [] // _all_ categories in all collections
|
||||
- `tags`: [] // _all_ categories in all collections
|
||||
- `production_canonical_url` the PRODUCTION canonical_url for this entity
|
||||
- `production_url` the PRODUCTION url for this entity
|
||||
- `production_path` the PRODUCTION path for this entity
|
||||
- `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`)
|
||||
- `previous` the previous entity in this collection
|
||||
- `next` the next entitiy in this collection
|
||||
|
||||
NOTE: Plugins, widgets, etc SHOULD NOT modify config, site, authors, author, or env.
|
||||
|
||||
desi.posts
|
||||
==========
|
||||
# desi.posts
|
||||
|
||||
, posts: { collated: desi.collated }
|
||||
|
||||
desi.config
|
||||
===========
|
||||
# desi.config
|
||||
|
||||
desi.site
|
||||
===========
|
||||
# desi.site
|
||||
|
||||
desi.env
|
||||
===========
|
||||
# desi.env
|
||||
|
|
84
ENTITY.md
84
ENTITY.md
|
@ -2,61 +2,65 @@ This is what an entity looks like:
|
|||
|
||||
```yml
|
||||
# inherited from File Entity
|
||||
path : My Posts/My-Old-Name.html
|
||||
lastModifiedDate : 2015-07-04T13:56:01Z
|
||||
createdDate : 2015-07-04T13:56:01Z
|
||||
contents : '...' # whatever the file is
|
||||
path: My Posts/My-Old-Name.html
|
||||
lastModifiedDate: 2015-07-04T13:56:01Z
|
||||
createdDate: 2015-07-04T13:56:01Z
|
||||
contents: "..." # whatever the file is
|
||||
|
||||
# inherited from Collection Entity
|
||||
name : My-Old-Name.html
|
||||
relativePath : My Posts
|
||||
ext : .html
|
||||
collection : posts
|
||||
name: My-Old-Name.html
|
||||
relativePath: My Posts
|
||||
ext: .html
|
||||
collection: posts
|
||||
|
||||
# inherited from Content Entity
|
||||
frontmatter : '---\n...\n---' # frontmatter as a string
|
||||
yml : {} # frontmatter, parsed
|
||||
body : 'I think ...' # body, after frontmatter
|
||||
frontmatter: '---\n...\n---' # frontmatter as a string
|
||||
yml: {} # frontmatter, parsed
|
||||
body: "I think ..." # body, after frontmatter
|
||||
|
||||
# inherited from Normalized Entity
|
||||
title : My Title # yml.title | titlize(entity.name)
|
||||
slug : my-title # slugify(title)
|
||||
slug_path : my-posts # slugifyPath(relativePath)
|
||||
title: My Title # yml.title | titlize(entity.name)
|
||||
slug: my-title # slugify(title)
|
||||
slug_path: my-posts # slugifyPath(relativePath)
|
||||
|
||||
year : 2014
|
||||
month : 07
|
||||
day : 04
|
||||
hour : 13
|
||||
twelve_hour : 1
|
||||
meridian : pm
|
||||
minute : 22
|
||||
year: 2014
|
||||
month: 07
|
||||
day: 04
|
||||
hour: 13
|
||||
twelve_hour: 1
|
||||
meridian: pm
|
||||
minute: 22
|
||||
|
||||
categories : ['tech']
|
||||
tags : ['http','url','website']
|
||||
categories: ["tech"]
|
||||
tags:
|
||||
["http", "url", "website"]
|
||||
|
||||
# includes index.html
|
||||
relative_file : /posts/foo/index.html
|
||||
# includes index.html
|
||||
relative_file:
|
||||
/posts/foo/index.html
|
||||
|
||||
# excludes index.html
|
||||
relative_href : /posts/foo/
|
||||
# excludes index.html
|
||||
relative_href:
|
||||
/posts/foo/
|
||||
|
||||
# actual url of this file, even if redirect
|
||||
# excludes index.html
|
||||
url : http://dev.example.com/posts/foo/
|
||||
# actual url of this file, even if redirect
|
||||
# excludes index.html
|
||||
url:
|
||||
http://dev.example.com/posts/foo/
|
||||
|
||||
# the appropriate url, even in a redirect or duplicate
|
||||
# excludes index.html
|
||||
canonical_url : http://dev.example.com/posts/foo/
|
||||
# the appropriate url, even in a redirect or duplicate
|
||||
# excludes index.html
|
||||
canonical_url:
|
||||
http://dev.example.com/posts/foo/
|
||||
|
||||
# production url, even in development (for disqus, etc)
|
||||
# excludes index.html
|
||||
production_url : http://example.com/posts/foo/
|
||||
# production url, even in development (for disqus, etc)
|
||||
# excludes index.html
|
||||
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.
|
||||
|
||||
TODO
|
||||
----
|
||||
## TODO
|
||||
|
||||
* path relative from / in the browser
|
||||
* path relative from base_path on the file system
|
||||
- path relative from / in the browser
|
||||
- path relative from base_path on the file system
|
||||
|
|
34
GLOSSARY.md
34
GLOSSARY.md
|
@ -1,14 +1,12 @@
|
|||
Glossary
|
||||
========
|
||||
# Glossary
|
||||
|
||||
Canonical URL
|
||||
--------
|
||||
base\_url + base\_path + permalink
|
||||
## Canonical URL
|
||||
|
||||
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
|
||||
|
||||
|
@ -16,27 +14,25 @@ In some cases that might be https://school.edu/~/johndoe
|
|||
|
||||
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/
|
||||
|
||||
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.
|
||||
|
||||
For example:
|
||||
|
||||
* 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://school.edu/~/johndoe/blog/articles/first-post.html the permalink is yet still 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://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)
|
||||
|
||||
|
@ -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
|
||||
once again.
|
||||
|
||||
base\_url the
|
||||
permalink refers to
|
||||
base_url the
|
||||
permalink refers to
|
||||
|
|
206
LICENSE
206
LICENSE
|
@ -1,202 +1,4 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla
|
||||
Public License, v. 2.0. If a copy of the MPL was not distributed
|
||||
with this file, You can obtain one at
|
||||
https://mozilla.org/MPL/2.0/.
|
||||
|
|
210
README.md
210
README.md
|
@ -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
|
||||
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
|
||||
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.
|
||||
|
||||
|
@ -19,18 +17,17 @@ It can also run entirely from the commandline (with io.js / node.js).
|
|||
|
||||
**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)
|
||||
* 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
|
||||
* Browser (optional)
|
||||
* using your front-end templates to build in your front-end? Imagine that!
|
||||
* io.js (node.js) (optional)
|
||||
* 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).
|
||||
- `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)
|
||||
- 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
|
||||
- Browser (optional)
|
||||
- using your front-end templates to build in your front-end? Imagine that!
|
||||
- io.js (node.js) (optional)
|
||||
- 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).
|
||||
|
||||
Installation
|
||||
============
|
||||
# Installation
|
||||
|
||||
```bash
|
||||
bower install --save desirae
|
||||
|
@ -38,8 +35,7 @@ bower install --save desirae
|
|||
npm install --save desirae
|
||||
```
|
||||
|
||||
Why
|
||||
===
|
||||
# Why
|
||||
|
||||
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
|
||||
compatible with ruhoh).
|
||||
|
||||
Usage Overview
|
||||
==============
|
||||
# Usage Overview
|
||||
|
||||
### Before we get started
|
||||
|
||||
(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
|
||||
(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
|
||||
|
||||
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
|
||||
var desi = {}
|
||||
;
|
||||
var desi = {};
|
||||
```
|
||||
|
||||
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
|
||||
Desirae.init(
|
||||
desi
|
||||
, { url: 'https://johndoe.exmaple.com/blog'
|
||||
, base_url: 'https://johndoe.exmaple.com'
|
||||
, base_path: '/blog'
|
||||
, compiled_path: 'compiled_dev'
|
||||
Desirae.init(desi, {
|
||||
url: "https://johndoe.exmaple.com/blog",
|
||||
base_url: "https://johndoe.exmaple.com",
|
||||
base_path: "/blog",
|
||||
compiled_path: "compiled_dev",
|
||||
|
||||
// default: continue when possible
|
||||
, onError: function (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
// default: continue when possible
|
||||
onError: function (e) {
|
||||
return Promise.reject(e);
|
||||
},
|
||||
|
||||
// io.js / node.js only
|
||||
, working_path: './path/to/blog'
|
||||
}
|
||||
).then(function () {
|
||||
console.log('Desirae is initialized');
|
||||
// io.js / node.js only
|
||||
working_path: "./path/to/blog",
|
||||
}).then(function () {
|
||||
console.log("Desirae is initialized");
|
||||
});
|
||||
```
|
||||
|
||||
|
@ -198,43 +190,38 @@ Desirae.write(desi, env).then(function () {
|
|||
});
|
||||
```
|
||||
|
||||
|
||||
### Plugins
|
||||
|
||||
You need to start every file with a wrapper that is browser and io.js/node.js compatible
|
||||
|
||||
```javascript
|
||||
/*jshint -W054 */
|
||||
;(function (exports) {
|
||||
'use strict';
|
||||
|
||||
var DesiraeMyModule = {}
|
||||
;
|
||||
(function (exports) {
|
||||
"use strict";
|
||||
|
||||
var DesiraeMyModule = {};
|
||||
// ... a bunch of code ...
|
||||
|
||||
DesiraeMyModule.doStuff = doStuff;
|
||||
|
||||
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
|
||||
so steer away from things that are super iojs/node-ish or super window-ish.
|
||||
|
||||
Configuration
|
||||
=============
|
||||
# Configuration
|
||||
|
||||
There are a few configuration files:
|
||||
|
||||
* `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)
|
||||
* `config.yml` contains directives that describe *how* the blog should be compiled - more technical stuff.
|
||||
- `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)
|
||||
- `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.
|
||||
|
||||
API
|
||||
===
|
||||
# API
|
||||
|
||||
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)
|
||||
|
@ -246,28 +233,26 @@ But here's what I've got so far:
|
|||
|
||||
### (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`
|
||||
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
|
||||
var slim = exports.slimjs || require('slimjs')
|
||||
;
|
||||
|
||||
function render(contentstr/*, desi*/) {
|
||||
return PromiseA.resolve(slim(contentstr));
|
||||
var slim = exports.slimjs || require("slimjs");
|
||||
function render(contentstr /*, desi*/) {
|
||||
return Promise.resolve(slim(contentstr));
|
||||
}
|
||||
|
||||
Desirae.registerRenderer('.slim', render);
|
||||
Desirae.registerRenderer(".slim", render);
|
||||
```
|
||||
|
||||
## Data Mapping
|
||||
|
||||
### (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
|
||||
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.
|
||||
|
||||
*TODO: maybe pass in a two-level deep shallow copy ?*
|
||||
_TODO: maybe pass in a two-level deep shallow copy ?_
|
||||
|
||||
```javascript
|
||||
Desirae.registerDataMapper('ruhoh@3.0', function (view) {
|
||||
Desirae.registerDataMapper("ruhoh@3.0", function (view) {
|
||||
return {
|
||||
page: {
|
||||
name: view.entity.title
|
||||
}
|
||||
, author: {
|
||||
nickname: view.author.twitter
|
||||
}
|
||||
// ...
|
||||
name: view.entity.title,
|
||||
},
|
||||
author: {
|
||||
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.
|
||||
|
||||
|
||||
Server
|
||||
======
|
||||
# Server
|
||||
|
||||
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.
|
||||
|
||||
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`
|
||||
|
||||
* `dir` **must** be supplied. returns a flat list of all files, recursively
|
||||
* `dotfiles` default to `false`. includes dotfiles when `true`.
|
||||
* `extensions` defaults to `null`. inclode **only** the supplied extensions when `true`.
|
||||
- `dir` **must** be supplied. returns a flat list of all files, recursively
|
||||
- `dotfiles` default to `false`. includes dotfiles when `true`.
|
||||
- `extensions` defaults to `null`. inclode **only** the supplied extensions when `true`.
|
||||
|
||||
```json
|
||||
[
|
||||
{ "name": "happy-new-year.md"
|
||||
, "createdDate": "2015-01-05T18:19:30.000Z"
|
||||
, "lastModifiedDate": "2015-01-05T18:19:30.000Z"
|
||||
, "size": 2121
|
||||
, "relativePath": "posts/2015"
|
||||
}
|
||||
{
|
||||
"name": "happy-new-year.md",
|
||||
"createdDate": "2015-01-05T18:19:30.000Z",
|
||||
"lastModifiedDate": "2015-01-05T18:19:30.000Z",
|
||||
"size": 2121,
|
||||
"relativePath": "posts/2015"
|
||||
},
|
||||
|
||||
, { "name": "tips-for-the-ages.jade"
|
||||
, "createdDate": "2014-06-16T18:19:30.000Z"
|
||||
, "lastModifiedDate": "2014-06-16T18:19:30.000Z"
|
||||
, "size": 389
|
||||
, "relativePath": "posts"
|
||||
}
|
||||
, { "name": "my-first-post.html"
|
||||
, "createdDate": "2013-08-01T22:47:37.000Z"
|
||||
, "lastModifiedDate": "2013-08-01T22:47:37.000Z"
|
||||
, "size": 4118
|
||||
, "relativePath": "posts/2013"
|
||||
{
|
||||
"name": "tips-for-the-ages.jade",
|
||||
"createdDate": "2014-06-16T18:19:30.000Z",
|
||||
"lastModifiedDate": "2014-06-16T18:19:30.000Z",
|
||||
"size": 389,
|
||||
"relativePath": "posts"
|
||||
},
|
||||
{
|
||||
"name": "my-first-post.html",
|
||||
"createdDate": "2013-08-01T22:47:37.000Z",
|
||||
"lastModifiedDate": "2013-08-01T22:47:37.000Z",
|
||||
"size": 4118,
|
||||
"relativePath": "posts/2013"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
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`
|
||||
|
||||
* 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
|
||||
|
@ -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`
|
||||
|
||||
```json
|
||||
{ "path": "posts/intro-to-http-with-netcat-node-connect.md"
|
||||
, "createdDate": "2013-08-01T22:47:37.000Z"
|
||||
, "lastModifiedDate": "2013-08-01T22:47:37.000Z"
|
||||
, "contents": "..."
|
||||
, "sha1": "6eae3a5b062c6d0d79f070c26e6d62486b40cb46"
|
||||
{
|
||||
"path": "posts/intro-to-http-with-netcat-node-connect.md",
|
||||
"createdDate": "2013-08-01T22:47:37.000Z",
|
||||
"lastModifiedDate": "2013-08-01T22:47:37.000Z",
|
||||
"contents": "...",
|
||||
"sha1": "6eae3a5b062c6d0d79f070c26e6d62486b40cb46"
|
||||
}
|
||||
```
|
||||
|
||||
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`
|
||||
|
||||
* 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
|
||||
|
@ -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
|
||||
and return an error if you try to write to any other directory, unless `compiled=false` (not yet implemented).
|
||||
|
@ -452,9 +436,15 @@ The response may include errors of all shapes and sizes.
|
|||
}
|
||||
```
|
||||
|
||||
POST /api/fs/copy
|
||||
------------------
|
||||
## POST /api/fs/copy
|
||||
|
||||
```json
|
||||
{ files: { "assets/logo.png": "compiled/assets/logo.png" } }
|
||||
{ "files": { "assets/logo.png": "compiled/assets/logo.png" } }
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla \
|
||||
Public License, v. 2.0. If a copy of the MPL was not distributed \
|
||||
with this file, You can obtain one at \
|
||||
https://mozilla.org/MPL/2.0/.
|
||||
|
|
46
TODO.md
46
TODO.md
|
@ -4,7 +4,6 @@ show file path
|
|||
show prod url
|
||||
show dev url
|
||||
|
||||
|
||||
POST tests
|
||||
create a title and delete it (no error)
|
||||
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. does the frontmatter change? (yes)
|
||||
|
||||
|
||||
protection
|
||||
Don't allow changing the uuid, original_url, or original_date
|
||||
|
||||
TODO
|
||||
---
|
||||
## TODO
|
||||
|
||||
check that no other post uses the same permalink
|
||||
|
||||
|
||||
|
||||
default data-model 'ruhoh@2.2'
|
||||
other data-model 'desirae@1.0'
|
||||
|
||||
Widgets
|
||||
=======
|
||||
# Widgets
|
||||
|
||||
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
|
||||
config:
|
||||
foobeep: boop
|
||||
|
||||
|
||||
handle:
|
||||
- html
|
||||
- markdown
|
||||
|
@ -52,11 +46,10 @@ widgets:
|
|||
```
|
||||
|
||||
```javascript
|
||||
'use strict';
|
||||
"use strict";
|
||||
|
||||
module.exports.Foogizmo.create = function (foogizmoConf, desiState) {
|
||||
return new Promise(function (resolve) {
|
||||
|
||||
function pager(desiPageState) {
|
||||
// Do processing
|
||||
|
||||
|
@ -68,29 +61,28 @@ module.exports.Foogizmo.create = function (foogizmoConf, desiState) {
|
|||
|
||||
desiPostState.fooembedinator = function (fooval) {
|
||||
// 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 });
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
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.
|
||||
|
||||
config.yml
|
||||
----------
|
||||
## config.yml
|
||||
|
||||
Instead of having special names for some properties (`_root`)
|
||||
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.
|
||||
|
||||
data.yml
|
||||
--------
|
||||
## data.yml
|
||||
|
||||
No changes
|
||||
|
||||
|
||||
config.ru
|
||||
---------
|
||||
## config.ru
|
||||
|
||||
REMOVED (ruby only)
|
||||
|
||||
themes layout
|
||||
-------------
|
||||
## themes layout
|
||||
|
||||
TODO
|
||||
|
||||
|
|
18
bower.json
18
bower.json
|
@ -1,15 +1,10 @@
|
|||
{
|
||||
"name": "desirae",
|
||||
"version": "0.11.2",
|
||||
"authors": [
|
||||
"AJ ONeal <awesome@coolaj86.com>"
|
||||
],
|
||||
"authors": ["AJ ONeal <awesome@coolaj86.com>"],
|
||||
"description": "A blogging platform in the browser. Wow!",
|
||||
"main": "desirae.js",
|
||||
"moduleType": [
|
||||
"globals",
|
||||
"node"
|
||||
],
|
||||
"moduleType": ["globals", "node"],
|
||||
"keywords": [
|
||||
"desirae",
|
||||
"dear",
|
||||
|
@ -26,15 +21,8 @@
|
|||
],
|
||||
"license": "Apache2",
|
||||
"homepage": "http://github.com/DearDesi/desirae",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"ignore": ["**/.*", "node_modules", "bower_components", "test", "tests"],
|
||||
"dependencies": {
|
||||
"bluebird": "~2.6.2",
|
||||
"escape-string-regexp": "~1.0.2",
|
||||
"forEachAsync": "~5.0.5",
|
||||
"js-yaml": "~3.2.5",
|
||||
|
|
1159
desirae.js
1159
desirae.js
File diff suppressed because it is too large
Load Diff
|
@ -1,25 +1,23 @@
|
|||
/*jshint -W054 */
|
||||
;(function (exports) {
|
||||
'use strict';
|
||||
|
||||
var path = exports.path || require('path')
|
||||
, months
|
||||
, cores = {}
|
||||
;
|
||||
(function (exports) {
|
||||
"use strict";
|
||||
|
||||
var path = exports.path || require("path"),
|
||||
months,
|
||||
cores = {};
|
||||
months = {
|
||||
1: 'January'
|
||||
, 2: 'February'
|
||||
, 3: 'March'
|
||||
, 4: 'April'
|
||||
, 5: 'May'
|
||||
, 6: 'June'
|
||||
, 7: 'July'
|
||||
, 8: 'August'
|
||||
, 9: 'September'
|
||||
, 10: 'October'
|
||||
, 11: 'November'
|
||||
, 12: 'December'
|
||||
1: "January",
|
||||
2: "February",
|
||||
3: "March",
|
||||
4: "April",
|
||||
5: "May",
|
||||
6: "June",
|
||||
7: "July",
|
||||
8: "August",
|
||||
9: "September",
|
||||
10: "October",
|
||||
11: "November",
|
||||
12: "December",
|
||||
};
|
||||
|
||||
function byDate(a, b) {
|
||||
|
@ -67,9 +65,7 @@
|
|||
}
|
||||
|
||||
function collate(entities, env) {
|
||||
var yearsArr = []
|
||||
;
|
||||
|
||||
var yearsArr = [];
|
||||
entities.forEach(function (f) {
|
||||
var set;
|
||||
var yindex = 3000 - f.year;
|
||||
|
@ -85,10 +81,10 @@
|
|||
|
||||
if (!set.months[mindex]) {
|
||||
set.months[mindex] = {
|
||||
month_name: monthName
|
||||
, month_number: mindex
|
||||
, month: monthName
|
||||
, pages: []
|
||||
month_name: monthName,
|
||||
month_number: mindex,
|
||||
month: monthName,
|
||||
pages: [],
|
||||
};
|
||||
}
|
||||
set = set.months[mindex];
|
||||
|
@ -125,4 +121,4 @@
|
|||
};
|
||||
|
||||
exports.DesiraeAggregateCore = cores.DesiraeAggregateCore = cores;
|
||||
}('undefined' !== typeof exports && exports || window));
|
||||
})(("undefined" !== typeof exports && exports) || window);
|
||||
|
|
|
@ -1,67 +1,66 @@
|
|||
/*jshint -W054 */
|
||||
;(function (exports) {
|
||||
'use strict';
|
||||
(function (exports) {
|
||||
"use strict";
|
||||
|
||||
function create(Desi) {
|
||||
// Chrome, Firefox, and even MSIE11+ all support crypto
|
||||
var crypto = window.crypto || window.msCrypto
|
||||
, PromiseA = window.Promise
|
||||
, algos
|
||||
;
|
||||
var crypto = window.crypto || window.msCrypto,
|
||||
Promise = window.Promise,
|
||||
algos;
|
||||
|
||||
// convenience mappings for common digest algorithms
|
||||
algos = {
|
||||
'sha1': 'SHA-1'
|
||||
, 'sha256': 'SHA-256'
|
||||
, 'sha512': 'SHA-512'
|
||||
sha1: "SHA-1",
|
||||
sha256: "SHA-256",
|
||||
sha512: "SHA-512",
|
||||
};
|
||||
|
||||
// The function to generate a sha1sum is the same as generating any digest
|
||||
// but here's a shortcut function anyway
|
||||
function sha1sum(str) {
|
||||
return hashsum('sha1', str);
|
||||
return hashsum("sha1", str);
|
||||
}
|
||||
|
||||
// a more general convenience function
|
||||
function hashsum(hash, str) {
|
||||
// you have to convert from string to array buffer
|
||||
var ab
|
||||
// you have to represent the algorithm as an object
|
||||
, algo = { name: algos[hash] }
|
||||
;
|
||||
|
||||
if ('string' === typeof str) {
|
||||
// you have to convert from string to array buffer
|
||||
var ab,
|
||||
// you have to represent the algorithm as an object
|
||||
algo = { name: algos[hash] };
|
||||
if ("string" === typeof str) {
|
||||
ab = str2ab(str);
|
||||
} else {
|
||||
ab = str;
|
||||
}
|
||||
|
||||
// All crypto digest methods return a promise
|
||||
return crypto.subtle.digest(algo, ab).then(function (digest) {
|
||||
// you have to convert the ArrayBuffer to a DataView and then to a hex String
|
||||
return ab2hex(digest);
|
||||
}).catch(function (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;
|
||||
});
|
||||
return crypto.subtle
|
||||
.digest(algo, ab)
|
||||
.then(function (digest) {
|
||||
// you have to convert the ArrayBuffer to a DataView and then to a hex String
|
||||
return ab2hex(digest);
|
||||
})
|
||||
.catch(function (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
|
||||
function ab2hex(ab) {
|
||||
var dv = new DataView(ab)
|
||||
, i
|
||||
, len
|
||||
, hex = ''
|
||||
, c
|
||||
;
|
||||
var dv = new DataView(ab),
|
||||
i,
|
||||
len,
|
||||
hex = "",
|
||||
c;
|
||||
|
||||
for (i = 0, len = dv.byteLength; i < len; i += 1) {
|
||||
c = dv.getUint8(i).toString(16);
|
||||
|
||||
if (c.length < 2) {
|
||||
c = '0' + c;
|
||||
c = "0" + c;
|
||||
}
|
||||
|
||||
hex += c;
|
||||
|
@ -72,36 +71,31 @@
|
|||
|
||||
// convert from string to arraybuffer
|
||||
function str2ab(stringToEncode, insertBom) {
|
||||
stringToEncode = stringToEncode.replace(/\r\n/g,"\n");
|
||||
stringToEncode = stringToEncode.replace(/\r\n/g, "\n");
|
||||
|
||||
var utftext = []
|
||||
, n
|
||||
, c
|
||||
;
|
||||
var utftext = [],
|
||||
n,
|
||||
c;
|
||||
|
||||
if (true === insertBom) {
|
||||
utftext[0] = 0xef;
|
||||
utftext[1] = 0xbb;
|
||||
utftext[2] = 0xbf;
|
||||
if (true === insertBom) {
|
||||
utftext[0] = 0xef;
|
||||
utftext[1] = 0xbb;
|
||||
utftext[2] = 0xbf;
|
||||
}
|
||||
|
||||
for (n = 0; n < stringToEncode.length; n += 1) {
|
||||
|
||||
c = stringToEncode.charCodeAt(n);
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -112,26 +106,21 @@
|
|||
//
|
||||
// FSAPI
|
||||
//
|
||||
var fsapi
|
||||
;
|
||||
var fsapi;
|
||||
|
||||
function request() {
|
||||
}
|
||||
request.get = function (url/*, query*/) {
|
||||
function request() {}
|
||||
request.get = function (url /*, query*/) {
|
||||
// Return a new promise.
|
||||
return new PromiseA(function(resolve, reject) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
// Do the usual XHR stuff
|
||||
var req = new XMLHttpRequest()
|
||||
;
|
||||
|
||||
req.onload = function() {
|
||||
var req = new XMLHttpRequest();
|
||||
req.onload = function () {
|
||||
// This is called even on 404 etc
|
||||
// so check the status
|
||||
if (200 === req.status) {
|
||||
// Resolve the promise with the response text
|
||||
resolve(req.response);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Otherwise reject with the status text
|
||||
// which will hopefully be a meaningful error
|
||||
reject(Error(req.statusText));
|
||||
|
@ -139,30 +128,27 @@
|
|||
};
|
||||
|
||||
// Handle network errors
|
||||
req.onerror = function() {
|
||||
req.onerror = function () {
|
||||
reject(Error("Network Error"));
|
||||
};
|
||||
|
||||
// Make the request
|
||||
req.open('GET', url);
|
||||
req.open("GET", url);
|
||||
req.send();
|
||||
});
|
||||
};
|
||||
request.post = function (url/*, query*/, body) {
|
||||
request.post = function (url /*, query*/, body) {
|
||||
// Return a new promise.
|
||||
return new PromiseA(function(resolve, reject) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
// Do the usual XHR stuff
|
||||
var req = new XMLHttpRequest()
|
||||
;
|
||||
|
||||
req.onload = function() {
|
||||
var req = new XMLHttpRequest();
|
||||
req.onload = function () {
|
||||
// This is called even on 404 etc
|
||||
// so check the status
|
||||
if (200 === req.status) {
|
||||
// Resolve the promise with the response text
|
||||
resolve(req.response);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Otherwise reject with the status text
|
||||
// which will hopefully be a meaningful error
|
||||
reject(Error(req.statusText));
|
||||
|
@ -170,14 +156,14 @@
|
|||
};
|
||||
|
||||
// Handle network errors
|
||||
req.onerror = function() {
|
||||
req.onerror = function () {
|
||||
reject(Error("Network Error"));
|
||||
};
|
||||
|
||||
req.open('POST', url);
|
||||
req.open("POST", url);
|
||||
req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
||||
// Make the request
|
||||
if ('string' !== typeof body) {
|
||||
if ("string" !== typeof body) {
|
||||
body = JSON.stringify(body);
|
||||
}
|
||||
req.send(body);
|
||||
|
@ -188,87 +174,93 @@
|
|||
fsapi.getMeta = function (collections, opts) {
|
||||
opts = opts || {};
|
||||
|
||||
var extensions = ''
|
||||
, dotfiles = ''
|
||||
, contents = ''
|
||||
, sha1sum = ''
|
||||
;
|
||||
|
||||
var extensions = "",
|
||||
dotfiles = "",
|
||||
contents = "",
|
||||
sha1sum = "";
|
||||
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) {
|
||||
dotfiles = '&dotfiles=true';
|
||||
dotfiles = "&dotfiles=true";
|
||||
}
|
||||
if (opts.contents) {
|
||||
contents = '&contents=true';
|
||||
contents = "&contents=true";
|
||||
}
|
||||
if (false === opts.sha1sum) {
|
||||
sha1sum = '&sha1sum=false';
|
||||
sha1sum = "&sha1sum=false";
|
||||
}
|
||||
|
||||
return request.post('/api/fs/walk?_method=GET' + dotfiles + extensions + contents + sha1sum, {
|
||||
dirs: collections
|
||||
}).then(function (resp) {
|
||||
return JSON.parse(resp);
|
||||
}).catch(function (e) {
|
||||
throw e;
|
||||
});
|
||||
return request
|
||||
.post(
|
||||
"/api/fs/walk?_method=GET" +
|
||||
dotfiles +
|
||||
extensions +
|
||||
contents +
|
||||
sha1sum,
|
||||
{
|
||||
dirs: collections,
|
||||
}
|
||||
)
|
||||
.then(function (resp) {
|
||||
return JSON.parse(resp);
|
||||
})
|
||||
.catch(function (e) {
|
||||
throw e;
|
||||
});
|
||||
};
|
||||
|
||||
fsapi.getContents = function (filepaths) {
|
||||
return request.post('/api/fs/files?_method=GET', {
|
||||
paths: filepaths
|
||||
}).then(function (resp) {
|
||||
return JSON.parse(resp);
|
||||
});
|
||||
return request
|
||||
.post("/api/fs/files?_method=GET", {
|
||||
paths: filepaths,
|
||||
})
|
||||
.then(function (resp) {
|
||||
return JSON.parse(resp);
|
||||
});
|
||||
};
|
||||
|
||||
fsapi.getCache = function () {
|
||||
return request.get('/api/fs/static/cache.json').then(function (resp) {
|
||||
return JSON.parse(resp);
|
||||
}).catch(function (/*e*/) {
|
||||
return {};
|
||||
}).then(function (obj) {
|
||||
return obj;
|
||||
});
|
||||
return request
|
||||
.get("/api/fs/static/cache.json")
|
||||
.then(function (resp) {
|
||||
return JSON.parse(resp);
|
||||
})
|
||||
.catch(function (/*e*/) {
|
||||
return {};
|
||||
})
|
||||
.then(function (obj) {
|
||||
return obj;
|
||||
});
|
||||
};
|
||||
|
||||
fsapi.copy = function (files) {
|
||||
var body = { files: files };
|
||||
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) {
|
||||
var response = JSON.parse(resp)
|
||||
;
|
||||
|
||||
return request.post("/api/fs/copy", body).then(function (resp) {
|
||||
var response = JSON.parse(resp);
|
||||
// not accurate for utf8/unicode, but close enough
|
||||
response.size = body.length;
|
||||
return response;
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
fsapi.putFiles = function (files) {
|
||||
var body = { files: files }
|
||||
;
|
||||
|
||||
var body = { files: files };
|
||||
files.forEach(function (file) {
|
||||
if (!file.contents || 'string' === typeof file.contents) {
|
||||
if (!file.contents || "string" === typeof file.contents) {
|
||||
return;
|
||||
}
|
||||
if (/\.json$/i.test(file.path)) {
|
||||
file.contents = JSON.stringify(file.contents);
|
||||
}
|
||||
else if (/\.ya?ml$/i.test(file.path)) {
|
||||
file.contents = exports.jsyaml.dump(file.contents);
|
||||
} else if (/\.ya?ml$/i.test(file.path)) {
|
||||
file.contents = exports.jsyaml.dump(file.contents);
|
||||
}
|
||||
});
|
||||
|
||||
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) {
|
||||
var response = JSON.parse(resp)
|
||||
;
|
||||
|
||||
return request.post("/api/fs/files", body).then(function (resp) {
|
||||
var response = JSON.parse(resp);
|
||||
// not accurate for utf8/unicode, but close enough
|
||||
response.size = body.length;
|
||||
return response;
|
||||
|
@ -281,4 +273,4 @@
|
|||
} else {
|
||||
exports.create = create;
|
||||
}
|
||||
}('undefined' !== typeof exports && exports || window));
|
||||
})(("undefined" !== typeof exports && exports) || window);
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
'use strict';
|
||||
"use strict";
|
||||
|
||||
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");
|
||||
throw new Error('Not Implemented.');
|
||||
throw new Error("Not Implemented.");
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*jshint -W054 */
|
||||
;(function (exports) {
|
||||
'use strict';
|
||||
(function (exports) {
|
||||
"use strict";
|
||||
|
||||
function desiMap(obj) {
|
||||
obj.desi = obj;
|
||||
|
@ -8,4 +8,4 @@
|
|||
}
|
||||
|
||||
exports.DesiraeDatamapCore = desiMap.DesiraeDatamapCore = desiMap;
|
||||
}('undefined' !== typeof exports && exports || window));
|
||||
})(("undefined" !== typeof exports && exports) || window);
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
/*jshint -W054 */
|
||||
;(function (exports) {
|
||||
'use strict';
|
||||
(function (exports) {
|
||||
"use strict";
|
||||
|
||||
function create(Desi) {
|
||||
Desi.YAML = {};
|
||||
Desi.YAML.parse = (exports.jsyaml || require('js-yaml')).load;
|
||||
Desi.YAML.stringify = (exports.jsyaml || require('js-yaml')).dump;
|
||||
Desi.YAML = {};
|
||||
Desi.YAML.parse = (exports.jsyaml || require("js-yaml")).load;
|
||||
Desi.YAML.stringify = (exports.jsyaml || require("js-yaml")).dump;
|
||||
|
||||
function readFrontMatter(text) {
|
||||
var lines
|
||||
, line
|
||||
, padIndent = ''
|
||||
, ymllines = []
|
||||
;
|
||||
|
||||
var lines,
|
||||
line,
|
||||
padIndent = "",
|
||||
ymllines = [];
|
||||
lines = text.split(/\n/);
|
||||
line = lines.shift();
|
||||
|
||||
|
@ -25,14 +23,14 @@
|
|||
// that start without indentation, so
|
||||
// we can add it if this is the case
|
||||
if (lines[0] && lines[0].match(/^\S/)) {
|
||||
padIndent = '';
|
||||
padIndent = "";
|
||||
}
|
||||
|
||||
while (true) {
|
||||
line = lines.shift();
|
||||
|
||||
// premature end-of-file (unsupported yaml)
|
||||
if (!line && '' !== line) {
|
||||
if (!line && "" !== line) {
|
||||
ymllines = [];
|
||||
break;
|
||||
}
|
||||
|
@ -44,24 +42,21 @@
|
|||
|
||||
if (line) {
|
||||
// supported yaml
|
||||
ymllines.push(padIndent + line);
|
||||
ymllines.push(padIndent + line);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// XXX can't be sorted because arrays get messed up
|
||||
//ymllines.sort();
|
||||
if (ymllines) {
|
||||
return '---\n' + ymllines.join('\n');
|
||||
return "---\n" + ymllines.join("\n");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function separateText(text, fm) {
|
||||
var len
|
||||
, yml
|
||||
;
|
||||
var len, yml;
|
||||
|
||||
yml = readFrontMatter(fm);
|
||||
// strip frontmatter from text, if any
|
||||
|
@ -72,27 +67,26 @@
|
|||
len = 0;
|
||||
}
|
||||
|
||||
return text.split(/\n/).slice(len).join('\n');
|
||||
return text.split(/\n/).slice(len).join("\n");
|
||||
}
|
||||
|
||||
function parseText(text) {
|
||||
var fm = readFrontMatter(text)
|
||||
, body = fm && separateText(text, fm)
|
||||
, yml
|
||||
;
|
||||
var fm = readFrontMatter(text),
|
||||
body = fm && separateText(text, fm),
|
||||
yml;
|
||||
|
||||
if (fm) {
|
||||
try {
|
||||
yml = Desi.YAML.parse(fm);
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
yml: yml
|
||||
, frontmatter: fm
|
||||
, body: body
|
||||
yml: yml,
|
||||
frontmatter: fm,
|
||||
body: body,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -112,4 +106,4 @@
|
|||
} else {
|
||||
exports.create = create;
|
||||
}
|
||||
}('undefined' !== typeof exports && exports || window));
|
||||
})(("undefined" !== typeof exports && exports) || window);
|
||||
|
|
|
@ -1,51 +1,47 @@
|
|||
'use strict';
|
||||
"use strict";
|
||||
|
||||
var PromiseA = require('bluebird').Promise
|
||||
, fs = PromiseA.promisifyAll(require('fs'))
|
||||
;
|
||||
var fs = require('fs').promises;
|
||||
|
||||
function create(Desi, options) {
|
||||
var fsapi = Desi.fsapi
|
||||
;
|
||||
|
||||
var fsapi = Desi.fsapi;
|
||||
options.blogdir = options.blogdir || options.working_path;
|
||||
|
||||
fsapi.getMeta = function (dirnames, opts) {
|
||||
opts = opts || {};
|
||||
|
||||
var extensions = ''
|
||||
, dotfiles = ''
|
||||
, contents = ''
|
||||
, sha1sum = ''
|
||||
;
|
||||
|
||||
var extensions = "",
|
||||
dotfiles = "",
|
||||
contents = "",
|
||||
sha1sum = "";
|
||||
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) {
|
||||
dotfiles = '&dotfiles=true';
|
||||
dotfiles = "&dotfiles=true";
|
||||
}
|
||||
if (opts.contents) {
|
||||
contents = '&contents=true';
|
||||
contents = "&contents=true";
|
||||
}
|
||||
if (false === opts.sha1sum) {
|
||||
sha1sum = '&sha1sum=false';
|
||||
sha1sum = "&sha1sum=false";
|
||||
}
|
||||
|
||||
return fsapi.walk.walkDirs(options.blogdir, dirnames, opts);
|
||||
};
|
||||
|
||||
fsapi.getContents = function (filepaths) {
|
||||
|
||||
return fsapi.getfs(options.blogdir, filepaths);
|
||||
};
|
||||
|
||||
fsapi.getCache = function () {
|
||||
return fs.readFileAsync(options.blogdir, '/cache.json').catch(function (/*e*/) {
|
||||
return {};
|
||||
}).then(function (obj) {
|
||||
return obj;
|
||||
});
|
||||
return fs
|
||||
.readFile(options.blogdir, "/cache.json")
|
||||
.catch(function (/*e*/) {
|
||||
return {};
|
||||
})
|
||||
.then(function (obj) {
|
||||
return obj;
|
||||
});
|
||||
};
|
||||
|
||||
fsapi.copy = function (files) {
|
||||
|
@ -55,14 +51,13 @@ function create(Desi, options) {
|
|||
|
||||
fsapi.putFiles = function (files, opts) {
|
||||
files.forEach(function (file) {
|
||||
if (!file.contents || 'string' === typeof file.contents) {
|
||||
if (!file.contents || "string" === typeof file.contents) {
|
||||
return;
|
||||
}
|
||||
if (/\.json$/i.test(file.path)) {
|
||||
file.contents = JSON.stringify(file.contents);
|
||||
}
|
||||
else if (/\.ya?ml$/i.test(file.path)) {
|
||||
file.contents = Desi.YAML.stringify(file.contents);
|
||||
} else if (/\.ya?ml$/i.test(file.path)) {
|
||||
file.contents = Desi.YAML.stringify(file.contents);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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 fs = require("fs").promises;
|
||||
var path = require("path");
|
||||
|
||||
var forEachAsync = require("foreachasync").forEachAsync;
|
||||
var walk = require("walk");
|
||||
var escapeRegExp = require("escape-string-regexp");
|
||||
var safeResolve = require("../utils").safeResolve;
|
||||
var sha1sum = function (str) {
|
||||
return require("secret-utils").hashsum("sha1", str);
|
||||
};
|
||||
var copyAll = require("util").promisify(require("fs.extra").copy);
|
||||
|
||||
//, tmpdir = require('os').tmpdir()
|
||||
function strip(prefix, pathname) {
|
||||
return pathname.substr(prefix.length + 1);
|
||||
}
|
||||
|
@ -23,24 +23,24 @@ function walkDir(parent, sub, opts) {
|
|||
opts.sha1sum = true;
|
||||
}
|
||||
|
||||
var prefix = path.resolve(parent)
|
||||
, trueRoot = path.resolve(prefix, sub)
|
||||
, files = []
|
||||
;
|
||||
|
||||
var prefix = path.resolve(parent);
|
||||
var trueRoot = path.resolve(prefix, sub);
|
||||
var files = [];
|
||||
function filter(name) {
|
||||
if (!name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!opts.dotfiles && ('.' === name[0])) {
|
||||
if (!opts.dotfiles && "." === name[0]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts.extensions && opts.extensions.length) {
|
||||
if (!opts.extensions.some(function (ext) {
|
||||
return new RegExp('\\.' + escapeRegExp(ext) + '$').test(name);
|
||||
})) {
|
||||
if (
|
||||
!opts.extensions.some(function (ext) {
|
||||
return new RegExp("\\." + escapeRegExp(ext) + "$").test(name);
|
||||
})
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -48,11 +48,9 @@ function walkDir(parent, sub, opts) {
|
|||
return true;
|
||||
}
|
||||
|
||||
return new PromiseA(function (resolve) {
|
||||
var walker = walk.walk(trueRoot)
|
||||
;
|
||||
|
||||
walker.on('nodeError', function (filepath, stat, next) {
|
||||
return new Promise(function (resolve) {
|
||||
var walker = walk.walk(trueRoot);
|
||||
walker.on("nodeError", function (filepath, stat, next) {
|
||||
//stats.forEach(function (stat) {
|
||||
if (!filter(stat.name)) {
|
||||
return;
|
||||
|
@ -60,59 +58,56 @@ function walkDir(parent, sub, opts) {
|
|||
|
||||
stat.error.path = path.join(strip(prefix, filepath), stat.name);
|
||||
files.push({
|
||||
name: stat.name
|
||||
, relativePath: strip(prefix, filepath)
|
||||
, path: path.join(strip(prefix, filepath), stat.name)
|
||||
name: stat.name,
|
||||
relativePath: strip(prefix, filepath),
|
||||
path: path.join(strip(prefix, filepath), stat.name),
|
||||
|
||||
, type: undefined
|
||||
, error: stat.error
|
||||
type: undefined,
|
||||
error: stat.error,
|
||||
});
|
||||
//});
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
walker.on('files', function (root, stats, next) {
|
||||
var dirname = strip(prefix, root)
|
||||
;
|
||||
|
||||
walker.on("files", function (root, stats, next) {
|
||||
var dirname = strip(prefix, root);
|
||||
function eachFile(stat) {
|
||||
var file
|
||||
;
|
||||
var file;
|
||||
|
||||
if (!filter(stat.name)) {
|
||||
return PromiseA.resolve();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
file = {
|
||||
name: stat.name
|
||||
, relativePath: dirname
|
||||
, path: path.join(dirname, stat.name)
|
||||
name: stat.name,
|
||||
relativePath: dirname,
|
||||
path: path.join(dirname, stat.name),
|
||||
|
||||
, createdDate: (stat.birthtime||stat.ctime).toISOString()
|
||||
, lastModifiedDate: stat.mtime.toISOString()
|
||||
createdDate: (stat.birthtime || stat.ctime).toISOString(),
|
||||
lastModifiedDate: stat.mtime.toISOString(),
|
||||
|
||||
, size: stat.size
|
||||
, type: undefined // TODO include mimetype
|
||||
size: stat.size,
|
||||
type: undefined, // TODO include mimetype
|
||||
};
|
||||
files.push(file);
|
||||
|
||||
if (!(opts.sha1sum || opts.content)) {
|
||||
return PromiseA.resolve();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// TODO stream sha1 (for assets)
|
||||
return fs.readFileAsync(path.join(root, stat.name), null).then(function (buffer) {
|
||||
var contents = buffer.toString('utf8')
|
||||
;
|
||||
return fs
|
||||
.readFile(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);
|
||||
file.type = undefined;
|
||||
|
||||
if (opts.contents) {
|
||||
file.contents = contents;
|
||||
}
|
||||
});
|
||||
if (opts.contents) {
|
||||
file.contents = contents;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!opts.contents) {
|
||||
|
@ -125,7 +120,7 @@ function walkDir(parent, sub, opts) {
|
|||
}
|
||||
});
|
||||
|
||||
walker.on('end', function () {
|
||||
walker.on("end", function () {
|
||||
resolve(files);
|
||||
});
|
||||
});
|
||||
|
@ -134,9 +129,7 @@ function walkDir(parent, sub, opts) {
|
|||
function walkDirs(parent, subs, opts) {
|
||||
opts = opts || {};
|
||||
|
||||
var collections = {}
|
||||
;
|
||||
|
||||
var collections = {};
|
||||
return forEachAsync(subs, function (sub) {
|
||||
return walkDir(parent, sub, opts).then(function (results) {
|
||||
collections[sub] = results;
|
||||
|
@ -146,59 +139,53 @@ function walkDirs(parent, subs, opts) {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
function getfs(blogdir, filepaths) {
|
||||
var files = []
|
||||
;
|
||||
|
||||
var files = [];
|
||||
return forEachAsync(filepaths, function (filepath) {
|
||||
var pathname = safeResolve(blogdir, filepath)
|
||||
;
|
||||
var pathname = safeResolve(blogdir, filepath);
|
||||
return fs
|
||||
.lstat(pathname)
|
||||
.then(function (stat) {
|
||||
return fs.readFile(pathname, null).then(function (buffer) {
|
||||
files.push({
|
||||
name: path.basename(pathname),
|
||||
relativePath: path.dirname(filepath),
|
||||
path: filepath,
|
||||
|
||||
return fs.lstatAsync(pathname).then(function (stat) {
|
||||
return fs.readFileAsync(pathname, null).then(function (buffer) {
|
||||
createdDate: (stat.birthtime || stat.ctime).toISOString(),
|
||||
lastModifiedDate: stat.mtime.toISOString(),
|
||||
|
||||
files.push({
|
||||
name: path.basename(pathname)
|
||||
, relativePath: path.dirname(filepath)
|
||||
, path: filepath
|
||||
|
||||
, createdDate: (stat.birthtime||stat.ctime).toISOString()
|
||||
, lastModifiedDate: stat.mtime.toISOString()
|
||||
|
||||
, contents: buffer.toString('utf8')
|
||||
, size: buffer.length
|
||||
, sha1: sha1sum(buffer)
|
||||
, type: undefined
|
||||
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 () {
|
||||
return files;
|
||||
});
|
||||
}
|
||||
|
||||
function makeAllDirs(dirpaths) {
|
||||
var errors = []
|
||||
;
|
||||
|
||||
var errors = [];
|
||||
return forEachAsync(dirpaths, function (pathname) {
|
||||
return mkdirp(pathname).catch(function (e) {
|
||||
return fs.mkdir(pathname, { recursive: true }).catch(function (e) {
|
||||
// TODO exclude attempting to write files to this dir?
|
||||
errors.push({
|
||||
type: 'directory'
|
||||
type: "directory",
|
||||
|
||||
, directory: pathname
|
||||
directory: pathname,
|
||||
|
||||
, message: e.message
|
||||
, code: e.code
|
||||
, errno: e.errno
|
||||
, status: e.status
|
||||
, syscall: e.syscall
|
||||
message: e.message,
|
||||
code: e.code,
|
||||
errno: e.errno,
|
||||
status: e.status,
|
||||
syscall: e.syscall,
|
||||
});
|
||||
|
||||
});
|
||||
}).then(function () {
|
||||
return errors;
|
||||
|
@ -207,156 +194,162 @@ function makeAllDirs(dirpaths) {
|
|||
|
||||
function copyfs(blogdir, files) {
|
||||
// TODO switch format to { source: ..., dest: ..., opts: ... } ?
|
||||
var results = { errors: [] }
|
||||
, dirpaths = {}
|
||||
, sources = Object.keys(files)
|
||||
;
|
||||
|
||||
var results = { errors: [] },
|
||||
dirpaths = {},
|
||||
sources = Object.keys(files);
|
||||
return forEachAsync(sources, function (source) {
|
||||
/*
|
||||
var nsource = safeResolve(blogdir, source)
|
||||
;
|
||||
*/
|
||||
|
||||
var dest = safeResolve(blogdir, files[source])
|
||||
, pathname = path.dirname(dest)
|
||||
//, filename = path.basename(dest)
|
||||
;
|
||||
|
||||
var dest = safeResolve(blogdir, files[source]),
|
||||
pathname = path.dirname(dest);
|
||||
//, filename = path.basename(dest)
|
||||
dirpaths[pathname] = true;
|
||||
|
||||
return PromiseA.resolve();
|
||||
}).then(function () {
|
||||
// TODO is it better to do this lazy-like or as a batch?
|
||||
// I figure as batch when there may be hundreds of files,
|
||||
// likely within 2 or 3 directories
|
||||
return makeAllDirs(Object.keys(dirpaths)).then(function (errors) {
|
||||
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
|
||||
return Promise.resolve();
|
||||
})
|
||||
.then(function () {
|
||||
// TODO is it better to do this lazy-like or as a batch?
|
||||
// I figure as batch when there may be hundreds of files,
|
||||
// likely within 2 or 3 directories
|
||||
return makeAllDirs(Object.keys(dirpaths)).then(function (errors) {
|
||||
errors.forEach(function (e) {
|
||||
results.errors.push(e);
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
// TODO allow delete?
|
||||
return forEachAsync(sources, function (source) {
|
||||
return copyAll(
|
||||
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) {
|
||||
options = options || {};
|
||||
|
||||
var putfsResults = { errors: [] }
|
||||
, dirpaths = {}
|
||||
;
|
||||
|
||||
var putfsResults = { errors: [] },
|
||||
dirpaths = {};
|
||||
return forEachAsync(files, function (file) {
|
||||
var filepath = safeResolve(blogdir, file.path || path.join(file.relativePath, file.name))
|
||||
, pathname = path.dirname(filepath)
|
||||
, filename = file.name || path.basename(filepath)
|
||||
;
|
||||
|
||||
var filepath = safeResolve(
|
||||
blogdir,
|
||||
file.path || path.join(file.relativePath, file.name)
|
||||
),
|
||||
pathname = path.dirname(filepath),
|
||||
filename = file.name || path.basename(filepath);
|
||||
file.realPath = filepath;
|
||||
file.name = filename;
|
||||
|
||||
dirpaths[pathname] = true;
|
||||
|
||||
return PromiseA.resolve();
|
||||
}).then(function () {
|
||||
// TODO is it better to do this lazy-like or as a batch?
|
||||
// I figure as batch when there may be hundreds of files,
|
||||
// likely within 2 or 3 directories
|
||||
return forEachAsync(Object.keys(dirpaths), function (pathname) {
|
||||
return mkdirp(pathname).catch(function (e) {
|
||||
// TODO exclude attempting to write files to this dir?
|
||||
putfsResults.errors.push({
|
||||
type: 'directory'
|
||||
|
||||
, directory: pathname
|
||||
return Promise.resolve();
|
||||
})
|
||||
.then(function () {
|
||||
// TODO is it better to do this lazy-like or as a batch?
|
||||
// I figure as batch when there may be hundreds of files,
|
||||
// likely within 2 or 3 directories
|
||||
return forEachAsync(Object.keys(dirpaths), function (pathname) {
|
||||
return fs.mkdir(pathname, { recursive: true }).catch(function (e) {
|
||||
// TODO exclude attempting to write files to this dir?
|
||||
putfsResults.errors.push({
|
||||
type: "directory",
|
||||
|
||||
, message: e.message
|
||||
, code: e.code
|
||||
, errno: e.errno
|
||||
, status: e.status
|
||||
, syscall: e.syscall
|
||||
});
|
||||
directory: pathname,
|
||||
|
||||
});
|
||||
});
|
||||
}).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
|
||||
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
|
||||
return fs
|
||||
.access(file.realPath)
|
||||
.then(function () {
|
||||
if (file.delete || !file.contents) {
|
||||
return fs.unlink(file.realPath);
|
||||
}
|
||||
|
||||
if (false === options.replace || false === options.overwrite) {
|
||||
throw new Error("EEXIST: the file already exists");
|
||||
}
|
||||
|
||||
return fs.writeFile(file.realPath, file.contents, "utf8");
|
||||
})
|
||||
.catch(function () {
|
||||
return fs.writeFile(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) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
'use strict';
|
||||
"use strict";
|
||||
|
||||
exports.fsapi = require('./fsapi');
|
||||
exports.sha1sum = require('./sha1sum').sha1sum;
|
||||
exports.realFsapi = require('./fsapi-real');
|
||||
exports.fsapi = require("./fsapi");
|
||||
exports.sha1sum = require("./sha1sum").sha1sum;
|
||||
exports.realFsapi = require("./fsapi-real");
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
'use strict';
|
||||
"use strict";
|
||||
|
||||
var PromiseA = require('bluebird').Promise
|
||||
, secretutils = require('secret-utils')
|
||||
;
|
||||
var secretutils = require("secret-utils");
|
||||
|
||||
module.exports.sha1sum = function (str) {
|
||||
return PromiseA.resolve( secretutils.hashsum('sha1', str) );
|
||||
return Promise.resolve(secretutils.hashsum("sha1", str));
|
||||
};
|
||||
|
|
|
@ -1,39 +1,35 @@
|
|||
/*jshint -W054 */
|
||||
;(function (exports) {
|
||||
'use strict';
|
||||
(function (exports) {
|
||||
"use strict";
|
||||
|
||||
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(
|
||||
function renderMd(contentstr /*, desi*/) {
|
||||
var markitdown = (exports.markdownit || require("markdown-it"))({
|
||||
html: true,
|
||||
linkify: true,
|
||||
});
|
||||
return Promise.resolve(
|
||||
markitdown.render(contentstr)
|
||||
//.replace('"', '"')
|
||||
//.replace(''', "'")
|
||||
//.replace('/', '/')
|
||||
//.replace('"', '"')
|
||||
//.replace(''', "'")
|
||||
//.replace('/', '/')
|
||||
);
|
||||
}
|
||||
|
||||
function renderNoop(contentstr/*, desi*/) {
|
||||
function renderNoop(contentstr /*, desi*/) {
|
||||
// hmmm... that was easy
|
||||
return PromiseA.resolve(contentstr);
|
||||
return Promise.resolve(contentstr);
|
||||
}
|
||||
|
||||
function renderJade(contentstr, desi, options) {
|
||||
options = options || {};
|
||||
if (!('pretty' in options)) {
|
||||
if (!("pretty" in options)) {
|
||||
options.pretty = true;
|
||||
}
|
||||
|
||||
var jade = (exports.jade || require('jade'))
|
||||
, fn = jade.compile(contentstr, options)
|
||||
, html = fn(desi)
|
||||
;
|
||||
|
||||
return PromiseA.resolve(html);
|
||||
var jade = exports.jade || require("jade"),
|
||||
fn = jade.compile(contentstr, options),
|
||||
html = fn(desi);
|
||||
return Promise.resolve(html);
|
||||
}
|
||||
|
||||
exports.DesiraeRenderMarkdown = renderMd.DesiraeRenderMarkdown = renderMd;
|
||||
|
@ -41,4 +37,4 @@
|
|||
exports.DesiraeRenderCss = renderNoop.DesiraeRenderCss = renderNoop;
|
||||
exports.DesiraeRenderJs = renderNoop.DesiraeRenderJs = renderNoop;
|
||||
exports.DesiraeRenderJade = renderJade.DesiraeRenderJade = renderJade;
|
||||
}('undefined' !== typeof exports && exports || window));
|
||||
})(("undefined" !== typeof exports && exports) || window);
|
||||
|
|
|
@ -1,143 +1,153 @@
|
|||
/*jshint -W054 */
|
||||
;(function (exports) {
|
||||
'use strict';
|
||||
|
||||
var cores = {}
|
||||
, Desi = exports.Desirae || require('desirae').Desirae
|
||||
, path = exports.path || require('path')
|
||||
;
|
||||
(function (exports) {
|
||||
"use strict";
|
||||
|
||||
var cores = {},
|
||||
Desi = exports.Desirae || require("desirae").Desirae,
|
||||
path = exports.path || require("path");
|
||||
cores.lint = function (desi, env, collection, entity) {
|
||||
// TODO splice
|
||||
//desi.content.collections = desi.content.collections.filter(function (entity) {
|
||||
// TODO throw for any files that don't have a registered renderer
|
||||
if (!entity.yml) {
|
||||
if (!desi.config.empty_frontmatter) {
|
||||
throw new 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`.');
|
||||
}
|
||||
// TODO throw for any files that don't have a registered renderer
|
||||
if (!entity.yml) {
|
||||
if (!desi.config.empty_frontmatter) {
|
||||
throw new Error(
|
||||
"no frontmatter for " +
|
||||
(entity.path || entity.name) +
|
||||
"." +
|
||||
"Set `config.yml.empty_frontmatter: include|skip` to ignore this error."
|
||||
);
|
||||
}
|
||||
|
||||
if (!entity.body || !entity.body.trim()) {
|
||||
if (!desi.config.empty_body) {
|
||||
throw new Error('empty content file ' + (entity.path || entity.name)
|
||||
+ '. Set `config.yml.empty_body: 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 ('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`.');
|
||||
}
|
||||
if (!entity.body || !entity.body.trim()) {
|
||||
if (!desi.config.empty_body) {
|
||||
throw new Error(
|
||||
"empty content file " +
|
||||
(entity.path || entity.name) +
|
||||
". Set `config.yml.empty_body: include|skip` to ignore this error."
|
||||
);
|
||||
}
|
||||
|
||||
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) {
|
||||
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,
|
||||
// so we just go ahead and define that here
|
||||
if (/^index\.\w+$/.test(entity.path)) {
|
||||
entity.permalink = '/';
|
||||
entity.permalink = "/";
|
||||
} 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.push(entity.permalink.replace(/\/$/, '/index.html'));
|
||||
entity.redirects.push(entity.permalink.replace(/\/$/, "/index.html"));
|
||||
}
|
||||
};
|
||||
|
||||
cores.normalize = function (desi, env, collection, entity) {
|
||||
entity.title = entity.yml.title || Desi.firstCap(entity.name.replace(/\.\w+$/, ''));
|
||||
entity.date = entity.yml.date;
|
||||
entity.title =
|
||||
entity.yml.title || Desi.firstCap(entity.name.replace(/\.\w+$/, ""));
|
||||
entity.date = entity.yml.date;
|
||||
|
||||
if (!entity.date) {
|
||||
// TODO tell YAML parser to keep the date a string
|
||||
entity.date = new Date(entity.yml.created_at
|
||||
|| entity.yml.time
|
||||
|| entity.yml.updated_at
|
||||
|| entity.createdDate
|
||||
|| entity.lastModifiedDate
|
||||
entity.date = new Date(
|
||||
entity.yml.created_at ||
|
||||
entity.yml.time ||
|
||||
entity.yml.updated_at ||
|
||||
entity.createdDate ||
|
||||
entity.lastModifiedDate
|
||||
).toISOString();
|
||||
}
|
||||
if ('object' === typeof entity.date) {
|
||||
if ("object" === typeof entity.date) {
|
||||
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.year = entity.published_at.year;
|
||||
entity.month = entity.published_at.month;
|
||||
entity.day = entity.published_at.day;
|
||||
entity.hour = entity.published_at.hour;
|
||||
entity.twelve_hour = entity.published_at.twelve_hour;
|
||||
entity.meridian = entity.published_at.meridian;
|
||||
entity.minute = entity.published_at.minute;
|
||||
entity.published_at = Desi.fromLocaleDate(
|
||||
entity.date || entity.lastModifiedDate
|
||||
);
|
||||
entity.year = entity.published_at.year;
|
||||
entity.month = entity.published_at.month;
|
||||
entity.day = entity.published_at.day;
|
||||
entity.hour = entity.published_at.hour;
|
||||
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
|
||||
//entity.second = entity.published_at.second;
|
||||
|
||||
entity.slug = Desi.slugify(entity.title);
|
||||
entity.slug_path = Desi.slugifyPath(entity.relativePath);
|
||||
entity.slugPath = Desi.slugifyPath(entity.relativePath);
|
||||
entity.slug = Desi.slugify(entity.title);
|
||||
entity.slug_path = Desi.slugifyPath(entity.relativePath);
|
||||
entity.slugPath = Desi.slugifyPath(entity.relativePath);
|
||||
// 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
|
||||
if (Array.isArray(entity.yml.categories)) {
|
||||
entity.categories = entity.yml.categories;
|
||||
}
|
||||
else if ('string' === typeof entity.yml.categories) {
|
||||
entity.categories = [entity.yml.categories];
|
||||
}
|
||||
else if ('string' === typeof entity.yml.category) {
|
||||
entity.categories = [entity.yml.category];
|
||||
}
|
||||
else {
|
||||
entity.categories = [];
|
||||
entity.categories = entity.yml.categories;
|
||||
} else if ("string" === typeof entity.yml.categories) {
|
||||
entity.categories = [entity.yml.categories];
|
||||
} else if ("string" === typeof entity.yml.category) {
|
||||
entity.categories = [entity.yml.category];
|
||||
} else {
|
||||
entity.categories = [];
|
||||
}
|
||||
|
||||
// tags
|
||||
if (Array.isArray(entity.yml.tags)) {
|
||||
entity.tags = entity.yml.tags;
|
||||
}
|
||||
else if ('string' === typeof entity.yml.tags) {
|
||||
entity.tags = [entity.yml.tags];
|
||||
}
|
||||
else {
|
||||
entity.tags = [];
|
||||
entity.tags = entity.yml.tags;
|
||||
} else if ("string" === typeof entity.yml.tags) {
|
||||
entity.tags = [entity.yml.tags];
|
||||
} else {
|
||||
entity.tags = [];
|
||||
}
|
||||
|
||||
entity.permalink = entity.permalink || entity.yml.permalink;
|
||||
entity.permalink = entity.permalink || entity.yml.permalink;
|
||||
|
||||
if (!entity.permalink) {
|
||||
// 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)
|
||||
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)) {
|
||||
entity.htmllink = path.join(entity.permalink, 'index.html');
|
||||
|
@ -145,38 +155,51 @@
|
|||
*/
|
||||
|
||||
// relative to the site
|
||||
entity.relative_file = path.join(env.base_path, entity.permalink)
|
||||
.replace(/\/$/, '/index.html');
|
||||
entity.relative_href = path.join(env.base_path, entity.permalink)
|
||||
.replace(/\/index\.html$/, '/');
|
||||
entity.relative_link = entity.relative_href;
|
||||
entity.relative_file = path
|
||||
.join(env.base_path, entity.permalink)
|
||||
.replace(/\/$/, "/index.html");
|
||||
entity.relative_href = path
|
||||
.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)
|
||||
.replace(/\/index\.html$/, '/');
|
||||
entity.canonical_url = env.base_url + path.join(env.base_path, entity.permalink)
|
||||
.replace(/\/index\.html$/, '/');
|
||||
entity.production_url = 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$/, '/');
|
||||
entity.url =
|
||||
env.base_url +
|
||||
path.join(env.base_path, entity.permalink).replace(/\/index\.html$/, "/");
|
||||
entity.canonical_url =
|
||||
env.base_url +
|
||||
path.join(env.base_path, entity.permalink).replace(/\/index\.html$/, "/");
|
||||
entity.production_url =
|
||||
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
|
||||
['url', 'canonical_url', 'production_url', 'relative_url'].forEach(function (url) {
|
||||
entity[url] = entity[url].replace(/\/$/, '/index.html');
|
||||
});
|
||||
["url", "canonical_url", "production_url", "relative_url"].forEach(
|
||||
function (url) {
|
||||
entity[url] = entity[url].replace(/\/$/, "/index.html");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// i.e. bootstrap, hero page, darkly
|
||||
entity.theme = entity.theme || entity.yml.theme;
|
||||
entity.layout = entity.layout || entity.yml.layout;
|
||||
entity.swatch = entity.swatch || entity.yml.swatch;
|
||||
entity.theme = entity.theme || entity.yml.theme;
|
||||
entity.layout = entity.layout || entity.yml.layout;
|
||||
entity.swatch = entity.swatch || entity.yml.swatch;
|
||||
};
|
||||
|
||||
cores.disqus = function (desi, env, collection, entity) {
|
||||
var yml = entity.yml
|
||||
;
|
||||
|
||||
var yml = entity.yml;
|
||||
if (yml.uuid) {
|
||||
entity.disqus_identifier = yml.uuid;
|
||||
}
|
||||
|
@ -184,4 +207,4 @@
|
|||
};
|
||||
|
||||
exports.DesiraeTransformCore = cores.DesiraeTransformCore = cores;
|
||||
}('undefined' !== typeof exports && exports || window));
|
||||
})(("undefined" !== typeof exports && exports) || window);
|
||||
|
|
101
lib/utils.js
101
lib/utils.js
|
@ -1,9 +1,7 @@
|
|||
;(function (exports) {
|
||||
'use strict';
|
||||
|
||||
var path = exports.path || require('path')
|
||||
;
|
||||
(function (exports) {
|
||||
"use strict";
|
||||
|
||||
var path = exports.path || require("path");
|
||||
function escapeRegExp(str) {
|
||||
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
||||
}
|
||||
|
@ -11,10 +9,8 @@
|
|||
function safeResolve(basename, target) {
|
||||
basename = path.resolve(basename);
|
||||
|
||||
var targetname = path.resolve(basename, target)
|
||||
, re = new RegExp('^' + escapeRegExp(basename) + '(/|$)')
|
||||
;
|
||||
|
||||
var targetname = path.resolve(basename, target),
|
||||
re = new RegExp("^" + escapeRegExp(basename) + "(/|$)");
|
||||
return re.test(targetname) && targetname;
|
||||
}
|
||||
|
||||
|
@ -22,29 +18,24 @@
|
|||
exports.escapeRegExp = escapeRegExp;
|
||||
|
||||
function create(Desi) {
|
||||
var fsapi = Desi.fsapi || require('./node-adapters').fsapi
|
||||
;
|
||||
|
||||
var fsapi = Desi.fsapi || require("./node-adapters").fsapi;
|
||||
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) {
|
||||
var obj = {}
|
||||
;
|
||||
|
||||
var obj = {};
|
||||
Object.keys(collections).forEach(function (key) {
|
||||
var files = collections[key]
|
||||
, keyname = key.replace(/\.(json|ya?ml|\/)$/i, '')
|
||||
;
|
||||
|
||||
var files = collections[key],
|
||||
keyname = key.replace(/\.(json|ya?ml|\/)$/i, "");
|
||||
obj[keyname] = obj[keyname] || {};
|
||||
|
||||
files.forEach(function (file) {
|
||||
var filename = file.name.replace(/\.(json|ya?ml)$/i, '')
|
||||
, data = {}
|
||||
;
|
||||
|
||||
var filename = file.name.replace(/\.(json|ya?ml)$/i, ""),
|
||||
data = {};
|
||||
if (file.error) {
|
||||
console.error(file);
|
||||
console.error(file.error);
|
||||
|
@ -57,17 +48,16 @@
|
|||
if ("undefined" === data) {
|
||||
data = {};
|
||||
}
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
data = { error: e };
|
||||
console.error("Could not parse yaml for " + filename);
|
||||
console.error(file);
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
else if (/\.(json)$/i.test(file.name)) {
|
||||
} else if (/\.(json)$/i.test(file.name)) {
|
||||
try {
|
||||
data = JSON.parse(file.contents) || {};
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
data = { error: e };
|
||||
console.error("Could not parse json for " + filename);
|
||||
console.error(file);
|
||||
|
@ -99,36 +89,34 @@
|
|||
};
|
||||
|
||||
fsapi.getAllPartials = function () {
|
||||
return fsapi.getConfigs(['partials', 'partials.yml']).then(function (results) {
|
||||
var partials = {}
|
||||
;
|
||||
return fsapi
|
||||
.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) {
|
||||
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];
|
||||
partials[prop] = partial[prop];
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return partials;
|
||||
});
|
||||
return partials;
|
||||
});
|
||||
};
|
||||
|
||||
fsapi.getAllConfigFiles = function () {
|
||||
return fsapi.getConfigs(['config.yml', 'site.yml', 'authors']).then(function (results) {
|
||||
var authors = results.authors
|
||||
, config = results.config.config
|
||||
, site = results.site.site
|
||||
;
|
||||
|
||||
return { config: config, authors: authors, site: site };
|
||||
});
|
||||
return fsapi
|
||||
.getConfigs(["config.yml", "site.yml", "authors"])
|
||||
.then(function (results) {
|
||||
var authors = results.authors,
|
||||
config = results.config.config,
|
||||
site = results.site.site;
|
||||
return { config: config, authors: authors, site: site };
|
||||
});
|
||||
};
|
||||
|
||||
return exports;
|
||||
|
@ -136,8 +124,7 @@
|
|||
|
||||
if (exports.Desirae) {
|
||||
create(exports.Desirae);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
exports.create = create;
|
||||
}
|
||||
}('undefined' !== typeof exports && exports || window));
|
||||
})(("undefined" !== typeof exports && exports) || window);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
;(function (exports) {
|
||||
'use strict';
|
||||
(function (exports) {
|
||||
"use strict";
|
||||
|
||||
exports.verifyConfig = function (conf) {
|
||||
if (!conf.NuhohSpec) {
|
||||
|
@ -28,14 +28,18 @@
|
|||
|
||||
if (!Array.isArray(conf.collections)) {
|
||||
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'.");
|
||||
}
|
||||
|
||||
if (!conf.themes) {
|
||||
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'");
|
||||
}
|
||||
|
@ -55,4 +59,4 @@
|
|||
throw new Error("missing key root");
|
||||
}
|
||||
};
|
||||
}('undefined' !== typeof exports && exports || window));
|
||||
})(("undefined" !== typeof exports && exports) || window);
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
{
|
||||
"name": "desirae",
|
||||
"version": "0.12.4",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"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="
|
||||
},
|
||||
"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="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
package.json
19
package.json
|
@ -1,14 +1,15 @@
|
|||
{
|
||||
"name": "desirae",
|
||||
"version": "0.11.2",
|
||||
"version": "0.12.4",
|
||||
"description": "An in-browser static blog library and static site generator. Similar to Jekyll, Octopress, Nanoc, etc",
|
||||
"main": "desirae.js",
|
||||
"scripts": {
|
||||
"prettier": "prettier --write './**/*.{js,css,html,json,md,py,xml}'",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@git.daplie.com:Daplie/desirae.git"
|
||||
"type": "git+https",
|
||||
"url": "https://git.coolaj86.com/coolaj86/desirae.js.git"
|
||||
},
|
||||
"keywords": [
|
||||
"dear",
|
||||
|
@ -22,21 +23,19 @@
|
|||
"octopress",
|
||||
"nanoc"
|
||||
],
|
||||
"author": "AJ ONeal",
|
||||
"license": "Apache2",
|
||||
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
|
||||
"license": "MPL-2.0",
|
||||
"bugs": {
|
||||
"url": "https://git.daplie.com/Daplie/desirae/issues"
|
||||
"url": "https://git.coolaj86.com/coolaj86/desirae.js/issues"
|
||||
},
|
||||
"homepage": "https://git.daplie.com/Daplie/desirae",
|
||||
"homepage": "https://git.coolaj86.com/coolaj86/desirae.js",
|
||||
"dependencies": {
|
||||
"bluebird": "^2.5.3",
|
||||
"escape-string-regexp": "^1.0.2",
|
||||
"foreachasync": "^5.0.5",
|
||||
"fs.extra": "^1.3.2",
|
||||
"js-yaml": "^3.2.5",
|
||||
"markdown-it": "^3.0.2",
|
||||
"mkdirp": "^0.5.0",
|
||||
"mustache": "^1.0.0",
|
||||
"mustache": "^2.3.2",
|
||||
"node-uuid": "^1.4.2",
|
||||
"secret-utils": "^1.0.2",
|
||||
"walk": "^2.3.9"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
'use strict';
|
||||
"use strict";
|
||||
|
||||
// http://ruhoh.com/docs/2/pages/#toc_41
|
||||
/*
|
||||
|
@ -6,32 +6,31 @@
|
|||
Otherwise it should use the the permalink for that collection.
|
||||
*/
|
||||
|
||||
var tags
|
||||
, permalinkTransforms
|
||||
, cases
|
||||
, path = /*exports.path ||*/ require('path')
|
||||
;
|
||||
|
||||
var tags,
|
||||
permalinkTransforms,
|
||||
cases,
|
||||
path = /*exports.path ||*/ require("path");
|
||||
tags = {
|
||||
year: "Year from the page’s filename"
|
||||
, month: "Month from the page’s filename"
|
||||
, day: "Day from the page’s filename"
|
||||
, 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."
|
||||
, 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."
|
||||
, i_month: "Month from the page’s filename without leading zeros."
|
||||
, i_day: "Day from the page’s filename without leading zeros."
|
||||
, title: "The title, as a slug."
|
||||
, slug: "alias of title"
|
||||
, name: "alias of title"
|
||||
, collection: "i.e. posts/ or essays/ or whatever/"
|
||||
year: "Year from the page’s filename",
|
||||
month: "Month from the page’s filename",
|
||||
day: "Day from the page’s filename",
|
||||
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.",
|
||||
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.",
|
||||
i_month: "Month from the page’s filename without leading zeros.",
|
||||
i_day: "Day from the page’s filename without leading zeros.",
|
||||
title: "The title, as a slug.",
|
||||
slug: "alias of title",
|
||||
name: "alias of title",
|
||||
collection: "i.e. posts/ or essays/ or whatever/",
|
||||
};
|
||||
|
||||
function pad(str, n) {
|
||||
str = str.toString();
|
||||
if (str.length < n) {
|
||||
str = '0' + str;
|
||||
str = "0" + str;
|
||||
}
|
||||
|
||||
return str;
|
||||
|
@ -39,49 +38,51 @@ function pad(str, n) {
|
|||
|
||||
// https://www.youtube.com/watch?v=1NryFD9_hR0&list=RDOeLUK4a6Ojc&index=2
|
||||
cases = {
|
||||
"/:title.html" : "/my-title.html"
|
||||
, ":title/" : "/my-title/"
|
||||
, "/:bad/:title/" : "/:bad/my-title/"
|
||||
, "/:slug/" : "/my-title/"
|
||||
, "/:path/:name.html" : "/posts/fun/my-title.html"
|
||||
, "/:relative_path/:name/" : "/fun/my-title/"
|
||||
, "/:year-:month-:day/:name" : "/2015-07-04/my-title/"
|
||||
, "/:year/:i_month/:i_day/:name" : "/2015/7/4/my-title/"
|
||||
, "/:filename.html" : "/my-file-name.html"
|
||||
, "/:filename" : "/my-file-name/"
|
||||
, "/:filename/" : "/my-file-name/"
|
||||
, "/:collection/:title/" : "/posts/my-title/"
|
||||
, "/:collection/:filename" : "/posts/my-file-name/"
|
||||
, "/:something/:or/:other" : "/:something/:or/:other/"
|
||||
, "/:categories/:title/" : "/desi/my-title/"
|
||||
"/:title.html": "/my-title.html",
|
||||
":title/": "/my-title/",
|
||||
"/:bad/:title/": "/:bad/my-title/",
|
||||
"/:slug/": "/my-title/",
|
||||
"/:path/:name.html": "/posts/fun/my-title.html",
|
||||
"/:relative_path/:name/": "/fun/my-title/",
|
||||
"/:year-:month-:day/:name": "/2015-07-04/my-title/",
|
||||
"/:year/:i_month/:i_day/:name": "/2015/7/4/my-title/",
|
||||
"/:filename.html": "/my-file-name.html",
|
||||
"/:filename": "/my-file-name/",
|
||||
"/:filename/": "/my-file-name/",
|
||||
"/:collection/:title/": "/posts/my-title/",
|
||||
"/:collection/:filename": "/posts/my-file-name/",
|
||||
"/:something/:or/:other": "/:something/:or/:other/",
|
||||
"/:categories/:title/": "/desi/my-title/",
|
||||
};
|
||||
|
||||
Object.keys(cases).forEach(function (tpl) {
|
||||
var entity
|
||||
, tpld
|
||||
;
|
||||
var entity, tpld;
|
||||
|
||||
entity = {
|
||||
year : '2015'
|
||||
, month : '07'
|
||||
, day : '04'
|
||||
, title : "My Title"
|
||||
, slug : "my-title"
|
||||
, name : "My-File-Name.html"
|
||||
, relativePath : "posts/fun"
|
||||
, path : "posts/fun/My-File-Name.html"
|
||||
, collection : "posts"
|
||||
, yml : { categories: ['desi'] }
|
||||
year: "2015",
|
||||
month: "07",
|
||||
day: "04",
|
||||
title: "My Title",
|
||||
slug: "my-title",
|
||||
name: "My-File-Name.html",
|
||||
relativePath: "posts/fun",
|
||||
path: "posts/fun/My-File-Name.html",
|
||||
collection: "posts",
|
||||
yml: { categories: ["desi"] },
|
||||
};
|
||||
|
||||
tpld = permalinker(tpl, entity);
|
||||
|
||||
if (cases[tpl] !== tpld) {
|
||||
console.error('[ERROR]');
|
||||
console.error(tpl + ' ' + tpld + ' ' + cases[tpl]);
|
||||
console.error("[ERROR]");
|
||||
console.error(tpl + " " + tpld + " " + cases[tpl]);
|
||||
throw new Error(
|
||||
"Did not template permalink correctly. "
|
||||
+ tpl + ' ' + tpld + ' ' + cases[tpl]
|
||||
"Did not template permalink correctly. " +
|
||||
tpl +
|
||||
" " +
|
||||
tpld +
|
||||
" " +
|
||||
cases[tpl]
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue