diff --git a/.gitignore b/.gitignore index c2658d7..c07614a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ node_modules/ +output/* +!output/menu.html +!output/index.html + diff --git a/.jshintignore b/.jshintignore new file mode 100644 index 0000000..a25cc12 --- /dev/null +++ b/.jshintignore @@ -0,0 +1,3 @@ +node_modules/ +layouts/ +output/ diff --git a/Makefile b/Makefile index ee5f8a2..e1e7365 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ UNAME := $(shell uname) +GJSLINT := --nojsdoc --exclude_directories=node_modules,layouts,output --max_line_length=120 --disable=200,201,202,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,230,231,232,233,250,251,252 build: for name in `find ./layouts -mindepth 1 -maxdepth 1 -type d | sed -e 's/.\/layouts\///'` ; do \ @@ -34,3 +35,10 @@ get-fonts: node font-download.js .PHONY: build screenshots get-fonts + +lint: + fixjsstyle $(GJSLINT) -r . + gjslint $(GJSLINT) -r . + jshint . + +.PHONY: lint diff --git a/bin/generate-md b/bin/generate-md index 6440fe7..c4f2059 100755 --- a/bin/generate-md +++ b/bin/generate-md @@ -7,27 +7,29 @@ var fs = require('fs'), opts.options({ 'layouts': { }, + 'help': { }, 'layout': { }, 'input': { }, 'output': { }, - 'runner': { }, - 'command': { }, - 'asset-dir': { }, - 'partials': { }, - 'helpers': { }, - 'highlight': { } + 'v': { }, + 'version': { } }) - .boolean('layouts'); + .boolean('layouts').boolean('help').boolean('v').boolean('version'); var argv = opts.parse(process.argv); +if (argv.v || argv.version) { + console.log(require('../package.json').version); + process.exit(); +} + argv = resolveArgs(argv); var layoutDir = __dirname + '/../layouts/'; -if(argv.layouts || !fs.existsSync(argv.input)) { +if (argv.help || argv.layouts || !fs.existsSync(argv.input)) { if(!fs.existsSync(argv.input)) { - console.log(argv.input+' does not exist'); + console.log(argv.input + ' does not exist'); } // show layouts console.log('Available layouts:\n'+ @@ -40,7 +42,6 @@ if(argv.layouts || !fs.existsSync(argv.input)) { } console.log('Layout: ' + argv.layout); -console.log('Template file: ' + argv.template); console.log('Input: ' + argv.input); console.log('Output: ' + argv.output); diff --git a/font-download.js b/font-download.js index 215f2ab..8f2a40f 100644 --- a/font-download.js +++ b/font-download.js @@ -7,11 +7,11 @@ https.get({ path: '/repos/w0ng/googlefontdirectory/contents/fonts', headers: { 'user-agent': 'nodejs'} }, function(res) { - console.log("Got response: " + res.statusCode); + console.log('Got response: ' + res.statusCode); console.log(res.headers); var data = ''; res.setEncoding('utf8'); - res.on('data', function (chunk) { + res.on('data', function(chunk) { data += chunk; }); res.on('end', function() { @@ -23,12 +23,12 @@ https.get({ }); console.log('# Run the following commands to get the fonts: '); items.forEach(function(item) { - console.log('cd ~/.fonts && wget -N https://github.com/w0ng/googlefontdirectory/raw/master/'+item.path); + console.log('cd ~/.fonts && wget -N https://github.com/w0ng/googlefontdirectory/raw/master/' + item.path); }); - console.log('fc-cache -fv') + console.log('fc-cache -fv'); }); }); }).on('error', function(e) { - console.log("Got error: " + e.message); + console.log('Got error: ' + e.message); }); diff --git a/layouts/plain/page.html b/layouts/jasonm23-dark/page.html similarity index 100% rename from layouts/plain/page.html rename to layouts/jasonm23-dark/page.html diff --git a/layouts/jasonm23-foghorn/page.html b/layouts/jasonm23-foghorn/page.html new file mode 100644 index 0000000..fda2bba --- /dev/null +++ b/layouts/jasonm23-foghorn/page.html @@ -0,0 +1,12 @@ + + +
+]*>([\s\S]*?)<\/code><\/pre>/mg, '$1
')
.replace(/
]*)>\s*<\/p>/g, '
![]()
');
diff --git a/lib/index.js b/lib/index.js
index 210a5d7..82b82eb 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -2,4 +2,4 @@ module.exports = {
resolveArgs: require('./resolve-args'),
render: require('./render'),
pipeline: require('./pipeline')
-}
+};
diff --git a/lib/merge-meta.js b/lib/merge-meta.js
index acdb3f6..ba68149 100644
--- a/lib/merge-meta.js
+++ b/lib/merge-meta.js
@@ -4,7 +4,7 @@ var pi = require('pipe-iterators'),
module.exports = function(meta) {
return pi.map(function(item) {
- var item = xtend(item, meta[item.projectName] ? meta[item.projectName] : {});
+ item = xtend(item, meta[item.projectName] ? meta[item.projectName] : {});
if (!item.title && item.headings && item.headings[0]) {
item.title = item.headings[0].text;
diff --git a/lib/pipeline.js b/lib/pipeline.js
index ef1c83c..4a6ae77 100644
--- a/lib/pipeline.js
+++ b/lib/pipeline.js
@@ -16,8 +16,7 @@ module.exports = function(argv) {
// map paths
setOutputPath({
input: argv.input,
- output: argv.output,
- assetDir: argv.assetDir || argv.output + '/assets/'
+ output: argv.output
}),
// merge metadata now that projectName is set
@@ -30,9 +29,9 @@ module.exports = function(argv) {
// apply handlebars templates
applyTemplate({
// read the template
- template: fs.readFileSync(argv.template, 'utf8'),
- partials: argv.partials || [],
- helpers: argv.helpers || []
+ template: fs.readFileSync(argv.layout + '/page.html', 'utf8'),
+ partials: fs.existsSync(argv.layout + '/partials') ? argv.layout + '/partials' : [],
+ helpers: fs.existsSync(argv.layout + '/helpers') ? argv.layout + '/helpers' : []
})
]);
};
diff --git a/lib/render.js b/lib/render.js
index 1efe06d..6fbe9fc 100644
--- a/lib/render.js
+++ b/lib/render.js
@@ -6,8 +6,30 @@ var fs = require('fs'),
stream = require('./stream');
module.exports = function(argv, onDone) {
- glob.stream(fs.statSync(argv.input).isFile() ? argv.input : argv.input + '/**')
- .pipe(pi.head([
+ // --export
+ if (argv['export']) {
+ pi.fromArray(
+ glob.sync(path.normalize(argv['export']).replace(/\/$/, '') + '/**')
+ ).pipe(pi.filter(function(filename) {
+ var stat = fs.statSync(filename);
+ return stat.isFile();
+ }))
+ .pipe(stream.copy(function(filename) {
+ var target = path.normalize(filename.replace(argv['export'], argv.output + '/'));
+ console.log('Copy layout file', filename, '=>', target);
+ return target;
+ }))
+ .pipe(pi.devnull().once('finish', function() {
+ if (onDone) {
+ onDone();
+ }
+ }));
+ return;
+ }
+
+ pi.fromArray(
+ glob.sync(fs.statSync(argv.input).isFile() ? argv.input : argv.input.replace(/\/$/, '') + '/**')
+ ).pipe(pi.head([
pi.filter(function(filename) {
var stat = fs.statSync(filename);
@@ -50,23 +72,20 @@ module.exports = function(argv, onDone) {
// copy assets
- if(argv.assetDir && fs.existsSync(argv.assetDir)) {
- glob.stream(argv.assetDir + '/**')
+ var assetDir = path.normalize(argv.layout + '/assets').replace(/\/$/, '');
+ if (fs.existsSync(assetDir)) {
+ pi.fromArray(glob.sync(assetDir + '/**'))
.pipe(pi.filter(function(filename) {
var stat = fs.statSync(filename);
return stat.isFile();
}))
.pipe(stream.copy(function(filename) {
- var target = path.normalize(filename.replace(argv.assetDir, argv.output + '/assets/'));
+ var target = path.normalize(filename.replace(assetDir, argv.output + '/assets/'));
console.log('Copy asset file', filename, '=>', target);
return target;
}))
.pipe(pi.devnull());
} else {
- if (argv.assetDir) {
- console.log('Assets path does not exist: ' + argv.assetDir + ', so no assets were copied.');
- } else {
- console.log('No asset directory was found, so no assets were copied.');
- }
+ console.log('Assets path does not exist: ' + assetDir + ', so no assets were copied.');
}
};
diff --git a/lib/resolve-args.js b/lib/resolve-args.js
index 12f9c89..db8cd49 100644
--- a/lib/resolve-args.js
+++ b/lib/resolve-args.js
@@ -7,26 +7,46 @@ module.exports = function(argv) {
// defaults
argv.input = path.resolve(process.cwd(), argv.input || './input/');
argv.output = path.resolve(process.cwd(), argv.output || './output/');
- if(!argv.layout) {
- argv.layout = 'github';
+ if (!argv.layout) {
+ if (argv['export']) {
+ argv.layout = argv['export'];
+ } else {
+ argv.layout = 'github';
+ }
}
- // template is one of:
- if(fs.existsSync(process.cwd() + '/' + argv.layout)) {
- // 1) the supplied argument (normalized)
- argv.template = path.normalize(process.cwd() + '/' + argv.layout);
- } else if(fs.existsSync(layoutDir + argv.layout + '/page.html')) {
- // 2) a preset layout from the layout dir
- argv.template = path.normalize(layoutDir + argv.layout + '/page.html');
- } else {
- // 3) the default layout
- argv.template = path.normalize(layoutDir + 'plain/page.html');
+ if (argv.template) {
+ throw new Error('--template is deprecated in v2.0, please point --layout to ' +
+ 'the layout directory with ./page.html in it.');
+ }
+ if (argv['asset-dir'] || argv.assetDir) {
+ throw new Error('--asset-dir is deprecated in v2.0, please point --layout to ' +
+ 'the layout directory with ./page.html and ./assets in it.');
+ }
+ if (argv.command) {
+ throw new Error('--command is deprecated in v2.0');
+ }
+ if (argv.runner) {
+ throw new Error('--runner is deprecated in v2.0');
}
- if(argv.layouts || !fs.existsSync(argv.input)) {
- return argv;
+ // we only accept a single layout argument, --layout
+ if (fs.existsSync(argv.layout)) {
+ // 1) it can be an absolute path to a folder with ./page.html
+ argv.layout = path.normalize(argv.layout);
+ } else if (fs.existsSync(process.cwd() + '/' + argv.layout)) {
+ // 2) it can be a relative path
+ argv.layout = path.normalize(process.cwd() + '/' + argv.layout);
+ } else if (fs.existsSync(layoutDir + argv.layout + '/')) {
+ // 3c) it can be the name of a builtin layout
+ argv.layout = path.normalize(layoutDir + argv.layout + '/');
}
+ if (argv['export']) {
+ argv['export'] = argv.layout;
+ }
+
+ // set up partials and helpers directories
var layoutBase = path.dirname(argv.template);
['partials', 'helpers'].forEach(function(name) {
if (argv[name]) {
@@ -37,38 +57,21 @@ module.exports = function(argv) {
}
});
- if(argv['command']) {
- argv['command'] = argv['command'].split(' ');
- }
-
-
// parse --highlight-
var hl = {};
Object.keys(argv).forEach(function(name) {
var matched = (typeof name === 'string' ? name.match(/highlight\-(.*)/) : false);
- if(name == 'highlight') {
- argv[name] = findModule(argv[name], [ process.cwd, __dirname ]);
+ if (name == 'highlight') {
+ argv[name] = findModule(argv[name], [process.cwd, __dirname]);
hl['default'] = require(argv[name]);
- } else if(matched) {
+ } else if (matched) {
var ext = matched[1];
- argv[name] = findModule(argv[name], [ process.cwd, __dirname ]);
+ argv[name] = findModule(argv[name], [process.cwd, __dirname]);
hl[ext] = require(argv[name]);
}
});
-
argv['highlight'] = hl;
-
- // 1) lookup from the dir in which the template is
- if(argv['asset-dir']) {
- argv.assetDir = path.resolve(process.cwd(), argv['asset-dir']);
- } else if (fs.existsSync(path.dirname(argv.template) + '/assets/')) {
- argv.assetDir = path.normalize(path.dirname(argv.template) + '/assets/');
- } else if (argv.layout) {
- // 2) try the builtin template dir
- argv.assetDir = path.normalize(__dirname + '/../layouts/' + argv.layout + '/assets/');
- }
-
return argv;
};
diff --git a/lib/set-output-path.js b/lib/set-output-path.js
index 26a06fd..e249065 100644
--- a/lib/set-output-path.js
+++ b/lib/set-output-path.js
@@ -6,7 +6,7 @@ module.exports = function(opts) {
var relative = item.path.replace(opts.input + '/', '');
var outputDir = path.normalize(path.dirname(item.path).replace(opts.input, opts.output + path.sep));
// path: full path to the output file
- item.path = path.normalize(outputDir + path.sep + path.basename(item.path, '.md') + '.html' );
+ item.path = path.normalize(outputDir + path.sep + path.basename(item.path, '.md') + '.html');
// projectName: either the file name, or the folder name of this file
var isDirectory = (relative.indexOf('/') > -1);
item.projectName = (isDirectory ? path.dirname(relative) : path.basename(relative, '.md'));
diff --git a/lib/stream/copy.js b/lib/stream/copy.js
index 3182795..a947bb8 100644
--- a/lib/stream/copy.js
+++ b/lib/stream/copy.js
@@ -19,8 +19,10 @@ module.exports = function(targetFn) {
seen[copyDir] = true;
fs.createReadStream(filename)
.pipe(fs.createWriteStream(target))
- .once('finish', done)
+ .once('finish', function() {
+ done();
+ })
.once('error', done);
});
});
-}
+};
diff --git a/lib/stream/write.js b/lib/stream/write.js
index 734a9b3..9f8ba70 100644
--- a/lib/stream/write.js
+++ b/lib/stream/write.js
@@ -1,6 +1,6 @@
var path = require('path'),
fs = require('fs'),
- pi = require('pipe-iterators')
+ pi = require('pipe-iterators'),
mkdirp = require('mkdirp');
function dest() {
diff --git a/output/index.html b/output/index.html
new file mode 100644
index 0000000..c1b1d53
--- /dev/null
+++ b/output/index.html
@@ -0,0 +1,17 @@
+
+
+
+Markdown CSS
+
+
+
+
+
diff --git a/output/menu.html b/output/menu.html
new file mode 100644
index 0000000..2cf60d2
--- /dev/null
+++ b/output/menu.html
@@ -0,0 +1,51 @@
+
+
+Menu
+
+
+
+
+ mixu-book
+
+ mixu-page
+
+ mixu-radar
+
+ mixu-bootstrap
+
+ mixu-bootstrap-2col
+
+ mixu-gray
+
+ github
+
+
+ jasonm23-markdown
+
+ jasonm23-foghorn
+
+ jasonm23-dark
+
+ jasonm23-swiss
+
+ markedapp-byword
+
+ thomasf-solarizedcsslight
+
+ thomasf-solarizedcssdark
+
+ bootstrap3
+
+
+
+
diff --git a/package.json b/package.json
index 45e0839..597e155 100644
--- a/package.json
+++ b/package.json
@@ -38,6 +38,7 @@
"yargs": "~1.1.3"
},
"devDependencies": {
- "file-fixture": "0.0.2"
+ "file-fixture": "0.0.2",
+ "mds-csv": "0.0.0"
}
}
diff --git a/test/api.test.js b/test/api.test.js
index 15566a5..9f60a69 100644
--- a/test/api.test.js
+++ b/test/api.test.js
@@ -1,12 +1,109 @@
var fs = require('fs'),
path = require('path'),
+ assert = require('assert'),
fixture = require('file-fixture'),
mds = require('../');
var layouts = fs.readdirSync(__dirname + '/../layouts');
+var layoutDir = path.normalize(__dirname + '/../layouts/');
+var customLayout;
+var sampleInput;
+describe('api tests', function() {
+
+ before(function() {
+ customLayout = fixture.dir({
+ 'assets/css/style.css': 'style',
+ 'partials/foo.hdbs': 'foo
',
+ 'helpers/bar.js': [
+ 'module.exports = function(context, opts) {',
+ ' return "barHelper " + context;',
+ '};'
+ ],
+ 'page.html': '{{> foo}}{{bar "baz"}}{{~> content}}'
+ });
+ sampleInput = fixture.dir({
+ 'index.md': [
+ 'title: Hello world',
+ 'author: Anonymous',
+ '----',
+ '# Test',
+ '```js',
+ 'var foo = "bar";',
+ '```'
+ ].join('\n')
+ });
+
+ });
+
+ it('--input defaults to process.cwd() + ./input', function() {
+ assert.equal(mds.resolveArgs({ }).input, process.cwd() + '/input');
+ assert.equal(mds.resolveArgs({ input: 'foo' }).input, process.cwd() + '/foo');
+ assert.equal(mds.resolveArgs({ input: './foo' }).input, process.cwd() + '/foo');
+ });
+
+ it('--output defaults to process.cwd() + ./output', function() {
+ assert.equal(mds.resolveArgs({ }).output, process.cwd() + '/output');
+ assert.equal(mds.resolveArgs({ output: 'foo' }).output, process.cwd() + '/foo');
+ assert.equal(mds.resolveArgs({ output: './foo' }).output, process.cwd() + '/foo');
+ });
+
+ it('--layout specifies the layout to use', function() {
+ assert.equal(mds.resolveArgs({ }).layout, layoutDir + 'github/');
+ assert.equal(mds.resolveArgs({ layout: 'roryg-ghostwriter' }).layout, layoutDir + 'roryg-ghostwriter/');
+ });
+
+ it('loads page.html, assets, partials and helpers given --layout', function(done) {
+ var out = fixture.dirname();
+ sampleInput = fixture.dir({
+ 'index.md': [
+ 'title: Hello world',
+ 'author: Anonymous',
+ '----',
+ '# Test',
+ '```js',
+ 'var foo = "bar";',
+ '```'
+ ].join('\n')
+ });
+ mds.render(mds.resolveArgs({
+ input: sampleInput,
+ output: out,
+ layout: customLayout
+ }), function() {
+ // check that the helper ran
+ // check that the partial ran
+ // check that the assets were copied
+ // check that the output is as expected
+ console.log('custom layout', customLayout, out);
+ done();
+ });
+ });
+
+ it('--export exports the layout to a directory', function(done) {
+ var out = fixture.dirname();
+ mds.render(mds.resolveArgs({
+ 'export': 'github',
+ output: out
+ }), function() {
+ // check that the assets, helpers and partials were copied
+ done();
+ });
+ });
+
+ it('--highlight-lang module enables a highlighter', function(done) {
+ var out = fixture.dirname();
+ mds.render(mds.resolveArgs({
+ input: sampleInput,
+ output: out,
+ layout: customLayout
+ }), function() {
+ // check that the custom js highlighter ran
+ console.log('--highlight-lang', customLayout, out);
+ done();
+ });
+ });
-/*
it('has a render(opts, onDone) function that works like bin/generate-md', function(done) {
// params:
@@ -17,7 +114,7 @@ var layouts = fs.readdirSync(__dirname + '/../layouts');
// helpers
// layout
// assetDir
-
+ done();
});
it('has a pipeline(opts) function that returns a pipeline that accepts items', function(done) {
@@ -28,11 +125,52 @@ var layouts = fs.readdirSync(__dirname + '/../layouts');
// template
// partials
// helpers
-
+ done();
});
-*/
+/*
+ it('supports custom syntax highlighters for specific languages', function(done) {
+ var dir = fixture.dir({
+ 'foo.md': [
+ '# foo',
+ '',
+ '```csv',
+ 'abc,def,ghi',
+ '```'
+ ].join('\n')
+ });
+ var out = fixture.dirname();
+
+ mds.render({
+ input: dir,
+ output: out,
+ template: templateDir + '/page.html'
+ }, function() {
+ assert.equal(fs.readFileSync(out + '/foo.html', 'utf8'), [
+ '"Some title" by ',
+ 'Some title
',
+ 'abcdef
\n'
+ ].join('\n'));
+ done();
+ });
+
+
+function(code, lang) {
+ if (lang === 'csv') {
+ return mdsCsv(code, lang);
+ }
+ return false;
+ }
+
+ done();
+ });
+
+ });
+*/
+});
+
+/*
describe('can render every sample layout', function() {
var dir;
@@ -60,13 +198,6 @@ describe('can render every sample layout', function() {
layout: layout
}), function() {
// console.log(layout, out);
- /*
- assert.equal(fs.readFileSync(out + '/index.html', 'utf8'), [
- '"Hello world" by Anonymous',
- 'Test
',
- 'abcdef
\n'
- ].join('\n'));
- */
done();
});
}
@@ -76,3 +207,4 @@ describe('can render every sample layout', function() {
});
});
+*/
diff --git a/test/integration.test.js b/test/integration.test.js
index 28d94c4..bd3e82b 100644
--- a/test/integration.test.js
+++ b/test/integration.test.js
@@ -66,6 +66,8 @@ describe('integration tests', function() {
});
});
+ it('If there is no first heading, the file name is used (without the file extension)');
+
it('reads and scopes the meta.json based on the path relative to target directory', function(done) {
var dir = fixture.dir({
'meta.json': JSON.stringify({
@@ -74,7 +76,7 @@ describe('integration tests', function() {
}),
'foo.md': 'foo', // projectName foo
'foo/bar.md': 'bar', // projectName foo
- 'abc/bar/baz.md': 'baz', // projectName abc/bar
+ 'abc/bar/baz.md': 'baz' // projectName abc/bar
});
var templateDir = fixture.dir({
@@ -229,17 +231,10 @@ describe('integration tests', function() {
'aa
',
'foo
',
'' +
- 'var foo = bar;
b',
+ 'var foo = bar; b'
].join('\n'));
done();
});
});
-
- it('supports custom syntax highlighters for specific languages', function(done) {
- done();
- });
-
});
-
-
});
diff --git a/test/set-output-path.test.js b/test/set-output-path.test.js
index a8e0b79..788f081 100644
--- a/test/set-output-path.test.js
+++ b/test/set-output-path.test.js
@@ -5,7 +5,7 @@ var assert = require('assert'),
describe('set output path', function() {
it('can set a basic output path', function(done) {
- pi.fromArray([ { path: '/input/bar.md' }, { path: '/input/baz.md' } ])
+ pi.fromArray([{ path: '/input/bar.md' }, { path: '/input/baz.md' }])
.pipe(setOutputPath({
input: '/input',
output: '/output',