styledocco
Advanced tools
Comparing version 0.0.2 to 0.1.0
388
lib/main.js
(function() { | ||
var Language, destination, docco_template, ensureDirectory, exec, file_exists, fs, generateDocumentation, generateReadme, generateSourceHtml, getLanguage, jade, languages, makeSections, marked, parseArgs, path, preProcess, relative_base, walk, writeFile; | ||
var Language, buildRootPath, cssPath, exec, file, files, findit, fs, generateIndex, generateSourceHtml, getLanguage, input, jade, key, languages, link, makeDestination, makeSections, marked, menu, mkdirp, optimist, options, outputDir, overwriteResources, parts, path, preProcess, renderTemplate, sources, templateDir, trimNewLines, writeFile, _i, _len; | ||
@@ -12,6 +12,20 @@ exec = require('child_process').exec; | ||
mkdirp = require('mkdirp'); | ||
findit = require('findit'); | ||
jade = require('jade'); | ||
walk = require('walk'); | ||
optimist = require('optimist'); | ||
options = optimist.usage('Usage: $0 [options] [INPUT]').describe('name', 'Name of the project').alias('n', 'name').demand('name').describe('out', 'Output directory').alias('o', 'out')["default"]('out', 'docs').describe('tmpl', 'Template directory').boolean('overwrite').describe('overwrite', 'Overwrite existing files in target dir').argv; | ||
input = options._[0] || './'; | ||
outputDir = options.out; | ||
templateDir = options.tmpl || ("" + __dirname + "/../resources/"); | ||
overwriteResources = options.overwrite; | ||
marked.setOptions({ | ||
@@ -21,12 +35,2 @@ sanitize: false | ||
generateDocumentation = function(sourceFile, context, cb) { | ||
return fs.readFile(sourceFile, "utf-8", function(err, code) { | ||
var sections; | ||
if (err) throw err; | ||
sections = makeSections(getLanguage(sourceFile), code); | ||
generateSourceHtml(sourceFile, context, sections); | ||
return cb(); | ||
}); | ||
}; | ||
Language = (function() { | ||
@@ -39,10 +43,12 @@ | ||
if (this.symbols.single) { | ||
this.regexs.single = new RegExp('^\\s*' + this.symbols.single + '\\s?'); | ||
this.regexs.single = new RegExp('^\\s*' + this.symbols.single); | ||
} | ||
this.regexs.multi_start = new RegExp(/^[\s]*\/\*[.]*/); | ||
this.regexs.multi_end = new RegExp(/.*\*\/.*/); | ||
this.regexs.multi_start = new RegExp(/^[\s]*\/\*/); | ||
this.regexs.multi_end = new RegExp(/\*\//); | ||
} | ||
Language.prototype.checkType = function(str) { | ||
if (str.match(this.regexs.multi_start)) { | ||
if (str.match(this.regexs.multi_start) && str.match(this.regexs.multi_end)) { | ||
return 'single'; | ||
} else if (str.match(this.regexs.multi_start)) { | ||
return 'multistart'; | ||
@@ -99,3 +105,3 @@ } else if (str.match(this.regexs.multi_end)) { | ||
}, { | ||
cmd: 'scss', | ||
cmd: 'sass', | ||
args: ['-t', 'compressed'] | ||
@@ -123,72 +129,87 @@ }), | ||
trimNewLines = function(str) { | ||
return str.replace(/^\n*/, '').replace(/\n*$/, ''); | ||
}; | ||
makeDestination = function(file) { | ||
return path.join(path.dirname(file), path.basename(file, path.extname(file)) + '.html'); | ||
}; | ||
buildRootPath = function(str) { | ||
var root; | ||
if (path.dirname(str) === '.') { | ||
root = path.dirname(str); | ||
} else { | ||
root = path.dirname(str).replace(/[^\/]+/g, '..'); | ||
} | ||
if (root.slice(-1) !== '/') root += '/'; | ||
return root; | ||
}; | ||
preProcess = function(filename, cb) { | ||
var lang; | ||
lang = getLanguage(filename); | ||
return lang.compile(filename, cb); | ||
}; | ||
makeSections = function(lang, data) { | ||
var code, docs, hasCode, inMulti, line, lines, multiAccum, sections, _i, _len; | ||
var code, docs, formatCode, formatDocs, line, lines, sections; | ||
lines = data.split('\n'); | ||
sections = []; | ||
docs = code = multiAccum = ''; | ||
inMulti = false; | ||
hasCode = false; | ||
for (_i = 0, _len = lines.length; _i < _len; _i++) { | ||
line = lines[_i]; | ||
if (lang.checkType(line) === 'multistart' || inMulti) { | ||
if (hasCode) { | ||
sections.push({ | ||
docs: marked(docs), | ||
code: code | ||
}); | ||
docs = code = ''; | ||
hasCode = false; | ||
formatDocs = function(line) { | ||
return "" + (lang.filter(line)) + "\n"; | ||
}; | ||
formatCode = function(line) { | ||
return "" + line + "\n"; | ||
}; | ||
while (lines.length) { | ||
docs = code = ''; | ||
while (lines.length && lang.checkType(lines[lines.length - 1]) === 'code') { | ||
code = formatCode(lines.pop()) + code; | ||
} | ||
while (lines.length && lang.checkType(lines[lines.length - 1]) === 'single') { | ||
docs = formatDocs(lines.pop()) + docs; | ||
} | ||
if (lines.length && lang.checkType(lines[lines.length - 1]) === 'multiend') { | ||
while (lines.length) { | ||
line = lines.pop(); | ||
docs = formatDocs(line) + docs; | ||
if (lang.checkType(line) === 'multistart') break; | ||
} | ||
inMulti = true; | ||
multiAccum += line + '\n'; | ||
if (lang.checkType(line) === 'multiend') { | ||
inMulti = false; | ||
docs = multiAccum; | ||
multiAccum = ''; | ||
} | ||
} else if (lang.checkType(line) === 'single') { | ||
if (hasCode) { | ||
hasCode = false; | ||
sections.push({ | ||
docs: marked(docs), | ||
code: code | ||
}); | ||
docs = code = ''; | ||
} | ||
docs += lang.filter(line) + '\n'; | ||
} else { | ||
hasCode = true; | ||
code += line + '\n'; | ||
} | ||
sections.push({ | ||
docs: marked(docs.trim()), | ||
code: trimNewLines(code) | ||
}); | ||
} | ||
sections.push({ | ||
docs: marked(docs), | ||
code: code | ||
}); | ||
return sections; | ||
return sections.reverse(); | ||
}; | ||
preProcess = function(filename, cb) { | ||
var lang; | ||
lang = getLanguage(filename); | ||
return lang.compile(filename, cb); | ||
renderTemplate = function(templateName, content) { | ||
var template, templateFile; | ||
templateFile = "" + templateDir + templateName + ".jade"; | ||
template = fs.readFileSync(templateFile, 'utf-8'); | ||
return jade.compile(template, { | ||
filename: templateFile | ||
})(content); | ||
}; | ||
generateSourceHtml = function(source, context, sections) { | ||
var dest, title; | ||
title = path.basename(source); | ||
dest = destination(source, context); | ||
generateSourceHtml = function(source, menu, sections) { | ||
var dest; | ||
dest = makeDestination(source); | ||
return preProcess(source, function(err, css) { | ||
var html; | ||
if (err) throw err; | ||
html = docco_template({ | ||
title: title, | ||
file_path: source, | ||
var data, html; | ||
if (err != null) throw err; | ||
data = { | ||
title: "" + options.name + " – " + source, | ||
project: { | ||
name: options.name, | ||
menu: menu, | ||
root: buildRootPath(source) | ||
}, | ||
sections: sections, | ||
context: context, | ||
path: path, | ||
relative_base: relative_base, | ||
css: css | ||
}); | ||
console.log("styledocco: " + source + " -> " + dest); | ||
}; | ||
html = renderTemplate('docs', data); | ||
console.log("styledocco: " + source + " -> " + (path.join(outputDir, dest))); | ||
return writeFile(dest, html); | ||
@@ -198,173 +219,84 @@ }); | ||
generateReadme = function(context, sources, cb) { | ||
var currentDir, dest, getReadme, templateDir; | ||
templateDir = "" + __dirname + "/../resources/"; | ||
generateIndex = function(menu) { | ||
var content, currentDir, data, dest, files, html, readme; | ||
currentDir = "" + (process.cwd()) + "/"; | ||
dest = "" + context.config.output_dir + "/index.html"; | ||
getReadme = function(cb) { | ||
return fs.readdir(currentDir, function(err, files) { | ||
if (err != null) return cb(err); | ||
files = files.filter(function(file) { | ||
return file.toLowerCase().match(/^readme/); | ||
}); | ||
if (files[0] == null) return cb(new Error('No readme found')); | ||
return fs.readFile(currentDir + files[0], 'utf-8', function(err, content) { | ||
if ((err != null) || !content.length) return cb(err); | ||
return cb(null, marked(content), files[0]); | ||
}); | ||
dest = "index.html"; | ||
if (fs.statSync(input).isDirectory()) { | ||
files = fs.readdirSync(input).filter(function(file) { | ||
return file.toLowerCase().match(/^readme/); | ||
}); | ||
if (files[0] != null) readme = path.join(input, files[0]); | ||
} | ||
if (readme == null) { | ||
files = fs.readdirSync(currentDir).filter(function(file) { | ||
return file.toLowerCase().match(/^readme/); | ||
}); | ||
if (files[0] != null) readme = path.join(currentDir, files[0]); | ||
} | ||
content = readme != null ? marked(fs.readFileSync(readme, 'utf-8')) : "<h1>Readme</h1><p>Please add a README file to this project.</p>"; | ||
data = { | ||
title: options.name, | ||
project: { | ||
name: options.name, | ||
menu: menu, | ||
root: './' | ||
}, | ||
content: content | ||
}; | ||
return getReadme(function(err, content, readmePath) { | ||
var title; | ||
if (content == null) { | ||
content = "<h1>Readme</h1><p>Please add a README file to this project.</p>"; | ||
} | ||
if (readmePath == null) readmePath = './'; | ||
title = context.config.project_name || "README"; | ||
return fs.readFile(templateDir + 'readme.jade', 'utf-8', function(err, tmpl) { | ||
var html, readmeTemplate; | ||
readmeTemplate = jade.compile(tmpl, { | ||
filename: templateDir + 'readme.jade' | ||
}); | ||
html = readmeTemplate({ | ||
title: title, | ||
context: context, | ||
content: content, | ||
file_path: readmePath, | ||
path: path, | ||
relative_base: relative_base | ||
}); | ||
console.log("styledocco: " + readmePath + " -> " + dest); | ||
writeFile(dest, html); | ||
return cb(); | ||
}); | ||
}); | ||
html = renderTemplate('index', data); | ||
console.log("styledocco: " + (files[0] || './') + " -> " + (path.join(outputDir, dest))); | ||
return writeFile(dest, html); | ||
}; | ||
writeFile = function(dest, contents) { | ||
var target_dir, write_func; | ||
target_dir = path.dirname(dest); | ||
write_func = function() { | ||
return fs.writeFile(dest, contents, function(err) { | ||
if (err) throw err; | ||
}); | ||
}; | ||
return fs.stat(target_dir, function(err, stats) { | ||
if (err && err.code !== 'ENOENT') throw err; | ||
if (!err) return write_func(); | ||
if (err) { | ||
return exec("mkdir -p " + target_dir, function(err) { | ||
if (err) throw err; | ||
return write_func(); | ||
}); | ||
} | ||
}); | ||
dest = path.join(outputDir, dest); | ||
mkdirp.sync(path.dirname(dest)); | ||
return fs.writeFileSync(dest, contents); | ||
}; | ||
relative_base = function(filepath, context) { | ||
var result; | ||
result = path.dirname(filepath) + '/'; | ||
if (result === '/' || result === '//') { | ||
return ''; | ||
} else { | ||
return result; | ||
} | ||
}; | ||
mkdirp.sync(outputDir); | ||
destination = function(filepath, context) { | ||
var base_path; | ||
base_path = relative_base(filepath, context); | ||
return ("" + context.config.output_dir + "/") + base_path + path.basename(filepath, path.extname(filepath)) + '.html'; | ||
}; | ||
sources = findit.sync(input); | ||
ensureDirectory = function(dir, cb) { | ||
return exec("mkdir -p " + dir, function() { | ||
return cb(); | ||
}); | ||
}; | ||
files = sources.filter(function(source) { | ||
if (source.match(/(\/|^)\./)) return false; | ||
if (source.match(/(\/|^)_.*\.s[ac]ss$/)) return false; | ||
if (!(path.extname(source) in languages)) return false; | ||
if (!fs.statSync(source).isFile()) return false; | ||
return true; | ||
}).sort(); | ||
file_exists = function(path) { | ||
try { | ||
return fs.lstatSync(path).isFile; | ||
} catch (ex) { | ||
return false; | ||
menu = {}; | ||
for (_i = 0, _len = files.length; _i < _len; _i++) { | ||
file = files[_i]; | ||
link = { | ||
name: path.basename(file, path.extname(file)), | ||
href: makeDestination(file) | ||
}; | ||
parts = file.split('/').splice(1); | ||
key = parts.length > 1 ? parts[0] : './'; | ||
if (menu[key] != null) { | ||
menu[key].push(link); | ||
} else { | ||
menu[key] = [link]; | ||
} | ||
}; | ||
} | ||
docco_template = jade.compile(fs.readFileSync(__dirname + '/../resources/docs.jade').toString(), { | ||
filename: __dirname + '/../resources/docs.jade' | ||
generateIndex(menu); | ||
files.forEach(function(file) { | ||
var code, sections; | ||
code = fs.readFileSync(file, "utf-8"); | ||
sections = makeSections(getLanguage(file), code); | ||
return generateSourceHtml(file, menu, sections); | ||
}); | ||
parseArgs = function(cb) { | ||
var a, args, ext, lang_filter, project_name, roots; | ||
args = process.ARGV; | ||
project_name = ""; | ||
if (args[0] === "-name") { | ||
args.shift(); | ||
project_name = args.shift(); | ||
} | ||
args = args.sort(); | ||
if (!args.length) return; | ||
roots = (function() { | ||
var _i, _len, _results; | ||
_results = []; | ||
for (_i = 0, _len = args.length; _i < _len; _i++) { | ||
a = args[_i]; | ||
_results.push(a.replace(/\/+$/, '')); | ||
} | ||
return _results; | ||
})(); | ||
roots = roots.join(" "); | ||
lang_filter = (function() { | ||
var _results; | ||
_results = []; | ||
for (ext in languages) { | ||
_results.push(" -name '*" + ext + "' "); | ||
} | ||
return _results; | ||
})(); | ||
lang_filter = lang_filter.join(' -o '); | ||
return exec("find " + roots + " -type f \\( " + lang_filter + " \\)", function(err, stdout) { | ||
var sources; | ||
if (err) throw err; | ||
sources = stdout.split("\n").filter(function(file) { | ||
var filename; | ||
if (file === '') return false; | ||
filename = path.basename(file); | ||
if (filename[0] === '.') return false; | ||
if (filename.match(/^_.*\.s[ac]ss$/)) return false; | ||
return true; | ||
}); | ||
console.log("styledocco: Recursively generating docs underneath " + roots + "/"); | ||
return cb(sources, project_name, args); | ||
}); | ||
}; | ||
cssPath = path.join(outputDir, 'docs.css'); | ||
parseArgs(function(sources, project_name, raw_paths) { | ||
var context; | ||
context = { | ||
sources: sources, | ||
options: { | ||
project_name: project_name | ||
} | ||
}; | ||
context.config = { | ||
show_timestamp: true, | ||
output_dir: 'docs', | ||
project_name: project_name || '' | ||
}; | ||
return ensureDirectory(context.config.output_dir, function() { | ||
return generateReadme(context, raw_paths, function() { | ||
var files, nextFile; | ||
files = sources.slice(0, sources.length + 1 || 9e9); | ||
nextFile = function() { | ||
if (files.length) { | ||
return generateDocumentation(files.shift(), context, nextFile); | ||
} | ||
}; | ||
return nextFile(); | ||
}); | ||
}); | ||
}); | ||
if (overwriteResources || !path.existsSync(cssPath)) { | ||
fs.writeFileSync(cssPath, fs.readFileSync(__dirname + '/../resources/docs.css', 'utf-8')); | ||
console.log("styledocco: writing " + (path.join(outputDir, 'docs.css'))); | ||
} | ||
}).call(this); |
@@ -10,3 +10,3 @@ { | ||
"author": "Jacob Rask <jacob@jacobrask.net>", | ||
"version": "0.0.2", | ||
"version": "0.1.0", | ||
"licenses": [ | ||
@@ -34,7 +34,11 @@ { | ||
"dependencies": { | ||
"coffee-script": "latest", | ||
"jade": "0.17.0", | ||
"marked": "0.2.1", | ||
"walk": "2.0.2" | ||
"findit": "0.x.x", | ||
"jade": "0.17.x", | ||
"marked": "0.2.x", | ||
"mkdirp": "0.x.x", | ||
"optimist": "0.x.x" | ||
}, | ||
"devDependencies": { | ||
"coffee-script": "1.2.x" | ||
}, | ||
"directories": { | ||
@@ -45,4 +49,4 @@ "lib": "./lib" | ||
"bin": { | ||
"styledocco": "./bin/generate" | ||
"styledocco": "./bin/styledocco" | ||
} | ||
} |
@@ -1,48 +0,63 @@ | ||
``` | ||
_______ __ __ _____ | ||
| __| |_.--.--.| |.-----.| \.-----..----..----..-----. | ||
|__ | _| | || || -__|| -- | _ || __|| __|| _ | | ||
|_______|____|___ ||__||_____||_____/|_____||____||____||_____| | ||
|_____| | ||
``` | ||
_______ __ __ _____ | ||
| __| |_.--.--| |-----.| \-----.----.----.-----. | ||
|__ | _| | | | -__|| -- | _ | __| __| _ | | ||
|_______|____|___ |__|_____||_____/_____|____|____|_____| | ||
|_____| | ||
About | ||
----- | ||
===== | ||
StyleDocco takes your stylesheets and generates style guide documents with the stylesheets in question applied to the documents. | ||
StyleDocco takes your stylesheets and generates style guide documents with the processed stylesheets applied to the documents. | ||
`styledocco main.css` will generate `docs/main.html` with all the comments from the file (passed through GitHub flavored Markdown) in one column, and all the code in another column. | ||
`styledocco --name 'My Site' main.css` will generate `docs/main.html` with all the comments from the file (passed through GitHub flavored Markdown) in one column, and all the code in another column. | ||
The CSS in `main.css` will be applied to the page. This means you can add sample HTML content in the comments of your CSS file, and have it rendered in the browser using that same CSS. | ||
To add extra styles to your documentation, add a file named `docs.css` in your `docs` folder. | ||
If your project includes a `README` file, it will be used as the base for an `index.html`. | ||
If you point StyleDocco to a directory, it will add a menu to navigate between the different files (ignoring hidden files and SASS partials). If your project includes a `README` file, it will be used as the base for an `index.html`. | ||
StyleDocco will add `docs.css` with some default styles to your documentation, but you might want to modify it to make it fit with your project. | ||
StyleDocco will automatically compile any SASS, SCSS, Less or Stylus code before it is applied to the page. | ||
StyleDocco will automatically compile any SASS, SCSS, Less or Stylus code before it is applied to the page. Hidden files and SASS partials will be ignored. | ||
Install | ||
======= | ||
StyleDocco requires [Node.js](http://nodejs.org). | ||
`npm install -g styledocco` | ||
Usage | ||
===== | ||
`styledocco [options] [INPUT]` | ||
Options | ||
------- | ||
`npm install styledocco` | ||
* `--name`, `-n` Name of the project *(required)* | ||
* `--out`, `-o` Output directory *(default: "docs")* | ||
* `--tmpl` Directory for custom `index.jade` and `docs.jade` templates *(optional)* | ||
* `--overwrite` Overwrite existing files (`docs.css`) in target directory. | ||
Examples | ||
-------- | ||
======== | ||
``` | ||
/* <button class="btn btn-primary">Primary</button> | ||
Provides extra visual weight and identifies the primary action in a set of buttons */ | ||
.btn.btn-primary { | ||
background: blue; | ||
color: white; | ||
} | ||
``` | ||
/* <button class="btn primary">Primary</button> | ||
Provides extra visual weight and identifies the primary action in a set of buttons */ | ||
.btn.primary { | ||
background: blue; | ||
color: white; | ||
} | ||
Would output an HTML document with one column displaying the rendered button followed by the description, and another column with the code. The code will also be included in a `style` element of the document. | ||
See the `examples` folder for more in-depth examples. | ||
Acknowledgements | ||
---------------- | ||
================ | ||
@@ -49,0 +64,0 @@ Thanks to: |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
105791
20
776
68
5
1
2
+ Addedfindit@0.x.x
+ Addedmkdirp@0.x.x
+ Addedoptimist@0.x.x
+ Addedchainsaw@0.0.9(transitive)
+ Addedfindit@0.1.2(transitive)
+ Addedhashish@0.0.4(transitive)
+ Addedmarked@0.2.10(transitive)
+ Addedminimist@0.0.101.2.8(transitive)
+ Addedmkdirp@0.5.6(transitive)
+ Addedoptimist@0.6.1(transitive)
+ Addedseq@0.3.5(transitive)
+ Addedtraverse@0.3.9(transitive)
+ Addedwordwrap@0.0.3(transitive)
- Removedcoffee-script@latest
- Removedwalk@2.0.2
- Removedcoffee-script@1.12.7(transitive)
- RemovedforEachAsync@3.0.0(transitive)
- Removedmarked@0.2.1(transitive)
- Removedmkdirp@3.0.1(transitive)
- Removedwalk@2.0.2(transitive)
Updatedjade@0.17.x
Updatedmarked@0.2.x