diff --git a/lib/highlight.js b/lib/highlight.js index 44a5210..e0dcd7d 100644 --- a/lib/highlight.js +++ b/lib/highlight.js @@ -2,8 +2,8 @@ // https://github.com/lepture/nico/blob/master/lib/sdk/highlight.js 'use strict' -var format = require('util').format; -var hl = require('highlight.js'); +const format = require('util').format; +const hl = require('highlight.js'); var escape = function(html) { @@ -23,7 +23,7 @@ exports.render = function(code, language) { if (language === 'html') { language = 'xml'; } - code = hl.highlight(language, code).value; + code = hl.highlight(code, { language }).value; return format( '
%s
', language, code diff --git a/lib/parsers/page.js b/lib/parsers/page.js index e8b66a0..22a88c9 100644 --- a/lib/parsers/page.js +++ b/lib/parsers/page.js @@ -18,6 +18,7 @@ function Page(attrs) { this.slug = path.basename(fpath, this.ext) this.path = path.relative(this.site.cwd, fpath).replaceAll(path.sep, '/') this.title = util.capitalize(this.slug) + this.type = 'pages'; if (this.validFormat && fs.existsSync(fpath)) { var content = fs.readFileSync(fpath, this.site.encoding) @@ -33,7 +34,7 @@ function Page(attrs) { } } - this.url = `/${this.path}`.replace(/\/index\.(?:md|html)$/, '') + this.url = `/${this.path}`.replace(/\/index\.(?:md|html)$/, '').replace(/\.md$/, '.html') this.dest = path.join(this.site.dest, this.site.baseurl.slice(1), this.path.replace(/\.\w+$/, this.ext == '.md' ? '.html' : this.ext)) diff --git a/lib/parsers/post.js b/lib/parsers/post.js index b4eb778..7ce5fac 100644 --- a/lib/parsers/post.js +++ b/lib/parsers/post.js @@ -31,6 +31,7 @@ function Post(attrs) { parseInt(date[2], 10) ) this.title = util.capitalize(this.slug) + this.type = fpath.includes('_drafts/') ? 'drafts' : 'posts'; if (fs.existsSync(fpath)) { var content = fs.readFileSync(fpath, this.site.encoding) diff --git a/lib/writers/templated.js b/lib/writers/templated.js index 46ee081..08cbec6 100644 --- a/lib/writers/templated.js +++ b/lib/writers/templated.js @@ -4,7 +4,7 @@ const path = require('path') const yaml = require('yaml-js') const mkdirp = require('mkdirp') const debug = require('debug')('darko') -const fs = require('fs') +const fs = require('fs/promises') const md = require('../markdown') const engine = require('../liquid') @@ -19,7 +19,6 @@ const mkdirpAsync = function(dir) { }) } - function liquid(tpl, site) { if (!engine.site) { engine.registerFileSystem( @@ -36,21 +35,21 @@ function markup(page) { return page } -var _layouts = {} +const _layouts = {} async function layout(page, data) { + const site = page.site const { layout: layoutName } = data || page; if (!layoutName || layoutName == 'nil') { return page } - const site = page.site // support .html layouts only, for now. const lpath = path.join(site.cwd, '_layouts', layoutName) + '.html' let lcache = _layouts[lpath] if (!lcache) { - let lsource = fs.readFileSync(lpath, site.encoding) + let lsource = await fs.readFile(lpath, site.encoding) const parts = lsource.split('---') let matter @@ -68,7 +67,7 @@ async function layout(page, data) { const template = await lcache.promise const res = await template.render({ site: site, - page: page, + page: {...page, ...data}, content: page.output }) page.output = res @@ -76,39 +75,52 @@ async function layout(page, data) { return page } -function render(page) { - fs.writeFileSync(page.dest, page.output) +async function render(page) { + await fs.writeFile(page.dest, page.output) debug('Generated ' + page.path) } +function getDefaults(page) { + const { site } = page; + if (!site.defaults) return; + + const defaults = site.defaults.filter(function({ scope }) { + if (!scope) return true; + let match = true; + if (scope.path) { + if (scope.path.includes('*')) { + match = new RegExp(`^${scope.path.replace('*', '.*?')}`).test(page.path); + } else { + const scopePath = scope.path ? scope.path + '/' : ''; + match = page.path.startsWith(scopePath) || page.path === scope.path; + } + } + if (scope.type && match) { + match = scope.type === page.type; + } + return match; + }) + return Object.assign({}, ...defaults.map(d => d.values)); +} -module.exports = function writeTemplated(page) { +module.exports = async function writeTemplated(page) { debug('Generating ' + page.path) - - return mkdirpAsync(path.dirname(page.dest)) - .then(function() { - return liquid(page.content, page.site) - }) - .then(function(template) { - return template.render({ - site: page.site, - page: page - }) - }) - .then(function(res) { - page.contentWas = page.content - page.content = res - return page - }) - .then(markup) - .then(layout) - .then(render) - .then(function() { - page.content = page.contentWas - return page - }) - .catch(function(err) { - console.error('Failed to generate ' + page.path + ':') - throw err + try { + await mkdirpAsync(path.dirname(page.dest)) + const template = await liquid(page.content, page.site) + const res = await template.render({ + site: page.site, + page: page }) + let content = page.content; + page.content = res; + await markup(page) + await layout(page, getDefaults(page)) + await render(page) + page.content = content; + return page + } catch (err) { + console.error('Failed to generate ' + page.path + ':') + throw err + } } diff --git a/package.json b/package.json index 945e24e..f9e3e73 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "darko-serve": "bin/darko-serve" }, "scripts": { - "test": "mocha test/*.js" + "test": "mocha --recursive test" }, "devDependencies": { "expect.js": "^0.3.1", diff --git a/test/fixture/_config.yml b/test/fixture/basic/_config.yml similarity index 100% rename from test/fixture/_config.yml rename to test/fixture/basic/_config.yml diff --git a/test/fixture/_data/members.yml b/test/fixture/basic/_data/members.yml similarity index 100% rename from test/fixture/_data/members.yml rename to test/fixture/basic/_data/members.yml diff --git a/test/fixture/_drafts/2014-01-09-bad.md b/test/fixture/basic/_drafts/2014-01-09-bad.md similarity index 100% rename from test/fixture/_drafts/2014-01-09-bad.md rename to test/fixture/basic/_drafts/2014-01-09-bad.md diff --git a/test/fixture/_includes/archive.html b/test/fixture/basic/_includes/archive.html similarity index 100% rename from test/fixture/_includes/archive.html rename to test/fixture/basic/_includes/archive.html diff --git a/test/fixture/_includes/footer.html b/test/fixture/basic/_includes/footer.html similarity index 100% rename from test/fixture/_includes/footer.html rename to test/fixture/basic/_includes/footer.html diff --git a/test/fixture/_layouts/page.html b/test/fixture/basic/_layouts/page.html similarity index 100% rename from test/fixture/_layouts/page.html rename to test/fixture/basic/_layouts/page.html diff --git a/test/fixture/_layouts/post.html b/test/fixture/basic/_layouts/post.html similarity index 100% rename from test/fixture/_layouts/post.html rename to test/fixture/basic/_layouts/post.html diff --git a/test/fixture/_posts/2014-01-05-bar.md b/test/fixture/basic/_posts/2014-01-05-bar.md similarity index 100% rename from test/fixture/_posts/2014-01-05-bar.md rename to test/fixture/basic/_posts/2014-01-05-bar.md diff --git a/test/fixture/_posts/2014-01-05-foo.md b/test/fixture/basic/_posts/2014-01-05-foo.md similarity index 100% rename from test/fixture/_posts/2014-01-05-foo.md rename to test/fixture/basic/_posts/2014-01-05-foo.md diff --git a/test/fixture/_posts/2014-01-06-good.md b/test/fixture/basic/_posts/2014-01-06-good.md similarity index 100% rename from test/fixture/_posts/2014-01-06-good.md rename to test/fixture/basic/_posts/2014-01-06-good.md diff --git a/test/fixture/_posts/2014-01-10-baz.md b/test/fixture/basic/_posts/2014-01-10-baz.md similarity index 100% rename from test/fixture/_posts/2014-01-10-baz.md rename to test/fixture/basic/_posts/2014-01-10-baz.md diff --git a/test/fixture/assets/base.css b/test/fixture/basic/assets/base.css similarity index 100% rename from test/fixture/assets/base.css rename to test/fixture/basic/assets/base.css diff --git a/test/fixture/assets/darko.svg b/test/fixture/basic/assets/darko.svg similarity index 100% rename from test/fixture/assets/darko.svg rename to test/fixture/basic/assets/darko.svg diff --git a/test/fixture/catus/_posts/2014-01-05-ham.md b/test/fixture/basic/catus/_posts/2014-01-05-ham.md similarity index 100% rename from test/fixture/catus/_posts/2014-01-05-ham.md rename to test/fixture/basic/catus/_posts/2014-01-05-ham.md diff --git a/test/fixture/felis/_posts/2014-01-05-egg.md b/test/fixture/basic/felis/_posts/2014-01-05-egg.md similarity index 100% rename from test/fixture/felis/_posts/2014-01-05-egg.md rename to test/fixture/basic/felis/_posts/2014-01-05-egg.md diff --git a/test/fixture/felis/index.md b/test/fixture/basic/felis/index.md similarity index 100% rename from test/fixture/felis/index.md rename to test/fixture/basic/felis/index.md diff --git a/test/fixture/index.md b/test/fixture/basic/index.md similarity index 100% rename from test/fixture/index.md rename to test/fixture/basic/index.md diff --git a/test/fixture/defaults/_config.yml b/test/fixture/defaults/_config.yml new file mode 100644 index 0000000..438da10 --- /dev/null +++ b/test/fixture/defaults/_config.yml @@ -0,0 +1,11 @@ +defaults: + - scope: + path: "" # An empty string means all files in the project + values: + layout: default + - scope: + path: "projects" # All files in the projects folder + type: pages + values: + layout: project + author: "Jane Smith" \ No newline at end of file diff --git a/test/fixture/defaults/_layouts/default.html b/test/fixture/defaults/_layouts/default.html new file mode 100644 index 0000000..9bec4d7 --- /dev/null +++ b/test/fixture/defaults/_layouts/default.html @@ -0,0 +1,4 @@ + +{{ content }} \ No newline at end of file diff --git a/test/fixture/defaults/_layouts/project.html b/test/fixture/defaults/_layouts/project.html new file mode 100644 index 0000000..e9fc477 --- /dev/null +++ b/test/fixture/defaults/_layouts/project.html @@ -0,0 +1,7 @@ +--- +layout: default +--- + +

{{ page.title }} - Project Detail

+{{ page.author }} +{{ content }} \ No newline at end of file diff --git a/test/fixture/defaults/index.md b/test/fixture/defaults/index.md new file mode 100644 index 0000000..efe0160 --- /dev/null +++ b/test/fixture/defaults/index.md @@ -0,0 +1,7 @@ +hunter x hunter + + \ No newline at end of file diff --git a/test/fixture/defaults/projects/bamboo.md b/test/fixture/defaults/projects/bamboo.md new file mode 100644 index 0000000..d7c7fe2 --- /dev/null +++ b/test/fixture/defaults/projects/bamboo.md @@ -0,0 +1,4 @@ +--- +--- + +毛竹 yum yum \ No newline at end of file diff --git a/test/site.test.js b/test/site.test.js deleted file mode 100644 index 492ed1d..0000000 --- a/test/site.test.js +++ /dev/null @@ -1,92 +0,0 @@ -'use strict' - -var path = require('path') -var expect = require('expect.js') -var fs = require('fs') - -var util = require('..').util -var Site = require('..').Site - -var exists = fs.existsSync - - -var site = new Site({ - cwd: path.join(__dirname, './fixture') -}) - -describe('basic attributes of Site', function() { - - it('should set cwd and dest', function() { - expect(site.cwd.replaceAll(path.sep, '/')).to.contain('test/fixture') - expect(site.dest.replaceAll(path.sep, '/')).to.contain('test/fixture/_site') - }) - - it('should parse _config.yml', function() { - expect(site.baseurl).to.be.a('string') - }) -}) - - -describe('posts, pages, and statics parsing of Site', function() { - before(function() { - site.parse() - }) - - it('should parse posts', function() { - expect(site.posts).not.to.be.empty() - - var postLinks = site.posts.map(function(post) { - return post.url - }) - - expect(postLinks).to.contain('/catus/ham.html') - expect(postLinks).to.contain('/felis/egg.html') - - // _posts/baz should not be published because it has the published property - // set to false in the YAML front mattter. - expect(postLinks).to.not.contain('/baz.html') - - // _drafts/bad should not be published because it is in the drafts folder. - expect(postLinks).to.not.contain('/bad.html') - }) - - it('should parse pages', function() { - expect(site.pages).not.to.be.empty() - - var pageLinks = site.pages.map(function(page) { - return page.url - }) - - // index.md should be parsed as the homepage. That is, a page with its url - // set to empty string because the /index.html part is removed. - expect(pageLinks).to.contain('') - - // felis/index.md should be parse as a page with its url set to /felis - expect(pageLinks).to.contain('/felis') - }) -}) - - -describe('posts, pages, and statics writing of Site', function() { - before(function(done) { - site.write() - .then(function() { - util.log('Generating', '... done') - done() - }) - .catch(done) - }) - - it('should create destination folder', function() { - expect(exists(site.dest)).to.be.ok() - }) - - it('should write posts', function() { - }) - - it('should write pages', function() { - }) - - it('should write statics', function() { - }) -}) diff --git a/test/liquid.test.js b/test/unit/liquid.test.js similarity index 97% rename from test/liquid.test.js rename to test/unit/liquid.test.js index c9097bd..97c27c0 100644 --- a/test/liquid.test.js +++ b/test/unit/liquid.test.js @@ -1,14 +1,14 @@ 'use strict' var expect = require('expect.js') -var engine = require('..').Liquid +var engine = require('../..').Liquid function liquid(tpl, data) { return engine.parseAndRender(tpl, data) } -describe('Liquid', function() { +describe('test/unit/liquid.test.js', function() { const timezoneOffset = new Date().getTimezoneOffset(); const timezone = [ timezoneOffset > 0 ? '-' : '+', diff --git a/test/unit/parsers/page.test.js b/test/unit/parsers/page.test.js new file mode 100644 index 0000000..e447697 --- /dev/null +++ b/test/unit/parsers/page.test.js @@ -0,0 +1,30 @@ +'use strict'; + +const { Site } = require('../../..'); +const expect = require('expect.js'); +const path = require('path'); + +describe('test/unit/parsers/page.test.js', function() { + let site; + before(function() { + site = new Site({ cwd: path.resolve(__dirname, '../../fixture/basic') }); + site.parse(); + }); + + describe('page.url', function() { + it('should generate correct url for pages', function() { + expect(site.pages).not.to.be.empty() + + const pageLinks = site.pages.map(function(page) { + return page.url + }) + + // index.md should be parsed as the homepage. That is, a page with its url + // set to empty string because the /index.html part is removed. + expect(pageLinks).to.contain('') + + // felis/index.md should be parse as a page with its url set to /felis + expect(pageLinks).to.contain('/felis') + }); + }); +}); \ No newline at end of file diff --git a/test/unit/parsers/post.test.js b/test/unit/parsers/post.test.js new file mode 100644 index 0000000..e3bcc06 --- /dev/null +++ b/test/unit/parsers/post.test.js @@ -0,0 +1,41 @@ +'use strict'; + +const { Site } = require('../../..'); +const expect = require('expect.js'); +const path = require('path'); + +describe('test/unit/parsers/post.test.js', function() { + let site; + before(function() { + site = new Site({ cwd: path.resolve(__dirname, '../../fixture/basic') }); + site.parse(); + }); + + describe('post.type', function() { + it('should be "posts" for posts', function() { + const post = site.posts.find(p => p.slug === 'ham'); + if (!post) throw new Error('Post "ham" not found'); + expect(post.type).to.equal('posts'); + }); + }); + + describe('post.url', function() { + it('should generate correct url for posts', function() { + expect(site.posts).not.to.be.empty() + + const postLinks = site.posts.map(function(post) { + return post.url + }) + + expect(postLinks).to.contain('/catus/ham.html') + expect(postLinks).to.contain('/felis/egg.html') + + // _posts/baz should not be published because it has the published property + // set to false in the YAML front matter. + expect(postLinks).to.not.contain('/baz.html') + + // _drafts/bad should not be published because it is in the drafts folder. + expect(postLinks).to.not.contain('/bad.html') + }); + }); +}); \ No newline at end of file diff --git a/test/unit/site.test.js b/test/unit/site.test.js new file mode 100644 index 0000000..70aa7d0 --- /dev/null +++ b/test/unit/site.test.js @@ -0,0 +1,42 @@ +'use strict' + +const path = require('path') +const expect = require('expect.js') +const Site = require('../..').Site + + + +describe('test/unit/site.test.js', function() { + + describe('basic site', function() { + const cwd = path.resolve(__dirname, '../fixture/basic'); + let site; + before(function() { + site = new Site({ cwd: cwd }); + site.parse(); + }); + + it('should set cwd and dest', function() { + expect(site.cwd).to.equal(cwd); + expect(site.dest).to.equal(path.resolve(cwd, '_site')); + }); + + it('should parse _config.yml', function() { + expect(site.baseurl).to.be.a('string'); + }); + }); + + describe('defaults', function() { + const cwd = path.resolve(__dirname, '../fixture/defaults'); + let site; + before(function() { + site = new Site({ cwd: cwd }); + site.parse(); + }); + + it('should hold default values from _config.yml', function() { + expect(Array.isArray(site.defaults)).to.be(true); + expect(site.defaults.length).to.be(2); + }); + }); +}); diff --git a/test/unit/writers/sass.test.js b/test/unit/writers/sass.test.js new file mode 100644 index 0000000..e69de29 diff --git a/test/unit/writers/templated.test.js b/test/unit/writers/templated.test.js new file mode 100644 index 0000000..2de794d --- /dev/null +++ b/test/unit/writers/templated.test.js @@ -0,0 +1,30 @@ +'use strict'; + +const { Site, util } = require('../../..'); +const expect = require('expect.js'); +const { existsSync: exists } = require('fs'); +const path = require('path'); + +describe('test/unit/writers/templated.test.js', function() { + let site; + before(async function() { + site = new Site({ cwd: path.resolve(__dirname, '../../fixture/basic') }); + site.parse(); + await site.write(); + util.log('Generating', '... done'); + }); + + describe('site.dest', function() { + it('should create destination folder', function() { + expect(exists(site.dest)).to.be.ok(); + }); + }); + + describe('site.posts', function() { + it('should write posts', function() { + const post = site.posts.find(p => p.slug === 'ham'); + if (!post) throw new Error('Post "ham" not found'); + expect(exists(post.dest)).to.be.ok(); + }); + }); +}); \ No newline at end of file