Socket
Socket
Sign inDemoInstall

styledocco

Package Overview
Dependencies
81
Maintainers
1
Versions
32
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.5.0 to 0.6.0

share/desktop.png

360

cli.js
'use strict';
var async = require('async');
var cleancss = require('clean-css');
var exec = require('child_process').exec;

@@ -10,18 +11,36 @@ var findit = require('findit');

var mkdirp = require('mkdirp');
// var mincss = require('ncss');
var mincss = function(str) { return str; };
var path = require('path');
var minjs = require('uglify-js');
var uglifyjs = require('uglify-js');
var util = require('util');
var styledocco = require('./styledocco');
var version = require('./package').version;
marked.setOptions({ sanitize: false, gfm: true });
// Helper functions
var add = function(a, b) { return a + b; };
var readFileSync = function(file) { return fs.readFileSync(file, 'utf-8'); };
var mincss = function(css) { return cleancss.process(css); };
var minjs = uglifyjs;
var pluck = function(arr, prop) {
return arr.map(function(item) { return item[prop]; });
};
var flatten = function(arr) {
return arr.reduce(function(tot, cur) {
return tot.concat(isArray(cur) ? flatten(cur) : cur);
}, []);
};
var inArray = function(arr, str) { return arr.indexOf(str) !== -1; };
var isArray = function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
};
// Get a filename without the extension
var baseFilename = function(str) {
return path.basename(str, path.extname(str));
var baseFilename = function(str) { return path.basename(str, path.extname(str)); };
var basePathname = function(file, basePath) {
return path.join(
path.dirname(path.relative(basePath, file) || path.basename(basePath)),
baseFilename(file)
);
};

@@ -38,19 +57,24 @@

// Find first file matching `re` in `dir`.
var findFile = function(dir, re) {
if (!fs.statSync(dir).isDirectory()) return null;
var files = fs.readdirSync(dir).sort().filter(function(file) {
return file.match(re);
var findFile = function(dir, re, cb) {
fs.stat(dir, function(err, stat) {
var files = fs.readdir(dir, function(err, files) {
files = files.sort().filter(function(file) { return file.match(re); });
if (!files.length) cb(new Error('No file found.'));
else cb(null, path.join(dir, files[0]));
});
});
return files[0] != null ? path.join(dir, files[0]) : null;
};
// Return content of first existing file in argument list.
var readFirstFileSync = function() {
var files = [].slice.call(arguments);
for (var i = 0, len = files.length; i < len; i++) {
if (path.existsSync(files[i])) {
return readFileSync(files[i]);
var getFiles = function(inPath, cb) {
fs.stat(inPath, function(err, stat) {
if (err != null) return cb(err);
if (stat.isFile()) {
cb(null, [ inPath ]);
} else {
var finder = findit.find(inPath);
var files = [];
finder.on('file', function(file) { files.push(file); });
finder.on('end', function() { cb(null, files); });
}
}
return '';
});
};

@@ -79,8 +103,24 @@

var preprocess = function(file, pp, options, cb) {
// stdin would have been nice here, but not all preprocessors (less)
// accepts that, so we need to read the file both here and for the parser.
if (pp != null) {
exec(pp + ' ' + file, function(err, stdout, stderr) {
// log('styledocco: preprocessing ' + file + ' with ' + pp);
// Fail gracefully on preprocessor errors
if (err != null) console.error(err.message);
if (stderr.length && options.verbose) console.error(stderr);
cb(null, stdout || '');
});
} else {
fs.readFile(file, 'utf8', cb);
}
};
var cli = function(options) {
// Config
var defaultResourceDir = path.resolve(__dirname, 'resources');
var resourcesDir = __dirname + '/share/';
// Filetypes and matching preprocessor binaries
// Filetypes and matching preprocessor binaries.
var fileTypes = {

@@ -97,130 +137,162 @@ '.css': null,

mkdirp(options.out);
// Compile custom or default template
var template = jade.compile(
readFirstFileSync(options.resources + '/docs.jade',
defaultResourceDir + '/docs.jade')
);
// Get custom or default JS and CSS files
var staticFiles = {
'docs.js': readFirstFileSync(options.resources + '/docs.js',
defaultResourceDir + '/docs.js'),
'docs.css': readFirstFileSync(options.resources + '/docs.css',
defaultResourceDir + '/docs.css'),
'previews.js': readFirstFileSync(options.resources + '/previews.js',
defaultResourceDir + '/previews.js')
// Custom error also outputing StyleDocco and Node versions.
var SDError = function(msg, err) {
this.message = msg + '\n' + err.message + '\n' +
'StyleDocco v' + version +
' running on Node ' + process.version + ' ' + process.platform;
if (options.verbose) {
this.message += '\nOptions: ' + JSON.stringify(options);
}
};
util.inherits(SDError, Error);
// Get optional extra CSS for previews
var previewCSS = mincss(options.include
.filter(function(file) { return path.extname(file) === '.css'; })
.map(readFileSync)
.reduce(add, '')
);
mkdirp(options.out);
// Get optional extra JavaScript for previews
var previewJS = minjs(options.include
.filter(function(file) { return path.extname(file) === '.js'; })
.map(readFileSync)
.reduce(add, '')
);
// Render template
var render = function(source, sections, css) {
if (css == null) css = '';
return template({
title: baseFilename(source),
sections: sections,
project: { name: options.name, menu: menu },
previewCSS: mincss(css) + previewCSS,
previewJS: previewJS
});
};
// Find files
var files = options['in'].reduce(function(files, file) {
if (fs.statSync(file).isDirectory()) {
files = files.concat(findit.sync(file));
} else {
files.push(file);
}
return files;
}, [])
.filter(function(file) {
// No hidden files
if (file.match(/(\/|^)\.[^\.\/]/)) return false;
// Only supported file types
if (!(path.extname(file) in fileTypes)) return false;
// Files only
if (!fs.statSync(file).isFile()) return false;
return true;
}).sort();
if (!files.length) return console.error('No files found');
var preprocess = function(file, cb) {
var pp = options.preprocessor || fileTypes[path.extname(file)];
if (pp != null) {
exec(pp + ' ' + file, function(err, stdout, stderr) {
log('styledocco: preprocessing ' + file + ' with ' + pp);
// Fail gracefully on preprocessor errors
if (err != null) console.error(err.message);
if (stderr.length) console.error(stderr);
cb(null, stdout || '');
// Fetch all static resources.
async.parallel({
template: function(cb) {
fs.readFile(resourcesDir + 'docs.jade', 'utf8', function(err, contents) {
if (err != null) return cb(err);
cb(null, jade.compile(contents));
});
} else {
fs.readFile(file, 'utf8', cb);
},
docs: function(cb) {
async.parallel({
css: async.apply(fs.readFile, resourcesDir + 'docs.css', 'utf8'),
js: function(cb) {
async.parallel([
async.apply(fs.readFile, resourcesDir + 'docs.ui.js', 'utf8'),
async.apply(fs.readFile, resourcesDir + 'docs.previews.js', 'utf8')
], function(err, res) {
if (err != null) return cb(err);
cb(null, res.join(''));
});
}
}, cb);
},
// Extra JavaScript and CSS files to include in previews.
previews: function(cb) {
fs.readFile(resourcesDir + 'previews.js', 'utf8', function(err, js) {
if (err != null) return cb(err);
var code = { js: js, css: '' };
var files = options.include.filter(function(file) {
return inArray(['.css', '.js'], path.extname(file));
});
async.filter(files, path.exists, function(files) {
async.reduce(files, code, function(tot, cur, cb) {
fs.readFile(cur, 'utf8', function(err, contents) {
if (err != null) return cb(err);
tot[path.extname(cur).slice(1)] += contents;
cb(null, tot);
});
}, cb);
});
});
},
// Find input files.
files: function(cb) {
async.reduce(options['in'], [], function(all, cur, cb) {
getFiles(cur, function(err, files) {
if (err != null) return cb(err);
cb(null, all.concat(files));
});
}, function(err, files) {
files = files.filter(function(file) {
// No hidden files
if (file.match(/(\/|^)\.[^\.\/]/)) return false;
// Only supported file types
if (!(path.extname(file) in fileTypes)) return false;
return true;
}).sort();
if (!files.length) cb(new Error('Failed to process files.'));
cb(null, files);
});
},
// Look for a README file.
readme: function(cb) {
findFile(options.basePath, /^readme\.m(ark)?d(own)?/i, function(err, file) {
if (file != null && err == null) return read(file);
findFile(process.cwd(), /^readme\.m(ark)?d(own)?/i, function(err, file) {
if (err != null) file = resourcesDir + 'README.md';
read(file);
});
});
var read = function(file) {
fs.readFile(file, 'utf8', function(err, content) {
if (err != null) cb(err);
cb(null, content);
});
};
}
};
// Build menu
var menu = menuLinks(files, options.basePath);
// Run files through preprocessor and StyleDocco parser.
async.mapSeries(files, function(file, cb) {
preprocess(file, function(err, css) {
cb(null, {
path: file,
css: css
}, function(err, resources) {
if (err != null) throw new SDError('Could not process files.', err);
var menu = menuLinks(resources.files, options.basePath);
// Run files through preprocessor and StyleDocco parser.
async.map(resources.files, function(file, cb) {
async.parallel({
css: async.apply(preprocess, file,
options.preprocessor || fileTypes[path.extname(file)], options),
docs: function(cb) {
fs.readFile(file, 'utf8', function(err, code) {
if (err != null) return cb(err);
cb(null, styledocco(code));
});
}
}, function(err, data) {
if (err != null) return cb(err);
data.path = file;
cb(null, data);
});
}, function(err, files) {
if (err != null) throw err;
// Get the combined CSS from all files.
var previewStyles = pluck(files, 'css').join('');
previewStyles += resources.previews.css;
// Build a JSON string of all files and their headings, for client side search.
var searchIndex = flatten(files.map(function(file) {
var arr = [ { title: baseFilename(file.path),
filename: basePathname(file.path, options.basePath),
url: htmlFilename(file.path, options.basePath) } ];
return arr.concat(file.docs.map(function(section) {
return { title: section.title,
filename: basePathname(file.path, options.basePath),
url: htmlFilename(file.path, options.basePath) + '#' + section.slug };
}));
}));
searchIndex = 'var searchIndex=' + JSON.stringify(searchIndex) + ';';
var docsScript = '(function(){' + searchIndex + resources.docs.js + '})();';
// Render files
var htmlFiles = files.map(function(file) {
return {
path: file.path,
html: resources.template({
title: baseFilename(file.path),
sections: file.docs,
project: { name: options.name, menu: menu },
resources: {
docs: { js: minjs(docsScript), css: mincss(resources.docs.css) },
previews: { js: minjs(resources.previews.js), css: mincss(previewStyles) }
}
})
};
});
// Add readme with "fake" index path.
htmlFiles.push({
path: path.join(options.basePath, 'index'),
html: resources.template({
title: '',
sections: styledocco.makeSections([{ docs: resources.readme, code: '' }]),
project: { name: options.name, menu: menu },
resources: {
docs: { js: minjs(docsScript), css: mincss(resources.docs.css) }
}
})
});
// Write files to the output dir.
htmlFiles.forEach(function(file) {
var dest = path.join(options.out, htmlFilename(file.path, options.basePath));
log('styledocco: writing ' + file.path + ' -> ' + dest);
fs.writeFileSync(dest, file.html);
});
});
}, function(err, htmlFiles) {
var css = htmlFiles.reduce(function(css, file) {
return css + file.css;
}, '');
htmlFiles = htmlFiles.map(function(file) {
return {
path: file.path,
html: render(file.path, styledocco(readFileSync(file.path)), css)
};
});
// Look for a README file.
var readmeFile = findFile(options.basePath, /^readme/i) ||
findFile(process.cwd(), /^readme/i) ||
findFile(options.resources, /^readme/i) ||
defaultResourceDir + '/README.md';
// Add readme with "fake" index path
htmlFiles.push({
path: path.join(options.basePath, 'index'),
html: render('', styledocco.makeSections([ { docs: readFileSync(readmeFile), code: '' } ]), css)
});
// Write files to the output dir.
htmlFiles.forEach(function(file) {
var dest = path.join(options.out, htmlFilename(file.path, options.basePath));
log('styledocco: writing ' + file.path + ' -> ' + dest);
fs.writeFileSync(dest, file.html);
});
// Write static resources to the output dir
Object.keys(staticFiles).forEach(function(file) {
var dest = path.join(options.out, file);
log('styledocco: writing ' + file + ' -> ' + dest);
fs.writeFileSync(dest, staticFiles[file]);
});
});

@@ -227,0 +299,0 @@ };

13

package.json
{
"name": "styledocco",
"description": "Generate style guides and documentation from your stylesheets.",
"keywords": [ "styleguide", "css", "documentation" ],
"description": "Generate documentation and style guides with design patterns from stylesheets.",
"keywords": [ "styleguide", "css", "documentation", "design", "patterns" ],
"author": "Jacob Rask <jacob@jacobrask.net>",
"version": "0.5.0",
"version": "0.6.0",
"homepage": "http://jacobrask.github.com/styledocco/",
"licenses": [ { "type": "MIT", "url": "http://github.com/jacobrask/styledocco/raw/master/LICENSE" } ],
"contributors": [ "Jacob Rask <jacob@jacobrask.net>" ],
"bugs": { "url": "https://github.com/jacobrask/styledocco/issues" },
"repository": { "type": "git", "url": "git://github.com/jacobrask/styledocco.git" },
"engines": { "node": ">=0.4.0" },
"preferGlobal": true,
"dependencies": {
"async": "0.1.x",
"clean-css": "0.4.x",
"findit": "0.x.x",

@@ -23,3 +24,2 @@ "jade": "0.23.x",

"browserify": "1.13.x",
"jquery-browserify": "1.7.x",
"jshint": "0.7.x",

@@ -31,5 +31,4 @@ "nodeunit": "0.7.x"

"scripts": {
"test": "nodeunit test",
"prepublish": "make"
"test": "nodeunit test"
}
}

@@ -6,7 +6,7 @@ StyleDocco

Stylesheet comments will be parsed through [Markdown](http://en.wikipedia.org/wiki/Markdown) and displayed in a generated HTML document. You can write HTML code prefixed with 4 spaces or between [code fences](http://github.github.com/github-flavored-markdown/) (<code>```</code>) in your comments, and StyleDocco renders a preview with the styles applied, and shows the example HTML code.
Stylesheet comments will be parsed through [Markdown](http://en.wikipedia.org/wiki/Markdown) and displayed in a generated HTML document. You can write HTML code prefixed with 4 spaces or between [code fences](http://github.github.com/github-flavored-markdown/) (<code>```</code>) in your comments, and StyleDocco show a preview with the styles applied, and displays the example HTML code.
An important philosophy of StyleDocco is to introduce as little custom syntax as possible, maintaining the stylesheet comments readable and useful even without StyleDocco.
The previews are rendered in a resizable iframes to make it easy to demonstrate responsive designs at different viewport sizes.
Suggestions, feature requests and bug reports are very welcome, either at [GitHub](https://github.com/jacobrask/styledocco/issues) or on Twitter ([@jacobrask](https://twitter.com/jacobrask)).
Suggestions, feature requests and bug reports are welcome either at [GitHub](https://github.com/jacobrask/styledocco/issues) or on Twitter ([@jacobrask](https://twitter.com/jacobrask)).

@@ -17,5 +17,5 @@

StyleDocco requires [Node.js](http://nodejs.org). After installing Node.js, run `npm install -g styledocco` or clone this repository.
StyleDocco requires [Node.js](http://nodejs.org). After installing Node.js, run `npm install -fg styledocco` or clone this repository and run `./bin/styledocco`.
StyleDocco is free software, released under the [MIT license](https://raw.github.com/jacobrask/styledocco/master/LICENSE).
StyleDocco is free and open source software, released under the [MIT license](https://raw.github.com/jacobrask/styledocco/master/LICENSE).

@@ -26,18 +26,13 @@

`styledocco [options] [INPUT]`
`styledocco [options] [STYLESHEET(S)]`
StyleDocco will automatically compile any SASS, SCSS, Less or Stylus files before they are applied to the page. You can also enter a custom preprocessor command if you want to pass custom parameters to the preprocessor.
If your project includes a `README` file, it will be used as the base for an `index.html`. StyleDocco will also add some default styles to your documentation, but they are easy to modify to make it fit with your project.
Options
-------
* `--name`, `-n` Name of the project *(required)*
* `--name`, `-n` Name of the project
* `--out`, `-o` Output directory *(default: "docs")*
* `--resources`, `-s` Directory with files to customize the documentation output. StyleDocco defaults will be used for any required file not found in this directory. *(optional)*
* `--preprocessor` Custom preprocessor command. *(optional)* (ex: `--preprocessor "scss --load-path=deps/"`)
* `--preprocessor` Custom preprocessor command. *(optional)* (ex: `--preprocessor "~/bin/lessc"`)
* `--include` Include specified CSS and/or JavaScript files in the previews. *(optional)* (ex: `--include mysite.css --include app.js`)
* `--verbose` Show log messages when generating the documentation. *(default: false)*
* Directory containing the stylesheets to document.
* Stylesheet (or directory of stylesheets) to process.

@@ -51,5 +46,5 @@ Usage examples

Generate documentation for *My Project* in the `mydocs` folder, from source files in the `styles` folder. Use the Less binary in `~/bin/lessc`.
Generate documentation for *My Project* in the `mydocs` folder, from source files in the `styles` folder. Use the `--compass` option for SASS to make Compass imports available.
`styledocco -n "My Project" -o mydocs -s mydocs --preprocessor "~/bin/lessc" styles`
`styledocco -n "My Project" -o mydocs -s mydocs --preprocessor "scss --compass" styles`

@@ -77,2 +72,5 @@

* StyleDocco will automatically compile any SASS, SCSS, Less or Stylus files before they are applied to the page. You can also enter a custom preprocessor command if you want to pass custom parameters to the preprocessor.
* If your project includes a `README.md` file, it will be used as the base for an `index.html`.
* If you don't specify a custom name, StyleDocco will use the name from a `package.json` file if it finds one.
* Put some whitespace before a comment block to exclude it from the documentation.

@@ -79,0 +77,0 @@ * Level 1 headings will automatically create a new section in the documentation.

@@ -14,2 +14,11 @@ 'use strict';

// Make an URL slug from `str`.
var slugify = function(str) {
return encodeURIComponent(
str.trim().toLowerCase()
.replace(/[^\w ]+/g,'')
.replace(/ +/g,'-')
);
};
// Check if a string is code or a comment (and which type of comment).

@@ -46,3 +55,3 @@ var checkType = function(str) {

// Trim newlines from beginning and end of multi line string.
// Trim newlines from beginning and end of a multi line string.
var trimNewLines = function(str) {

@@ -78,13 +87,16 @@ return str.replace(/^\n*/, '').replace(/\n*$/, '');

var htmlEntities = function(str) {
return str.replace(/</g, '&lt;');
};
var makeSections = exports.makeSections = function(blocks) {
return blocks
.map(function(block) {
// Run comments through marked.lexer to get Markdown tokens
return {
docs: marked.lexer(block.docs),
code: block.code
};
// Run comments through marked.lexer to get Markdown tokens.
block.docs = marked.lexer(block.docs);
return block;
})
.map(function(block) {
// If we encounter code blocks in documentation, add preview HTML
// If we encounter code blocks in documentation, add preview HTML.
var newBlock = {

@@ -94,7 +106,15 @@ code: block.code,

if (token.type === 'code' && (token.lang == null || token.lang === 'html')) {
tokens.push({
type: 'html',
pre: true,
text: '<div class="preview">' + token.text + '</div>'
});
token.type = 'html';
token.pre = true;
token.text = '<textarea class="preview-code" spellcheck="false">' + htmlEntities(token.text) + '</textarea>';
// Add permalink `id`s and some custom properties to headings.
} else if (token.type === 'heading') {
var slug = slugify(token.text);
token.type = 'html';
token._slug = slug;
token._origText = token.text;
// This token should start a new doc section
if (token.depth === 1) token._split = true;
token.text = '<h' + token.depth + ' id="' + slug + '">' +
token.text + '</h' + token.depth + '>\n';
}

@@ -113,15 +133,8 @@ tokens.push(token);

while (docs.length) {
if (docs[0].type === 'heading' && docs[0].depth === 1) {
// New section, add title property and strip heading.
var title = docs[0].text;
docs.shift();
if (docs.length === 0) {
// Nothing more after the heading in the doc block, start new section.
sections.push({ docs: [], code: '', title: title });
} else {
sections.push({ docs: [ docs.shift() ], code: '', title: title });
}
// First section
} else if (sections.length === 0) {
sections.push({ docs: [ docs.shift() ], code: '', title: null });
// New or first section, add title/slug properties.
if (docs[0]._split || sections.length === 0) {
var title = docs[0]._origText;
var slug = docs[0]._slug;
sections.push({ docs: [ docs.shift() ], code: '',
title: title, slug: slug });
} else {

@@ -147,2 +160,3 @@ // Add the documentation to the last section.

title: section.title ? section.title.trim() : '',
slug: section.slug || '',
docs: trimNewLines(marked.parser(section.docs)),

@@ -157,3 +171,4 @@ code: trimNewLines(section.code)

};
module.exports.makeSections = makeSections;
module.exports.separate = separate;

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc