Fix style, simplify logic related to layouts

This commit is contained in:
Mikito Takada 2015-01-29 22:23:06 -08:00
parent 5fb468fd0a
commit 34820811af
25 changed files with 379 additions and 96 deletions

4
.gitignore vendored
View File

@ -1 +1,5 @@
node_modules/
output/*
!output/menu.html
!output/index.html

3
.jshintignore Normal file
View File

@ -0,0 +1,3 @@
node_modules/
layouts/
output/

View File

@ -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

View File

@ -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);

View File

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

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link href="{{asset 'style.css'}}" rel="stylesheet"></link>
<link type="text/css" rel="stylesheet" href="{{asset 'hljs-github.min.css'}}"/>
</head>
<body>
{{~> content}}
</body>
</html>

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link href="{{asset 'style.css'}}" rel="stylesheet"></link>
<link type="text/css" rel="stylesheet" href="{{asset 'hljs-github.min.css'}}"/>
</head>
<body>
{{~> content}}
</body>
</html>

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link href="{{asset 'style.css'}}" rel="stylesheet"></link>
<link type="text/css" rel="stylesheet" href="{{asset 'hljs-github.min.css'}}"/>
</head>
<body>
{{~> content}}
</body>
</html>

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link href="{{asset 'style.css'}}" rel="stylesheet"></link>
<link type="text/css" rel="stylesheet" href="{{asset 'hljs-github.min.css'}}"/>
</head>
<body>
{{~> content}}
</body>
</html>

View File

@ -29,14 +29,14 @@ function loadHelpers(dir) {
module.exports = function(opts) {
if (opts.partials && !Array.isArray(opts.partials)) {
opts.partials = [ opts.partials ];
opts.partials = [opts.partials];
}
if (!opts.partials) {
opts.partials = [];
}
if (opts.helpers && !Array.isArray(opts.helpers)) {
opts.helpers = [ opts.helpers ];
opts.helpers = [opts.helpers];
}
if (!opts.helpers) {
opts.helpers = [];
@ -53,7 +53,7 @@ module.exports = function(opts) {
return pi.forEach(function(item, enc, done) {
item.contents = item.contents.replace(/<(ul|ol)>/g, '<$1 class="list">')
.replace("{{&gt;", "{{>")
.replace('{{&gt;', '{{>')
.replace(/<pre><code[^>]*>([\s\S]*?)<\/code><\/pre>/mg, '<pre class="prettyprint">$1</pre>')
.replace(/<p><img([^>]*)>\s*<\/p>/g, '<p class="img-container"><img$1></p>');

View File

@ -2,4 +2,4 @@ module.exports = {
resolveArgs: require('./resolve-args'),
render: require('./render'),
pipeline: require('./pipeline')
}
};

View File

@ -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;

View File

@ -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' : []
})
]);
};

View File

@ -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.');
}
};

View File

@ -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-<extension>
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;
};

View File

@ -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'));

View File

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

View File

@ -1,6 +1,6 @@
var path = require('path'),
fs = require('fs'),
pi = require('pipe-iterators')
pi = require('pipe-iterators'),
mkdirp = require('mkdirp');
function dest() {

17
output/index.html Normal file
View File

@ -0,0 +1,17 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>
<title>Markdown CSS</title>
</head>
<frameset rows="60,*" frameborder="0" border="0" framespacing="0">
<frame name="menu" src="menu.html" marginheight="0" marginwidth="0" scrolling="auto" noresize>
<frame name="content" src="github/index.html" marginheight="0" marginwidth="0" scrolling="auto" noresize>
<noframes>
<p>Yeah, old school frames!</p>
</noframes>
</frameset>
</html>

51
output/menu.html Normal file
View File

@ -0,0 +1,51 @@
<html>
<head>
<title>Menu</title>
<style type="text/css">
body {
background-color: #EEE;
border-bottom: 1px solid #DDD;
}
p {
padding-top: 6px;
padding-left: 6px;
}
</style>
</head>
<body>
<p>
<a href="mixu-book/index.html" target="content">mixu-book</a>
<a href="mixu-page/index.html" target="content">mixu-page</a>
<a href="mixu-radar/index.html" target="content">mixu-radar</a>
<a href="mixu-bootstrap/index.html" target="content">mixu-bootstrap</a>
<a href="mixu-bootstrap-2col/index.html" target="content">mixu-bootstrap-2col</a>
<a href="mixu-gray/index.html" target="content">mixu-gray</a>
<a href="github/index.html" target="content">github</a>
<a href="jasonm23-markdown/index.html" target="content">jasonm23-markdown</a>
<a href="jasonm23-foghorn/index.html" target="content">jasonm23-foghorn</a>
<a href="jasonm23-dark/index.html" target="content">jasonm23-dark</a>
<a href="jasonm23-swiss/index.html" target="content">jasonm23-swiss</a>
<a href="markedapp-byword/index.html" target="content">markedapp-byword</a>
<a href="thomasf-solarizedcsslight/index.html" target="content">thomasf-solarizedcsslight</a>
<a href="thomasf-solarizedcssdark/index.html" target="content">thomasf-solarizedcssdark</a>
<a href="bootstrap3/index.html" target="content">bootstrap3</a>
</p>
</body>
</html>

View File

@ -38,6 +38,7 @@
"yargs": "~1.1.3"
},
"devDependencies": {
"file-fixture": "0.0.2"
"file-fixture": "0.0.2",
"mds-csv": "0.0.0"
}
}

View File

@ -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': '<p>foo</p>',
'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 ',
'<h1 id="some-title">Some title</h1>',
'<p>abcdef</p>\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',
'<h1 id="test">Test</h1>',
'<p>abcdef</p>\n'
].join('\n'));
*/
done();
});
}
@ -76,3 +207,4 @@ describe('can render every sample layout', function() {
});
});
*/

View File

@ -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() {
'a<p>a</p>',
'<h1 id="foo">foo</h1>',
'<pre class="hljs"><code>' +
'<span class="hljs-keyword">var</span> foo = bar;</code></pre>b',
'<span class="hljs-keyword">var</span> foo = bar;</code></pre>b'
].join('\n'));
done();
});
});
it('supports custom syntax highlighters for specific languages', function(done) {
done();
});
});
});

View File

@ -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',