Skip to content

Commit

Permalink
0.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
robhrt7 committed Mar 15, 2015
1 parent dc2e594 commit 532fc3e
Show file tree
Hide file tree
Showing 5 changed files with 316 additions and 2 deletions.
4 changes: 4 additions & 0 deletions .gitignore
@@ -1,3 +1,7 @@
# IDE
*.idea
*.iml

# Logs
logs
*.log
Expand Down
123 changes: 121 additions & 2 deletions README.md
@@ -1,2 +1,121 @@
# sourcejs-contrib-dss
CSS Documentation parser integration for transporting DSS into SourceJS Specs.
# CSS Documentation support for SourceJS

Appends rendered [DSS](https://github.com/darcyclarke/DSS) documentation into SourceJS Spec pages.

[SourceJS](http://sourcejs.com) plugin (for 0.5.0+ version).

## Install

To install middleware, run npm command in `sourcejs/user` folder:

```
npm install sourcejs-contrib-dss --save
```

After restarting your app, middleware will be loaded automatically. To disable it, remove npm module and restart the app.

## Usage

DSS works as SourceJS middleware, when Spec (documentation page) is requested, plugin is searching CSS files in Spec folder by defined mask.

All found CSS (or LESS/SASS/SCSS/Stylus) will be processed through [DSS](https://github.com/darcyclarke/DSS) in runtime, during request.

Here's an example of Spec folder structure

```
specs/button
button.css
readme.md
info.json
```

`button.css` contents:

```css
/**
* @name Default
*
* @state .button-pro_big - Bigger version
*
* @markup
* <button class="button-pro">Click me</button>
*/

.button-pro {}
.button-pro.button-pro_big {}

/**
* @name Disabled
*
* @state .button-pro_big - Bigger disabled version
*
* @markup
* <button class="button-pro button-pro_disabled">Click me</button>
*/

.button-pro.button-pro_disabled {}
```

Spec rendered result (http://127.0.0.1:8080/specs/button):

![image](http://d.pr/i/GH6g+)

`readme.md` contents will be rendered at the top of Spec file. Other file types as `index.src` or `index.jade` (with [Jade plugin](https://github.com/sourcejs/sourcejs-jade)) could also be used.

**Note** that Spec file and `info.json` should be present in folder for page to load.

## Options

You can configure plugin options from `user/options.js`:

```js
{
core: {},
assets: {},
plugins: {
dss: {
targetCssMask: '**/*.{css,less,stylus,sass,scss}',
visibleCode: true,
templates: {
sections: path.join(__dirname, '../views/sections.ejs')
}
}
}
}
```

### targetCssMask

Type: `String`
Default value: `**/*.{css,less,stylus,sass,scss}`

Glob mask for searching CSS files for DSS parsing, starting from requested Spec path (https://github.com/isaacs/node-glob).

### visibleCode

Type: `Boolean`
Default: `true`

Set `source_visible` to every `src-html` code containers to show code preview by default. Set to `false` if you prefer hiding code blocks by default (toggled from menu `Show source code`).

### templates

Type: `Object`

#### templates.item

Type: `String`
Default: `path.join(__dirname, '../views/sections.ejs')`

Set path to EJS template for rendering DSS JSON result. Currently this plugin uses only one template for sections.

## Upcoming features

* DSS parser caching
* Pre-build cache (during app start)
* DSS improvements

## Other SourceJS middlewares

* https://github.com/sourcejs/sourcejs-jade
* https://github.com/sourcejs/sourcejs-smiles
139 changes: 139 additions & 0 deletions core/middleware/index.js
@@ -0,0 +1,139 @@
var dss = require('dss');
var path = require('path');
var deepExtend = require('deep-extend');
var glob = require('glob');
var fs = require('fs');
var ejs = require('ejs');
var prettyHrtime = require('pretty-hrtime');
var cheerio = require('cheerio');
var util = require('util');

// Module configuration. Public object is exposed to Front-end via options API
var config = {
enabled: true,

// Glob mask, starting from requested spec path (https://github.com/isaacs/node-glob)
targetCssMask: '**/*.{css,less,stylus,sass,scss}',

// Define if example HTML is visible by default
visibleCode: true,
templates: {
sections: path.join(__dirname, '../views/sections.ejs')
},

public: {}
};
// Overwriting base options
deepExtend(config, global.opts.plugins.dss);

/*
* Gets markup and adds custom class to all first level nodes
*
* @param {String} markup - HTML markup string
* @param {String} cssClass - CSS class to add
* */
var addClassToExample = function(markup, cssClass){
var $ = cheerio.load('<div id="content">'+ markup +'</div>');

// Add to all first level elements a state class
$('#content > *').each(function(){
$(this).addClass(cssClass);
});

return $('#content').html();
};

/*
* Dynamically render DSS into requested Spec
*
* @param {object} req - Request object
* @param {object} res - Response object
* @param {function} next - The callback function
* */
var processRequest = function (req, res, next) {
if (!config.enabled) {
next();
return;
}

if (
req.specData &&
req.specData.info.role !== 'navigation' && // Not navigation page
req.specData.renderedHtml && // Is spec and has renderedHTML in req
!(req.specData.info.plugins && req.specData.info.plugins.dss && !req.specData.info.plugins.dss.enabled) // Is not disabled per Spec
) {
var start = process.hrtime();
var specPath = path.join(global.app.get('user'), req.path);
var specDirPath = path.dirname(specPath);

glob(config.targetCssMask, {
cwd: specDirPath
}, function (err, files) {
if (err || files.length === 0) {
next();
return;
}

var sectionsTplPath = config.templates.sections;

fs.readFile(sectionsTplPath, 'utf-8', function(err, file){
if (err) {
global.log.warn('DSS template not found'+ sectionsTplPath +':', err);

next();
return;
}

var sectionsTemplate = file;
var dataForTemplates = {
config: config,
helpers: {
addClassToExample: addClassToExample
},
sections: []
};

// Gather all DSS blocks
files.forEach(function(filePath){
var fullPath = path.join(specDirPath, filePath);

try {
var file = fs.readFileSync(fullPath, 'utf-8');

dss.parse(file, {}, function(parsed) {

// Normalizing DSS parser output https://github.com/darcyclarke/DSS/issues/58
parsed.blocks.forEach(function(block){
for (var key in block) {
var value = block[key];

if (key === 'state' && !util.isArray(value)) {
block[key] = [value];
}
}
});

dataForTemplates.sections = dataForTemplates.sections.concat(parsed.blocks);
});
} catch(err) {
global.log.debug('DSS error parsing '+ fullPath +':', err);
}
});

// Update HTML
req.specData.renderedHtml += ejs.render(sectionsTemplate, dataForTemplates);

var end = process.hrtime(start);
global.log.debug('DSS processing took:', prettyHrtime(end));

next();
});


});
} else {
next();
}
};

exports.process = processRequest;
38 changes: 38 additions & 0 deletions core/views/sections.ejs
@@ -0,0 +1,38 @@
<% for (var i = 0; i < sections.length; i++) { %>
<section class="source_section">
<h2><%= sections[i].name %></h2>
<% if (sections[i].description) { %>
<p><%= sections[i].description %></p>
<% } %>
<% if (sections[i].markup) { %>
<code class="src-html <% if (config.visibleCode) { %>source_visible<% } %>">
<%- sections[i].markup.escaped %>
</code>
<div class="source_example">
<%- sections[i].markup.example %>
</div>
<% } %>
<% if (sections[i].state) { %>
<% for (var s = 0; s < sections[i].state.length; s++) { %>
<% var state = sections[i].state[s]; %>
<% var html = helpers.addClassToExample(sections[i].markup.example, state.escaped); %>
<h3><%= state.name %></h3>
<% if (state.description) { %>
<p><%= state.description %></p>
<% } %>
<code class="src-html <% if (config.visibleCode) { %>source_visible<% } %>">
<%= html %>
</code>
<div class="source_example">
<%- html %>
</div>
<% } %>
<% } %>
</section>
<% } %>
14 changes: 14 additions & 0 deletions package.json
@@ -0,0 +1,14 @@
{
"name": "sourcejs-contrib-dss",
"version": "0.1.0",
"author": "Robert Haritonov",
"description": "CSS Documentation parser integration for transporting DSS into SourceJS Specs",
"dependencies": {
"cheerio": "^0.18.0",
"deep-extend": "^0.3.2",
"dss": "^1.0.4",
"ejs": "^2.3.1",
"glob": "^5.0.3",
"pretty-hrtime": "^1.0.0"
}
}

0 comments on commit 532fc3e

Please sign in to comment.