Skip to content

Commit

Permalink
Merge branch 'master' into highlight
Browse files Browse the repository at this point in the history
  • Loading branch information
stevenjoezhang committed Dec 12, 2022
2 parents f78fb51 + 40df922 commit 220819a
Show file tree
Hide file tree
Showing 26 changed files with 152 additions and 83 deletions.
26 changes: 15 additions & 11 deletions lib/extend/index.js
@@ -1,13 +1,17 @@
'use strict';

exports.Console = require('./console');
exports.Deployer = require('./deployer');
exports.Filter = require('./filter');
exports.Generator = require('./generator');
exports.Helper = require('./helper');
exports.Highlight = require('./highlight');
exports.Injector = require('./injector');
exports.Migrator = require('./migrator');
exports.Processor = require('./processor');
exports.Renderer = require('./renderer');
exports.Tag = require('./tag');
const Console = require('./console');
const Deployer = require('./deployer');
const Filter = require('./filter');
const Generator = require('./generator');
const Helper = require('./helper');
const Highlight = require('./highlight');
const Injector = require('./injector');
const Migrator = require('./migrator');
const Processor = require('./processor');
const Renderer = require('./renderer');
const Tag = require('./tag');

module.exports = {
Console, Deployer, Filter, Generator, Helper, Highlight, Injector, Migrator, Processor, Renderer, Tag
};
1 change: 1 addition & 0 deletions lib/extend/renderer.js
Expand Up @@ -52,6 +52,7 @@ class Renderer {
this.storeSync[name].output = output;

this.store[name] = Promise.method(fn);
this.store[name].disableNunjucks = fn.disableNunjucks;
} else {
if (fn.length > 2) fn = Promise.promisify(fn);
this.store[name] = fn;
Expand Down
10 changes: 2 additions & 8 deletions lib/hexo/index.js
Expand Up @@ -201,15 +201,9 @@ class Hexo extends EventEmitter {
return db.model('Page').find(query);
});

locals.set('categories', () => {
// Ignore categories with zero posts
return db.model('Category').filter(category => category.length);
});
locals.set('categories', () => db.model('Category'));

locals.set('tags', () => {
// Ignore tags with zero posts
return db.model('Tag').filter(tag => tag.length);
});
locals.set('tags', () => db.model('Tag'));

locals.set('data', () => {
const obj = {};
Expand Down
33 changes: 22 additions & 11 deletions lib/hexo/post.js
Expand Up @@ -376,19 +376,30 @@ class Post {
return Promise.reject(new Error('No input file or string!')).asCallback(callback);
}

// disable Nunjucks when the renderer specify that.
let disableNunjucks = false;
let extRenderer = ext && ctx.render.renderer.get(ext);
if (extRenderer) {
disableNunjucks = Boolean(extRenderer.disableNunjucks);
if (!Object.prototype.hasOwnProperty.call(extRenderer, 'disableNunjucks')) {
extRenderer = ctx.render.renderer.get(ext, true);
if (extRenderer) {
disableNunjucks = Boolean(extRenderer.disableNunjucks);
}
}
// Files like js and css are also processed by this function, but they do not require preprocessing like markdown
// data.source does not exist when tag plugins call the markdown renderer
const isPost = !data.source || ['html', 'htm'].includes(ctx.render.getOutput(data.source));

if (!isPost) {
return promise.then(content => {
data.content = content;
ctx.log.debug('Rendering file: %s', magenta(source));

return ctx.render.render({
text: data.content,
path: source,
engine: data.engine,
toString: true
});
}).then(content => {
data.content = content;
return data;
}).asCallback(callback);
}

// disable Nunjucks when the renderer specify that.
let disableNunjucks = ext && ctx.render.renderer.get(ext) && !!ctx.render.renderer.get(ext).disableNunjucks;

// front-matter overrides renderer's option
if (typeof data.disableNunjucks === 'boolean') disableNunjucks = data.disableNunjucks;

Expand Down
2 changes: 1 addition & 1 deletion lib/hexo/scaffold.js
Expand Up @@ -26,7 +26,7 @@ class Scaffold {
if (!exist) return [];

return listDir(scaffoldDir, {
ignoreFilesRegex: /^_|\/_/
ignorePattern: /^_|\/_/
});
}).map(item => ({
name: item.substring(0, item.length - extname(item).length),
Expand Down
4 changes: 3 additions & 1 deletion lib/models/category.js
Expand Up @@ -52,7 +52,9 @@ module.exports = ctx => {
});

Category.virtual('length').get(function() {
return this.posts.length;
const PostCategory = ctx.model('PostCategory');

return PostCategory.find({category_id: this._id}).length;
});

// Check whether a category exists
Expand Down
24 changes: 14 additions & 10 deletions lib/models/index.js
@@ -1,12 +1,16 @@
'use strict';

exports.Asset = require('./asset');
exports.Cache = require('./cache');
exports.Category = require('./category');
exports.Data = require('./data');
exports.Page = require('./page');
exports.Post = require('./post');
exports.PostAsset = require('./post_asset');
exports.PostCategory = require('./post_category');
exports.PostTag = require('./post_tag');
exports.Tag = require('./tag');
const Asset = require('./asset');
const Cache = require('./cache');
const Category = require('./category');
const Data = require('./data');
const Page = require('./page');
const Post = require('./post');
const PostAsset = require('./post_asset');
const PostCategory = require('./post_category');
const PostTag = require('./post_tag');
const Tag = require('./tag');

module.exports = {
Asset, Cache, Category, Data, Page, Post, PostAsset, PostCategory, PostTag, Tag
};
13 changes: 13 additions & 0 deletions lib/models/post.js
Expand Up @@ -71,7 +71,17 @@ module.exports = ctx => {
return Tag.find({_id: {$in: ids}});
});

Post.method('notPublished', function() {
// The same condition as ctx._bindLocals
return (!ctx.config.future && this.date > Date.now()) || (!ctx._showDrafts() && this.published === false);
});

Post.method('setTags', function(tags) {
if (this.notPublished()) {
// Ignore tags of draft posts
// If the post is unpublished then the tag needs to be removed, thus the function cannot be returned early here
tags = [];
}
tags = removeEmptyTag(tags);

const PostTag = ctx.model('PostTag');
Expand Down Expand Up @@ -119,6 +129,9 @@ module.exports = ctx => {
});

Post.method('setCategories', function(cats) {
if (this.notPublished()) {
cats = [];
}
// Remove empty categories, preserving hierarchies
cats = cats.filter(cat => {
return Array.isArray(cat) || (cat != null && cat !== '');
Expand Down
6 changes: 5 additions & 1 deletion lib/models/tag.js
Expand Up @@ -43,7 +43,11 @@ module.exports = ctx => {
});

Tag.virtual('length').get(function() {
return this.posts.length;
// Note: this.posts.length is also working
// But it's slow because `find` has to iterate over all posts
const PostTag = ctx.model('PostTag');

return PostTag.find({tag_id: this._id}).length;
});

// Check whether a tag exists
Expand Down
2 changes: 1 addition & 1 deletion lib/plugins/console/generate.js
Expand Up @@ -133,7 +133,7 @@ class Generater {
// Check the public folder
return stat(publicDir).then(stats => {
if (!stats.isDirectory()) {
throw new Error('%s is not a directory', magenta(tildify(publicDir)));
throw new Error(`${magenta(tildify(publicDir))} is not a directory`);
}
}).catch(err => {
// Create public folder if not exists
Expand Down
11 changes: 6 additions & 5 deletions lib/plugins/console/list/index.js
@@ -1,13 +1,14 @@
'use strict';

const abbrev = require('abbrev');
const page = require('./page');
const post = require('./post');
const route = require('./route');
const tag = require('./tag');
const category = require('./category');

const store = {
page: require('./page'),
post: require('./post'),
route: require('./route'),
tag: require('./tag'),
category: require('./category')
page, post, route, tag, category
};

const alias = abbrev(Object.keys(store));
Expand Down
16 changes: 12 additions & 4 deletions lib/plugins/console/new.js
Expand Up @@ -2,6 +2,7 @@

const tildify = require('tildify');
const { magenta } = require('picocolors');
const { basename } = require('path');

const reservedKeys = {
_: true,
Expand All @@ -21,16 +22,23 @@ const reservedKeys = {
};

function newConsole(args) {
// Display help message if user didn't input any arguments
if (!args._.length) {
const path = args.p || args.path;
let title;
if (args._.length) {
title = args._.pop();
} else if (path) {
// Default title
title = basename(path);
} else {
// Display help message if user didn't input any arguments
return this.call('help', {_: ['new']});
}

const data = {
title: args._.pop(),
title,
layout: args._.length ? args._[0] : this.config.default_layout,
slug: args.s || args.slug,
path: args.p || args.path
path
};

const keys = Object.keys(args);
Expand Down
2 changes: 1 addition & 1 deletion lib/plugins/helper/list_categories.js
Expand Up @@ -29,7 +29,7 @@ function listCategoriesHelper(categories, options) {
query.parent = {$exists: false};
}

return categories.find(query).sort(orderby, order).filter(cat => cat.length);
return categories.find(query).sort(orderby, order);
};

const hierarchicalList = (level, parent) => {
Expand Down
3 changes: 0 additions & 3 deletions lib/plugins/helper/list_tags.js
Expand Up @@ -47,9 +47,6 @@ function listTagsHelper(tags, options) {
// Sort the tags
tags = tags.sort(orderby, order);

// Ignore tags with zero posts
tags = tags.filter(tag => tag.length);

// Limit the number of tags
if (options.amount) tags = tags.limit(options.amount);

Expand Down
4 changes: 2 additions & 2 deletions lib/plugins/helper/number_format.js
Expand Up @@ -30,7 +30,7 @@ function numberFormatHelper(num, options = {}) {
const afterLast = after[precision];
const last = parseInt(after[precision - 1], 10);

afterResult = after.substring(0, precision - 1) + (afterLast < 5 ? last : last + 1);
afterResult = after.substring(0, precision - 1) + (Number(afterLast) < 5 ? last : last + 1);
} else {
afterResult = after;
for (let i = 0, len = precision - afterLength; i < len; i++) {
Expand All @@ -39,7 +39,7 @@ function numberFormatHelper(num, options = {}) {
}

after = afterResult;
} else if (precision === 0 || precision === '0') {
} else if (precision === 0) {
after = '';
}

Expand Down
6 changes: 1 addition & 5 deletions lib/plugins/helper/open_graph.js
@@ -1,7 +1,7 @@
'use strict';

const { isMoment, isDate } = require('moment');
const { encodeURL, prettyUrls, htmlTag, stripHTML, escapeHTML } = require('hexo-util');
const { encodeURL, prettyUrls, stripHTML, escapeHTML } = require('hexo-util');
const { default: moize } = require('moize');

const localeMap = {
Expand Down Expand Up @@ -169,10 +169,6 @@ function openGraphHelper(options = {}) {
result += meta('twitter:site', options.twitter_site, false);
}

if (options.google_plus) {
result += `${htmlTag('link', { rel: 'publisher', href: options.google_plus })}\n`;
}

if (options.fb_admins) {
result += og('fb:admins', options.fb_admins);
}
Expand Down
10 changes: 7 additions & 3 deletions lib/plugins/tag/img.js
Expand Up @@ -3,7 +3,8 @@
const { htmlTag, url_for } = require('hexo-util');

const rUrl = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[.!/\\w]*))?)/;
const rMeta = /["']?([^"']+)?["']?\s*["']?([^"']+)?["']?/;
const rMetaDoubleQuote = /"?([^"]+)?"?/;
const rMetaSingleQuote = /'?([^']+)?'?/;

/**
* Image tag
Expand All @@ -13,7 +14,7 @@ const rMeta = /["']?([^"']+)?["']?\s*["']?([^"']+)?["']?/;
*/
module.exports = ctx => {

return function imgTag(args, content) {
return function imgTag(args) {
const classes = [];
let src, width, height, title, alt;

Expand All @@ -38,7 +39,10 @@ module.exports = ctx => {
}
}

const match = rMeta.exec(args.join(' '));
const meta = args.join(' ');
const rMetaTitle = meta.startsWith('"') ? rMetaDoubleQuote : rMetaSingleQuote;
const rMetaAlt = meta.endsWith('"') ? rMetaDoubleQuote : rMetaSingleQuote;
const match = new RegExp(`${rMetaTitle.source}\\s*${rMetaAlt.source}`).exec(meta);

// Find image title and alt
if (match != null) {
Expand Down
5 changes: 3 additions & 2 deletions lib/plugins/tag/post_link.js
Expand Up @@ -8,7 +8,7 @@ const { postFindOneFactory } = require('./');
* Post link tag
*
* Syntax:
* {% post_link slug [title] [escape] %}
* {% post_link slug | title [title] [escape] %}
*/
module.exports = ctx => {
return function postLinkTag(args) {
Expand All @@ -24,7 +24,8 @@ module.exports = ctx => {
escape = 'true';
}

const post = postFindOneFactory(ctx)({ slug });
const factory = postFindOneFactory(ctx);
const post = factory({ slug }) || factory({ title: slug });
if (!post) {
throw new Error(`Post not found: post_link ${slug}.`);
}
Expand Down
5 changes: 3 additions & 2 deletions lib/plugins/tag/post_path.js
Expand Up @@ -8,14 +8,15 @@ const { postFindOneFactory } = require('./');
* Post path tag
*
* Syntax:
* {% post_path slug %}
* {% post_path slug | title %}
*/
module.exports = ctx => {
return function postPathTag(args) {
const slug = args.shift();
if (!slug) return;

const post = postFindOneFactory(ctx)({ slug });
const factory = postFindOneFactory(ctx);
const post = factory({ slug }) || factory({ title: slug });
if (!post) return;

const link = encodeURL(resolve(ctx.config.root, post.path));
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -77,7 +77,7 @@
"husky": "^8.0.1",
"lint-staged": "^13.0.3",
"mocha": "^10.0.0",
"sinon": "^14.0.0"
"sinon": "^15.0.0"
},
"engines": {
"node": ">=14"
Expand Down
2 changes: 1 addition & 1 deletion test/benchmark.js
Expand Up @@ -168,7 +168,7 @@ async function init() {
log.info('Setting up a dummy hexo site with 500 posts');
await gitClone('https://github.com/hexojs/hexo-theme-unit-test.git', testDir);
await gitClone('https://github.com/hexojs/hexo-theme-landscape', resolve(testDir, 'themes', 'landscape'));
await gitClone('https://github.com/SukkaLab/hexo-many-posts.git', resolve(testDir, 'source', '_posts', 'hexo-many-posts'));
await gitClone('https://github.com/hexojs/hexo-many-posts.git', resolve(testDir, 'source', '_posts', 'hexo-many-posts'));
}

log.info('Installing dependencies');
Expand Down

0 comments on commit 220819a

Please sign in to comment.