npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

desirae

v0.12.4

Published

An in-browser static blog library and static site generator. Similar to Jekyll, Octopress, Nanoc, etc

Downloads

23

Readme

Did you mean DearDesi?

If you're looking for DearDesi, the DIY blog platform for normal people 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 is a static webpage compiler written in JavaScript.

It can run entirely in the browser (with a little help from a minimal just to serve to read and save files).

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).

Installation

bower install --save desirae

npm install --save desirae

Why

Because I hate ruby.

Well, I don't hate it, but it hates me. Or at least it moves too fast and has too many breaking changes between updates.

Anyway, I had to drop ruhoh because I made a new year's resolution to blog more (last year I made dozens of gists and 0 blog posts) and I just couldn't get the right ruby versions and gems and whatnot... so I gave up and wrote my own (because I needed something compatible with ruhoh).

Usage Overview

Before we get started

(disclaimers)

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 the server in mind. If you can't respect that, don't look at the code. :-)

NOTE: The mixing of snake_case with camelCase is somewhat intentional. It's an artifact of this project being born out of the ashes of my ruhoh blog, which is built in ruby and uses YAML.

Getting Started

First off you need to declare a state object that will be used in every desirae action.

var desi = {};

After that you'll load any plugins you need.

Here's how you would load all of the common plugins:

// load the module whether in browser or node
function dload(filename, exportname) {
  return dload('undefined' !== typeof window && window[exportname] || require(filename)[exportname];
}
//
// 1. Transform (yml, slug, etc)
//
Desi.registerTransform(
  'lint'
, dload('desirae/lib/transform-core', 'DesiraeTransformCore').lint
, { collections: true }
);
Desi.registerTransform(
  'root'
, dload('desirae/lib/transform-core', 'DesiraeTransformCore').root
, { root: true }
);
Desi.registerTransform(
  'normalize'
, dload('desirae/lib/transform-core', 'DesiraeTransformCore').normalize
, { root: true, collections: true }
);
Desi.registerTransform(
  'disqus'
, dload('desirae/lib/transform-core', 'DesiraeTransformCore').disqus
, { collections: true }
);

//
// 2. Register Aggregators (rss, categories, tags, etc)
//
Desi.registerAggregator(dload('desirae/lib/aggregate-core', 'DesiraeAggregateCore').collate);

//
// 3. Register Datamappers (ruhoh, desirae, jade, mustache, liquid)
//
Desi.registerDataMapper('desirae', dload('desirae/lib/datamap-core', 'DesiraeDatamapCore'));
Desi.registerDataMapper('[email protected]', dload('desirae/lib/datamap-core', 'DesiraeDatamapCore'));
Desi.registerDataMapper('ruhoh', dload('desirae-datamap-ruhoh', 'DesiraeDatamapRuhoh'));
Desi.registerDataMapper('[email protected]', dload('desirae-datamap-ruhoh', 'DesiraeDatamapRuhoh'));
Desi.registerDataMapper('[email protected]', dload('desirae-datamap-ruhoh', 'DesiraeDatamapRuhoh'));

//
// 4. Register Renderers (md -> html, less -> css, etc)
//
Desi.registerRenderer(
  'js'
, dload('desirae/lib/render-core', 'DesiraeRenderJs')
, { themes: true, assets: true }
);
Desi.registerRenderer(
  'css'
, dload('desirae/lib/render-core', 'DesiraeRenderCss')
, { themes: true, assets: true }
);
Desi.registerRenderer(
  'html'
, dload('desirae/lib/render-core', 'DesiraeRenderHtml')
, { root: true, collections: true, themes: true, assets: true }
);
['md', 'markdown'].forEach(function (ext) {
  Desi.registerRenderer(
    ext
  , dload('desirae/lib/render-core', 'DesiraeRenderMarkdown')
  , { root: true, collections: true }
  );
});
Desi.registerRenderer(
  'jade'
, dload('desirae/lib/render-core', 'DesiraeRenderJade')
, { root: true, collections: true, themes: true }
);

And then you'll initialize Desirae with an environment.

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);
  },

  // io.js / node.js only
  working_path: "./path/to/blog",
}).then(function () {
  console.log("Desirae is initialized");
});

Using the paths specified in the environment it will read the appropriate config.yml, site.yml, and authors/*.yml files to initialize itself.

Then you can specify to build the static blog. You'll need to pass the environment again.

Desirae.buildAll(desi, env).then(function () {
  console.log('Desirae built your blog!');
});

Finally, you save the built out to disk.

Desirae.write(desi, env).then(function () {
  console.log('Desirae pushd all files to the appropriate fs adapter!');
});

Plugins

You need to start every file with a wrapper that is browser and io.js/node.js compatible

/*jshint -W054 */
(function (exports) {
  "use strict";

  var DesiraeMyModule = {};
  // ... a bunch of code ...

  DesiraeMyModule.doStuff = doStuff;

  exports.DesiraeMyModule = DesiraeMyModule.DesiraeMyModule = DesiraeMyModule;
})(("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

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.

If any of these files change, the entire site needs to be retemplated.

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) and still pass the string along to the 'real' markdown parser afterwards.

But here's what I've got so far:

Rendering Engines

(html, markdown, jade)

  • 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

var slim = exports.slimjs || require("slimjs");
function render(contentstr /*, desi*/) {
  return Promise.resolve(slim(contentstr));
}

Desirae.registerRenderer(".slim", render);

Data Mapping

(desirae, ruhoh, etc)

  • 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 (i.e. it wants {{ page.name }} instead of {{ entity.title }}), you can use a 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 ?

Desirae.registerDataMapper("[email protected]", function (view) {
  return {
    page: {
      name: view.entity.title,
    },
    author: {
      nickname: view.author.twitter,
    },
    // ...
  };
});

The default datamapper is [email protected]

(note that that might be misnamed, I'm a little confused as to how Ruhoh's template versions correspond to Ruhoh proper versions).

Adapters

(fs, http, dropbox)

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

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 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.
[
  {
    "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"
  }
]

To retrieve multiple dir listings at once:

  • 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.
POST http://local.dear.desi:8080/api/fs/walk?dotfiles=true&extensions=md,markdown,jade,htm,html&_method=GET

{ "dirs": [ "old", "2013,12", "2013,11" ] }
{
  "posts/2015": [ { "name": ... }, { ... } ]
, "posts/2013": [ ... ]
}

GET /api/fs/files

GET http://local.dear.desi:8080/api/fs/files?path=posts/happy-new-year.md

{
  "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

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.
POST http://local.dear.desi:8080/api/fs/files?dotfiles=true&extensions=md,markdown,jade,htm,html&_method=GET

{ "paths": [ "posts/foo.md", "posts/2013,11,30.md" ] }
[
  { "path": "posts/foo.md"
  , "lastModifiedDate": "2013-08-01T22:47:37.000Z"
  , "contents": "..."
  , "sha1": "6eae3a5b062c6d0d79f070c26e6d62486b40cb46"
  }
, ...
]

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).

_method=PUT is just for funzies.

Including sha1 is optional, but recommended.

lastModifiedDate is optional and may or may not make any difference.

strict (not yet implemented) fail immediately and completely on any error

POST http://local.dear.desi:8080/api/fs/files?compiled=true&_method=PUT

{
  "files": [
    { "path": "posts/foo.md"
    , "name": "foo.md"
    , "relativePath": "posts"
    , "createdDate": "2013-08-01T22:47:37.000Z"
    , "lastModifiedDate": "2013-08-01T22:47:37.000Z"
    , "contents": "..."
    , "sha1": "6eae3a5b062c6d0d79f070c26e6d62486b40cb46"
    , "delete": false
    }
  , ...
  ]
}

The response may include errors of all shapes and sizes.

{ "error": { message: "any top-level error", ... }
, "errors": [
    { "type": "file|directory"
    , "message": "maybe couldn't create the directory, but maybe still wrote the file. Maybe not"
    }
  , ...
  ]
}

POST /api/fs/copy

{ "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/.