Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Making helper md native to fumanchu #48

Merged
merged 3 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/markdown.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';
const hljs = require('highlight.js');
const utils = require('handlebars-utils');
const Remarkable = require('remarkable');
const {Remarkable} = require('remarkable');

/**
* Expose markdown `helpers` (for performance we're using getters so
Expand Down Expand Up @@ -55,7 +55,7 @@ Object.defineProperty(helpers, 'markdown', {
* @api public
*/

helpers.md = require('helper-md');
helpers.md = require('./md.js');


helpers.helpersForMarkdown = function(config) {
Expand Down
119 changes: 119 additions & 0 deletions lib/md.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*!
* helper-markdown <https://github.com/jonschlinkert/helper-markdown>
*
* Copyright (c) 2014 Jon Schlinkert, contributors.
* Licensed under the MIT license.
*/

'use strict';

var fs = require('fs');
var path = require('path');
const { Remarkable } = require('remarkable');
var extend = require('extend-shallow');
var exists = require('fs-exists-sync');
var ent = require('ent');

/**
* Expose `md` helper
*/

var helper = module.exports = function(name, options, cb) {
if (typeof options === 'function') {
cb = options;
options = {};
}

if (typeof cb !== 'function') {
return helper.sync.apply(this, arguments);
}

/* c8 ignore next 3 */
if (typeof this === 'undefined' || typeof this.app === 'undefined') {
throw new Error('md async helper expects `app` to be exposed on the context');
}

var opts = extend({cwd: process.cwd()}, this.options, options);
opts = extend({}, opts, opts.hash);
var md = markdown(opts);

var filepath = path.resolve(opts.cwd, name);
var view;
var str = '';

if (exists(filepath)) {
// create a collection to ensure middleware is consistent
this.app.create('mdfiles');
str = fs.readFileSync(filepath, 'utf8');
view = this.app.mdfile(filepath, {path: filepath, content: str});
} else {
view = this.app.find(name);
}
/* c8 ignore next 4 */
if (typeof view === 'undefined') {
cb(null, '');
return;
}

view.content = ent.decode(md.render(view.content));
this.app.render(view, this.context, function(err, res) {
if (err) return cb(err);
cb(null, res.content);
});
};

helper.sync = function(name, options) {
var ctx = this || {};
var app = ctx.app || {};

var opts = extend({cwd: process.cwd()}, ctx.options, options);
opts = extend({}, opts, opts.hash);
var md = markdown(opts);

var filepath = path.resolve(opts.cwd, name);
var view;
var html = '';
var str = '';

if (exists(filepath)) {
str = fs.readFileSync(filepath, 'utf8');
html = ent.decode(md.render(str));
} else if (app.views) {
view = app.find(name);
if (view) {
html = view.content = ent.decode(md.render(view.content));
}
}

if (view && typeof view.compile === 'function') {
view.compile(opts);
var data = ctx.cache ? ctx.cache.data : {};
ctx = extend({}, data, view.data);
return view.fn(ctx);
}

if (typeof this.compile === 'function') {
var fn = this.compile(html);
return fn(this);
}
return html;
};

/**
* Shared settings for remarkable
*
* @param {Object} `options`
* @return {Object}
* @api private
*/

function markdown(options) {
let optsConfig = options || {};
optsConfig.breaks = true;
optsConfig.html = true;
optsConfig.langPrefix = 'lang-';
optsConfig.typographer = false;
optsConfig.xhtmlOut = false;
const md = new Remarkable(optsConfig);
return md;
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
"array-sort": "^1.0.0",
"create-frame": "^1.0.0",
"define-property": "^2.0.2",
"ent": "^2.2.0",
"falsey": "^1.0.0",
"for-in": "^1.0.2",
"for-own": "^1.0.0",
Expand All @@ -127,7 +128,6 @@
"handlebars-utils": "^1.0.6",
"has-value": "^2.0.2",
"helper-date": "^1.0.1",
"helper-md": "^0.2.2",
"highlight.js": "^11.9.0",
"html-tag": "^2.0.0",
"is-even": "^1.0.0",
Expand All @@ -138,13 +138,15 @@
"logging-helpers": "^1.0.0",
"micromatch": "^4.0.5",
"relative": "^3.0.2",
"remarkable": "^2.0.1",
"striptags": "^3.2.0",
"to-gfm-code-block": "^0.1.1",
"year": "^0.2.1"
},
"devDependencies": {
"c8": "^9.1.0",
"chai": "^4.3.10",
"lodash": "^4.17.21",
"mocha": "^10.4.0",
"rimraf": "^5.0.5",
"template-helpers": "^1.0.1",
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/a.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# AAA

> this is aaa
3 changes: 3 additions & 0 deletions test/fixtures/b.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# BBB

> this is bbb
3 changes: 3 additions & 0 deletions test/fixtures/c.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# CCC

This is {{name}}
3 changes: 3 additions & 0 deletions test/fixtures/d.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# DDD

This is <%= name %>
6 changes: 6 additions & 0 deletions test/fixtures/e.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# EEE

```
var message = 'This is an alert';
alert(message);
```
2 changes: 2 additions & 0 deletions test/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ describe('fs', function() {
path.join('lib', 'markdown.js'),
path.join('lib', 'match.js'),
path.join('lib', 'math.js'),
path.join('lib', 'md.js'),
path.join('lib', 'misc.js'),
path.join('lib', 'number.js'),
path.join('lib', 'object.js'),
Expand Down Expand Up @@ -86,6 +87,7 @@ describe('fs', function() {
path.join('lib', 'markdown.js'),
path.join('lib', 'match.js'),
path.join('lib', 'math.js'),
path.join('lib', 'md.js'),
path.join('lib', 'misc.js'),
path.join('lib', 'number.js'),
path.join('lib', 'object.js'),
Expand Down
147 changes: 147 additions & 0 deletions test/md.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*!
* helper-md <https://github.com/jonschlinkert/helper-md>
*
* Copyright (c) 2014 Jon Schlinkert, contributors.
* Licensed under the MIT License
*/

'use strict';

require('mocha');
var assert = require('assert');
var handlebars = require('handlebars');
var Templates = require('templates');
var hljs = require('highlight.js');
var md = require('../lib/md.js');
var _ = require('lodash');
var app;

describe('sync', function() {
beforeEach(function() {
app = new Templates();

app.helper('md', md.sync);
app.engine('md', require('engine-base'));
app.option('engine', 'md');

app.create('page');
app.create('partial', {viewType: ['partial']});
app.create('include', {viewType: ['partial']});

app.include('one', {content: '# heading <%= name %>', data: {name: 'one'}});
app.partial('two', {content: '# heading <%= name %>', data: {name: 'two'}});
});

it('should convert markdown on the `content` property of a template to HTML:', function(cb) {
app.page('home.md', {content: '<%= md("one") %>'});

app.render('home.md', function(err, view) {
if (err) return cb(err);
assert.equal(view.content, '<h1>heading one</h1>\n');
cb();
});
});

it('should support rendering markdown from a file:', function() {
assert.equal(md.sync('test/fixtures/a.md'), '<h1>AAA</h1>\n<blockquote>\n<p>this is aaa</p>\n</blockquote>\n');
});

describe('handlebars:', function() {
it('should support rendering markdown from a file:', function() {
handlebars.registerHelper('md', md.sync);
assert.equal(handlebars.compile('{{{md "test/fixtures/a.md"}}}')(), '<h1>AAA</h1>\n<blockquote>\n<p>this is aaa</p>\n</blockquote>\n');
});

it('should use the `render` function passed on the locals to render templates in partials :', function() {
handlebars.registerHelper('md', md.sync);
var locals = {name: 'CCC', compile: handlebars.compile};
assert.equal(handlebars.compile('{{{md "test/fixtures/c.md"}}}')(locals), '<h1>CCC</h1>\n<p>This is CCC</p>\n');
});
});
});

describe('async', function() {
beforeEach(function() {
app = new Templates();

app.asyncHelper('md', md);
app.engine('md', require('engine-base'));
app.option('engine', 'md');

app.create('page');
app.create('partial', {viewType: ['partial']});
app.create('include', {viewType: ['partial']});

app.include('one', {content: '# heading <%= name %>', data: {name: 'one'}});
app.partial('two', {content: '# heading <%= name %>', data: {name: 'two'}});
});

it('should convert markdown on the `content` property of a template to HTML:', function(cb) {
app.page('home.md', {content: '<%= md("one") %>'});

app.render('home.md', function(err, view) {
if (err) return cb(err);
assert.equal(view.content, '<h1>heading one</h1>\n');
cb();
});
});

it('should support rendering from a file', function(cb) {
app.page('home.md', {content: '<%= md("test/fixtures/d.md") %>'});

app.render('home.md', {name: 'DDD'}, function(err, view) {
if (err) return cb(err);
assert.equal(view.content, '<h1>DDD</h1>\n<p>This is DDD</p>\n');
cb();
});
});

it('should use sync helper when a callback is not passed:', function(cb) {
app.helper('md2', md);
app.page('home.md', {content: '<%= md2("one") %>'});

app.render('home.md', function(err, view) {
if (err) return cb(err);
assert.equal(view.content, '<h1>heading one</h1>\n');
cb();
});
});
});

describe('lodash:', function() {
it('should work as a lodash mixin:', function() {
_.mixin({md: md.sync});
assert.equal(_.template('<%= _.md("test/fixtures/a.md") %>', {})(), '<h1>AAA</h1>\n<blockquote>\n<p>this is aaa</p>\n</blockquote>\n');
});

it('should work when passed to lodash on the locals:', function() {
assert.equal(_.template('<%= _.md("test/fixtures/a.md") %>')({md: md.sync}), '<h1>AAA</h1>\n<blockquote>\n<p>this is aaa</p>\n</blockquote>\n');
});

it('should work as a lodash import:', function() {
var settings = {imports: {md: md.sync}};
assert.equal(_.template('<%= _.md("test/fixtures/a.md") %>', {}, settings)(), '<h1>AAA</h1>\n<blockquote>\n<p>this is aaa</p>\n</blockquote>\n');
});
});

describe('highlight:', function(argument) {
it('should support syntax highlighting', function() {
var actual = md.sync('test/fixtures/e.md', {
highlight: function(code, lang) {
try {
try {
return hljs.highlight(lang, code).value;
} catch (err) {
if (!/Unknown language/i.test(err.message)) {
throw err;
}
return hljs.highlightAuto(code).value;
}
} catch (err) {
return code;
}
}
});
assert.equal(actual, '<h1>EEE</h1>\n<pre><code><span class="hljs-keyword">var</span> <span class="hljs-keyword">message</span> = <span class="hljs-string">\'This is an alert\'</span>;\nalert(<span class="hljs-keyword">message</span>);\n</code></pre>\n');
});
});
Loading