From 9e3360a9af3c7842636519cb82a8c773ca7dd526 Mon Sep 17 00:00:00 2001 From: Abner Chou Date: Thu, 15 Jul 2021 23:30:23 -0400 Subject: [PATCH 001/105] Update sponsors --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 248a92bd21..f95f409676 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ Also, we welcome PR or issue to [official-plugins](https://github.com/hexojs). ## Sponsors -JetBrains +RSS3 ## License From 042f86294691f7a18ab27b853dd7066d6aed1408 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 25 Jul 2021 09:39:14 +0200 Subject: [PATCH 002/105] fix(helper): escape HTML by default in list_tag helper (#4743) BREAKING CHANGES: escape the html tag values. It should be text, not HTML. This is a breaking change and documentation should be updated to explain that when using function transform, the value should be escaped if needed. --- lib/plugins/helper/list_tags.js | 4 ++-- test/scripts/helpers/list_tags.js | 38 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/lib/plugins/helper/list_tags.js b/lib/plugins/helper/list_tags.js index 252eb62ef0..6d56850515 100644 --- a/lib/plugins/helper/list_tags.js +++ b/lib/plugins/helper/list_tags.js @@ -1,6 +1,6 @@ 'use strict'; -const { url_for } = require('hexo-util'); +const { url_for, escapeHTML } = require('hexo-util'); function listTagsHelper(tags, options) { if (!options && (!tags || !Object.prototype.hasOwnProperty.call(tags, 'length'))) { @@ -59,7 +59,7 @@ function listTagsHelper(tags, options) { result += `
  • `; result += `'; if (showCount) { diff --git a/test/scripts/helpers/list_tags.js b/test/scripts/helpers/list_tags.js index f4d3ed62d3..7e07280fb7 100644 --- a/test/scripts/helpers/list_tags.js +++ b/test/scripts/helpers/list_tags.js @@ -205,3 +205,41 @@ describe('list_tags', () => { ].join('')); }); }); + +describe('list_tags transform', () => { + const Hexo = require('../../../lib/hexo'); + const hexo = new Hexo(__dirname); + const Post = hexo.model('Post'); + + const ctx = { + config: hexo.config + }; + + const listTags = require('../../../lib/plugins/helper/list_tags').bind(ctx); + + before(async () => { + await hexo.init(); + const posts = await Post.insert([ + {source: 'foo', slug: 'foo'} + ]); + + // TODO: Warehouse needs to add a mutex lock when writing data to avoid data sync problem + await Promise.all([ + ['badHTML'] + ].map((tags, i) => posts[i].setTags(tags))); + + hexo.locals.invalidate(); + ctx.site = hexo.locals.toObject(); + }); + + // no transform should escape HTML + it('no transform', () => { + const result = listTags(); + + result.should.eql([ + '' + ].join('')); + }); +}); From bb61f15716a8fd267f01473e6dc7f1e9f83c8f9c Mon Sep 17 00:00:00 2001 From: Mimi <1119186082@qq.com> Date: Tue, 21 Sep 2021 02:32:38 +0800 Subject: [PATCH 003/105] chore: drop Node 10 (#4779) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a77858ef8c..bccd4cd607 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "sinon": "^10.0.0" }, "engines": { - "node": ">=10.13.0" + "node": ">=12.13.0" }, "husky": { "hooks": { From c18d5756c0a9d697463a8e9d1b289bd9c5ba1a76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Sep 2021 03:19:14 +0800 Subject: [PATCH 004/105] chore: bump mocha from 8.4.0 to 9.1.1 (#4765) Bumps [mocha](https://github.com/mochajs/mocha) from 8.4.0 to 9.1.1. - [Release notes](https://github.com/mochajs/mocha/releases) - [Changelog](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md) - [Commits](https://github.com/mochajs/mocha/compare/v8.4.0...v9.1.1) --- updated-dependencies: - dependency-name: mocha dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bccd4cd607..270bf4932e 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "hexo-renderer-marked": "^4.0.0", "husky": "^4.2.5", "lint-staged": "^11.0.0", - "mocha": "^8.0.1", + "mocha": "^9.1.1", "nyc": "^15.0.0", "sinon": "^10.0.0" }, From 6164db1b005897366a327f9f891621c900094353 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Sep 2021 08:22:20 +0800 Subject: [PATCH 005/105] chore: bump sinon from 10.0.1 to 11.1.2 (#4747) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 270bf4932e..2d1f32a234 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "lint-staged": "^11.0.0", "mocha": "^9.1.1", "nyc": "^15.0.0", - "sinon": "^10.0.0" + "sinon": "^11.1.2" }, "engines": { "node": ">=12.13.0" From 67fc8446d3bd18081209d842f4cdf9434bcf1e9d Mon Sep 17 00:00:00 2001 From: rui Date: Mon, 20 Sep 2021 20:31:09 -0400 Subject: [PATCH 006/105] doc: add homebrew install (#4724) --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index f95f409676..6ed5f7bcee 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,12 @@ $ npm install hexo-cli -g ``` +Install with [brew](https://brew.sh/) on macOS and Linux: + +```bash +$ brew install hexo +``` + **Setup your blog** ``` bash From 0c979bffe511ad45631ee0e2682e3f352224cd25 Mon Sep 17 00:00:00 2001 From: Sukka Date: Tue, 21 Sep 2021 08:40:50 +0800 Subject: [PATCH 007/105] refactor(post): use state machine to escape swig tag (#4780) --- lib/hexo/post.js | 150 +++++++++++++++++++++++++++++++---- test/fixtures/post_render.js | 12 +-- test/scripts/hexo/post.js | 22 +++++ 3 files changed, 163 insertions(+), 21 deletions(-) diff --git a/lib/hexo/post.js b/lib/hexo/post.js index ff16819c8a..bc0214e826 100644 --- a/lib/hexo/post.js +++ b/lib/hexo/post.js @@ -12,9 +12,6 @@ const { parse: yfmParse, split: yfmSplit, stringify: yfmStringify } = require('h const preservedKeys = ['title', 'slug', 'path', 'layout', 'date', 'content']; const rHexoPostRenderEscape = /([\s\S]+?)<\/hexoPostRenderCodeBlock>/g; -const rSwigVarAndComment = /{[{#][\s\S]+?[}#]}/g; -const rSwigFullBlock = /{% *(\S+?)(?: *| +.+?)%}[\s\S]+?{% *end\1 *%}/g; -const rSwigBlock = /{%[\s\S]+?%}/g; const rSwigPlaceHolder = /(?:<|<)!--swig\uFFFC(\d+)--(?:>|>)/g; const rCodeBlockPlaceHolder = /(?:<|<)!--code\uFFFC(\d+)--(?:>|>)/g; @@ -28,30 +25,153 @@ const _restoreContent = cache => (_, index) => { return value; }; -class PostRenderCache { +const STATE_PLAINTEXT = Symbol('plaintext'); +const STATE_SWIG_VAR = Symbol('swig_var'); +const STATE_SWIG_COMMENT = Symbol('swig_comment'); +const STATE_SWIG_TAG = Symbol('swig_tag'); +const STATE_SWIG_FULL_TAG = Symbol('swig_full_tag'); + +const isNonWhiteSpaceChar = char => char !== '\r' + && char !== '\n' + && char !== '\t' + && char !== '\f' + && char !== '\v' + && char !== ' '; + +class PostRenderEscape { constructor() { - this.cache = []; + this.stored = []; } restoreAllSwigTags(str) { - const restored = str.replace(rSwigPlaceHolder, _restoreContent(this.cache)); - if (restored === str) return restored; - return this.restoreAllSwigTags(restored); // self-recursive for nexted escaping + const restored = str.replace(rSwigPlaceHolder, _restoreContent(this.stored)); + return restored; } restoreCodeBlocks(str) { - return str.replace(rCodeBlockPlaceHolder, _restoreContent(this.cache)); + return str.replace(rCodeBlockPlaceHolder, _restoreContent(this.stored)); } escapeCodeBlocks(str) { - return str.replace(rHexoPostRenderEscape, (_, content) => _escapeContent(this.cache, 'code', content)); + return str.replace(rHexoPostRenderEscape, (_, content) => _escapeContent(this.stored, 'code', content)); } + /** + * @param {string} str + * @returns string + */ escapeAllSwigTags(str) { - const escape = _str => _escapeContent(this.cache, 'swig', _str); - return str.replace(rSwigVarAndComment, escape) // Remove swig comment first to reduce string size being matched next - .replace(rSwigFullBlock, escape) // swig full block must escaped before swig block to avoid confliction - .replace(rSwigBlock, escape); + let state = STATE_PLAINTEXT; + let buffer = ''; + let output = ''; + + let swig_tag_name_begin = false; + let swig_tag_name = ''; + let swig_full_tag_start_buffer = ''; + + const { length } = str; + + for (let idx = 0; idx < length; idx++) { + const char = str[idx]; + const next_char = str[idx + 1]; + + if (state === STATE_PLAINTEXT) { // From plain text to swig + if (char === '{') { + if (next_char === '{') { + state = STATE_SWIG_VAR; + idx++; + } else if (next_char === '#') { + state = STATE_SWIG_COMMENT; + idx++; + } else if (next_char === '%') { + state = STATE_SWIG_TAG; + idx++; + swig_tag_name = ''; + swig_full_tag_start_buffer = ''; + swig_tag_name_begin = false; // Mark if it is the first non white space char in the swig tag + } + } else { + output += char; + } + } else if (state === STATE_SWIG_TAG) { + if (char === '%' && next_char === '}') { // From swig back to plain text + idx++; + if (str.includes(`end${swig_tag_name}`)) { + state = STATE_SWIG_FULL_TAG; + } else { + swig_tag_name = ''; + state = STATE_PLAINTEXT; + output += _escapeContent(this.stored, 'swig', `{%${buffer}%}`); + } + + buffer = ''; + } else { + buffer = buffer + char; + swig_full_tag_start_buffer = swig_full_tag_start_buffer + char; + + if (isNonWhiteSpaceChar(char)) { + if (!swig_tag_name_begin) { + swig_tag_name_begin = true; + } + + if (swig_tag_name_begin) { + swig_tag_name += char; + } + } else { + if (swig_tag_name_begin === true) { + swig_tag_name_begin = false; + } + } + } + } else if (state === STATE_SWIG_VAR) { + if (char === '}' && next_char === '}') { + idx++; + state = STATE_PLAINTEXT; + output += _escapeContent(this.stored, 'swig', `{{${buffer}}}`); + buffer = ''; + } else { + buffer = buffer + char; + } + } else if (state === STATE_SWIG_COMMENT) { // From swig back to plain text + if (char === '#' && next_char === '}') { + idx++; + state = STATE_PLAINTEXT; + buffer = ''; + } + } else if (state === STATE_SWIG_FULL_TAG) { + if (char === '{' && next_char === '%') { + let swig_full_tag_end_buffer = ''; + + let _idx = idx + 2; + for (; _idx < length; _idx++) { + const _char = str[_idx]; + const _next_char = str[_idx + 1]; + + if (_char === '%' && _next_char === '}') { + _idx++; + break; + } + + swig_full_tag_end_buffer = swig_full_tag_end_buffer + _char; + } + + if (swig_full_tag_end_buffer.includes(`end${swig_tag_name}`)) { + state = STATE_PLAINTEXT; + output += _escapeContent(this.stored, 'swig', `{%${swig_full_tag_start_buffer}%}${buffer}{%${swig_full_tag_end_buffer}%}`); + idx = _idx; + swig_full_tag_start_buffer = ''; + swig_full_tag_end_buffer = ''; + buffer = ''; + } else { + buffer += char; + } + } else { + buffer += char; + } + } + } + + return output; } } @@ -263,7 +383,7 @@ class Post { // front-matter overrides renderer's option if (typeof data.disableNunjucks === 'boolean') disableNunjucks = data.disableNunjucks; - const cacheObj = new PostRenderCache(); + const cacheObj = new PostRenderEscape(); return promise.then(content => { data.content = content; diff --git a/test/fixtures/post_render.js b/test/fixtures/post_render.js index 5e7d9b938f..bca5d3c60e 100644 --- a/test/fixtures/post_render.js +++ b/test/fixtures/post_render.js @@ -16,11 +16,11 @@ exports.content = [ '', '## Another title', '{% blockquote %}', - 'quote content', + 'quote content 1', '{% endblockquote %}', '', '{% quote Hello World %}', - 'quote content', + 'quote content 2', '{% endquote %}' ].join('\n'); @@ -30,9 +30,9 @@ exports.expected = [ '\n

    some content

    \n', '

    Another title

    ', '
    ', - '

    quote content

    \n', + '

    quote content 1

    \n', '
    \n\n', - '

    quote content

    \n', + '

    quote content 2

    ', '
    Hello World
    ' ].join(''); @@ -42,10 +42,10 @@ exports.expected_disable_nunjucks = [ '\n

    some content

    \n', '

    Another title

    ', '

    {% blockquote %}
    ', - 'quote content
    ', + 'quote content 1
    ', '{% endblockquote %}

    \n', '

    {% quote Hello World %}
    ', - 'quote content
    ', + 'quote content 2
    ', '{% endquote %}

    ' ].join(''); diff --git a/test/scripts/hexo/post.js b/test/scripts/hexo/post.js index cfeb17016d..6bba9a6aae 100644 --- a/test/scripts/hexo/post.js +++ b/test/scripts/hexo/post.js @@ -880,6 +880,28 @@ describe('Post', () => { } }); + it('render() - nested swig tag', async () => { + const content = [ + '{% blockquote %}', + 'test1', + '{% quote test2 %}', + 'test3', + '{% endquote %}', + 'test4', + '{% endblockquote %}' + ].join('\n'); + + const data = await post.render(null, { + content + }); + data.content.trim().should.eql([ + '

    test1

    ', + '

    test3

    ', + '
    test2
    ', + 'test4
    ' + ].join('\n')); + }); + // #2321 it('render() - allow backtick code block in "blockquote" tag plugin', async () => { const code = 'alert("Hello world")'; From e6fa0871a740819840ccecb432ba58259393c2e3 Mon Sep 17 00:00:00 2001 From: Kristof Zerbe Date: Tue, 21 Sep 2021 11:44:55 +0200 Subject: [PATCH 008/105] Resolve #1490 --- lib/plugins/processor/post.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/plugins/processor/post.js b/lib/plugins/processor/post.js index f5fba69ef5..92fb3e2037 100644 --- a/lib/plugins/processor/post.js +++ b/lib/plugins/processor/post.js @@ -241,7 +241,14 @@ module.exports = ctx => { if (!result || isHiddenFile(result.path)) return; + //checks only if there is a renderer for the file type or if is included in skip_render result.renderable = ctx.render.isRenderable(path) && !isMatch(path, ctx.config.skip_render); + + //if post_asset_folder is set, restrict renderable files to default file extension + if (result.renderable && ctx.config.post_asset_folder) { + result.renderable = (extname(ctx.config.new_post_name) === extname(path)); + } + return result; }), From a2ec6b251927c5c416b745936a88aac99c191a76 Mon Sep 17 00:00:00 2001 From: KentarouTakeda Date: Thu, 23 Sep 2021 06:08:42 +0900 Subject: [PATCH 009/105] feat(open_graph): different URLs for og:image and twitter:image (#4748) --- lib/plugins/helper/open_graph.js | 8 +++++++- test/scripts/helpers/open_graph.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/lib/plugins/helper/open_graph.js b/lib/plugins/helper/open_graph.js index 71fb3cb5a4..f3668382e2 100644 --- a/lib/plugins/helper/open_graph.js +++ b/lib/plugins/helper/open_graph.js @@ -158,7 +158,13 @@ function openGraphHelper(options = {}) { result += meta('twitter:card', twitterCard); - if (images.length) { + if (options.twitter_image) { + let twitter_image = options.twitter_image; + if (!parse(twitter_image).host) { + twitter_image = resolve(url || config.url, twitter_image); + } + result += meta('twitter:image', twitter_image, false); + } else if (images.length) { result += meta('twitter:image', images[0], false); } diff --git a/test/scripts/helpers/open_graph.js b/test/scripts/helpers/open_graph.js index c703117dbd..f8b78ffb61 100644 --- a/test/scripts/helpers/open_graph.js +++ b/test/scripts/helpers/open_graph.js @@ -317,6 +317,36 @@ describe('open_graph', () => { result.should.have.string(meta({property: 'og:image', content: urlFn.resolve(config.url, '/foo/bar/test.jpg')})); }); + it('twitter_image - default same as og:image', () => { + const result = openGraph.call({ + page: {}, + config: hexo.config, + is_post: isPost + }, {images: 'image.jpg'}); + + result.should.have.string(meta({name: 'twitter:image', content: hexo.config.url + '/image.jpg'})); + }); + + it('twitter_image - different URLs for og:image and twitter:image', () => { + const result = openGraph.call({ + page: {}, + config: hexo.config, + is_post: isPost + }, {twitter_image: 'twitter.jpg', images: 'image.jpg'}); + + result.should.have.string(meta({name: 'twitter:image', content: hexo.config.url + '/twitter.jpg'})); + }); + + it('images - twitter_image absolute url', () => { + const result = openGraph.call({ + page: {}, + config: hexo.config, + is_post: isPost + }, {twitter_image: 'https://hexo.io/twitter.jpg', images: 'image.jpg'}); + + result.should.have.string(meta({name: 'twitter:image', content: 'https://hexo.io/twitter.jpg'})); + }); + it('site_name - options', () => { const result = openGraph.call({ page: {}, From 6f702fc0eb149effaf1a2fa395d7f6ab2d51cf5e Mon Sep 17 00:00:00 2001 From: Mimi <1119186082@qq.com> Date: Thu, 23 Sep 2021 23:45:18 +0800 Subject: [PATCH 010/105] feat: load hexo plugin in the theme's package.json (#4771) --- lib/hexo/index.js | 8 +++--- lib/hexo/load_plugins.js | 30 ++++++++++++--------- test/scripts/hexo/load_plugins.js | 45 ++++++++++++++++++------------- 3 files changed, 47 insertions(+), 36 deletions(-) diff --git a/lib/hexo/index.js b/lib/hexo/index.js index 944afd714d..dc345a65d7 100644 --- a/lib/hexo/index.js +++ b/lib/hexo/index.js @@ -268,16 +268,14 @@ class Hexo extends EventEmitter { return this.database.model(name, schema); } - resolvePlugin(name) { - const baseDir = this.base_dir; - + resolvePlugin(name, basedir) { try { // Try to resolve the plugin with the resolve.sync. - return sync(name, { basedir: baseDir }); + return sync(name, { basedir }); } catch (err) { // There was an error (likely the plugin wasn't found), so return a possibly // non-existing path that a later part of the resolution process will check. - return join(baseDir, 'node_modules', name); + return join(basedir, 'node_modules', name); } } diff --git a/lib/hexo/load_plugins.js b/lib/hexo/load_plugins.js index 03f733b3a9..9b95c18ece 100644 --- a/lib/hexo/load_plugins.js +++ b/lib/hexo/load_plugins.js @@ -11,8 +11,8 @@ module.exports = ctx => { return loadModules(ctx).then(() => loadScripts(ctx)); }; -function loadModuleList(ctx) { - const packagePath = join(ctx.base_dir, 'package.json'); +function loadModuleList(ctx, basedir) { + const packagePath = join(basedir, 'package.json'); // Make sure package.json exists return exists(packagePath).then(exist => { @@ -24,7 +24,7 @@ function loadModuleList(ctx) { const deps = Object.keys(json.dependencies || {}); const devDeps = Object.keys(json.devDependencies || {}); - return deps.concat(devDeps); + return basedir === ctx.base_dir ? deps.concat(devDeps) : deps; }); }).filter(name => { // Ignore plugins whose name is not started with "hexo-" @@ -37,22 +37,26 @@ function loadModuleList(ctx) { if (name.startsWith('@types/')) return false; // Make sure the plugin exists - const path = ctx.resolvePlugin(name); + const path = ctx.resolvePlugin(name, basedir); return exists(path); + }).then(modules => { + return Object.fromEntries(modules.map(name => [name, ctx.resolvePlugin(name, basedir)])); }); } function loadModules(ctx) { - return loadModuleList(ctx).map(name => { - const path = ctx.resolvePlugin(name); - - // Load plugins - return ctx.loadPlugin(path).then(() => { - ctx.log.debug('Plugin loaded: %s', magenta(name)); - }).catch(err => { - ctx.log.error({err}, 'Plugin load failed: %s', magenta(name)); + return Promise.map([ctx.base_dir, ctx.theme_dir], basedir => loadModuleList(ctx, basedir)) + .then(([hexoModuleList, themeModuleList]) => { + return Object.entries(Object.assign(themeModuleList, hexoModuleList)); + }) + .map(([name, path]) => { + // Load plugins + return ctx.loadPlugin(path).then(() => { + ctx.log.debug('Plugin loaded: %s', magenta(name)); + }).catch(err => { + ctx.log.error({err}, 'Plugin load failed: %s', magenta(name)); + }); }); - }); } function loadScripts(ctx) { diff --git a/test/scripts/hexo/load_plugins.js b/test/scripts/hexo/load_plugins.js index c4bc4ef252..087d7365f0 100644 --- a/test/scripts/hexo/load_plugins.js +++ b/test/scripts/hexo/load_plugins.js @@ -29,34 +29,31 @@ describe('Load plugins', () => { delete hexo._script_test; } - function createPackageFile(...args) { + function createPackageFile(name, path) { const pkg = { name: 'hexo-site', version: '0.0.0', private: true, - dependencies: {} + dependencies: { + [name]: '*' + } }; - for (const arg of args) { - pkg.dependencies[arg] = '*'; - } - - return fs.writeFile(join(hexo.base_dir, 'package.json'), JSON.stringify(pkg, null, ' ')); + path = path || join(hexo.base_dir, 'package.json'); + return fs.writeFile(path, JSON.stringify(pkg, null, ' ')); } - function createPackageFileWithDevDeps(...args) { + function createPackageFileWithDevDeps(name) { const pkg = { name: 'hexo-site', version: '0.0.0', private: true, dependencies: {}, - devDependencies: {} + devDependencies: { + [name]: '*' + } }; - for (let i = 0, len = args.length; i < len; i++) { - pkg.devDependencies[args[i]] = '*'; - } - return fs.writeFile(join(hexo.base_dir, 'package.json'), JSON.stringify(pkg, null, ' ')); } @@ -67,6 +64,10 @@ describe('Load plugins', () => { after(() => fs.rmdir(hexo.base_dir)); + afterEach(async () => { + await createPackageFile('hexo-another-plugin'); + }); + it('load plugins', () => { const name = 'hexo-plugin-test'; const path = join(hexo.plugin_dir, name, 'index.js'); @@ -106,9 +107,19 @@ describe('Load plugins', () => { }); }); - it('ignore plugin whose name is "hexo-theme-[hexo.config.theme]"', async () => { - hexo.config.theme = 'test_theme'; + it('load plugins in the theme\'s package.json', async () => { + const name = 'hexo-plugin-test'; + const path = join(hexo.plugin_dir, name, 'index.js'); + return Promise.all([ + createPackageFile(name, join(hexo.theme_dir, 'package.json')), + fs.writeFile(path, script) + ]).then(() => loadPlugins(hexo)).then(() => { + validate(path); + return fs.unlink(path); + }); + }); + it('ignore plugin whose name is started with "hexo-theme-"', async () => { const script = 'hexo._script_test = true'; const name = 'hexo-theme-test_theme'; const path = join(hexo.plugin_dir, name, 'index.js'); @@ -124,9 +135,7 @@ describe('Load plugins', () => { return fs.unlink(path); }); - it('ignore plugin whose name endswith "hexo-theme-[hexo.config.theme]"', async () => { - hexo.config.theme = 'test_theme'; - + it('ignore scoped plugin whose name is started with "hexo-theme-"', async () => { const script = 'hexo._script_test = true'; const name = '@hexojs/hexo-theme-test_theme'; const path = join(hexo.plugin_dir, name, 'index.js'); From a3424220e206e07ed091f78b0be929d87b8e5786 Mon Sep 17 00:00:00 2001 From: Sukka Date: Fri, 24 Sep 2021 00:53:21 +0800 Subject: [PATCH 011/105] perf: overall improvements (#4783) - Make some functions pure and move them to the top level - Avoid some Promise overhead - Utilize Array method on non-performance-intensive tasks - Avoid using new Promise --- lib/hexo/index.js | 30 ++- lib/hexo/load_plugins.js | 12 +- lib/hexo/post.js | 46 ++-- lib/hexo/render.js | 18 +- lib/plugins/processor/asset.js | 188 +++++++-------- lib/plugins/processor/post.js | 402 ++++++++++++++++----------------- 6 files changed, 351 insertions(+), 345 deletions(-) diff --git a/lib/hexo/index.js b/lib/hexo/index.js index dc345a65d7..67c332ab28 100644 --- a/lib/hexo/index.js +++ b/lib/hexo/index.js @@ -253,15 +253,10 @@ class Hexo extends EventEmitter { args = {}; } - return new Promise((resolve, reject) => { - const c = this.extend.console.get(name); + const c = this.extend.console.get(name); - if (c) { - Reflect.apply(c, this, [args]).then(resolve, reject); - } else { - reject(new Error(`Console \`${name}\` has not been registered yet!`)); - } - }).asCallback(callback); + if (c) return Reflect.apply(c, this, [args]).asCallback(callback); + return Promise.reject(new Error(`Console \`${name}\` has not been registered yet!`)); } model(name, schema) { @@ -319,7 +314,8 @@ class Hexo extends EventEmitter { ]); }).then(() => { mergeCtxThemeConfig(this); - }).then(() => this._generate({cache: false})).asCallback(callback); + return this._generate({cache: false}); + }).asCallback(callback); } watch(callback) { @@ -344,7 +340,7 @@ class Hexo extends EventEmitter { ]); }).then(() => { mergeCtxThemeConfig(this); - }).then(() => { + this.source.on('processAfter', this._watchBox); this.theme.on('processAfter', () => { this._watchBox(); @@ -400,10 +396,8 @@ class Hexo extends EventEmitter { return Promise.map(Object.keys(generators), key => { const generator = generators[key]; - return Reflect.apply(generator, this, [siteLocals]).then(data => { - log.debug('Generator: %s', magenta(key)); - return data; - }); + log.debug('Generator: %s', magenta(key)); + return Reflect.apply(generator, this, [siteLocals]); }).reduce((result, data) => { return data ? result.concat(data) : result; }, []); @@ -432,10 +426,12 @@ class Hexo extends EventEmitter { .thenReturn(path); }).then(newRouteList => { // Remove old routes - const removed = routeList.filter(item => !newRouteList.includes(item)); + for (let i = 0, len = routeList.length; i < len; i++) { + const item = routeList[i]; - for (let i = 0, len = removed.length; i < len; i++) { - route.remove(removed[i]); + if (!newRouteList.includes(item)) { + route.remove(item); + } } }); } diff --git a/lib/hexo/load_plugins.js b/lib/hexo/load_plugins.js index 9b95c18ece..d48b8830cb 100644 --- a/lib/hexo/load_plugins.js +++ b/lib/hexo/load_plugins.js @@ -62,10 +62,6 @@ function loadModules(ctx) { function loadScripts(ctx) { const baseDirLength = ctx.base_dir.length; - function displayPath(path) { - return magenta(path.substring(baseDirLength)); - } - return Promise.filter([ ctx.theme_script_dir, ctx.script_dir @@ -75,9 +71,13 @@ function loadScripts(ctx) { const path = join(scriptDir, name); return ctx.loadPlugin(path).then(() => { - ctx.log.debug('Script loaded: %s', displayPath(path)); + ctx.log.debug('Script loaded: %s', displayPath(path, baseDirLength)); }).catch(err => { - ctx.log.error({err}, 'Script load failed: %s', displayPath(path)); + ctx.log.error({err}, 'Script load failed: %s', displayPath(path, baseDirLength)); }); })); } + +function displayPath(path, baseDirLength) { + return magenta(path.substring(baseDirLength)); +} diff --git a/lib/hexo/post.js b/lib/hexo/post.js index bc0214e826..77a7780312 100644 --- a/lib/hexo/post.js +++ b/lib/hexo/post.js @@ -16,15 +16,6 @@ const rHexoPostRenderEscape = /([\s\S]+?)<\/hexoPostRen const rSwigPlaceHolder = /(?:<|<)!--swig\uFFFC(\d+)--(?:>|>)/g; const rCodeBlockPlaceHolder = /(?:<|<)!--code\uFFFC(\d+)--(?:>|>)/g; -const _escapeContent = (cache, flag, str) => ``; - -const _restoreContent = cache => (_, index) => { - assert(cache[index]); - const value = cache[index]; - cache[index] = null; - return value; -}; - const STATE_PLAINTEXT = Symbol('plaintext'); const STATE_SWIG_VAR = Symbol('swig_var'); const STATE_SWIG_COMMENT = Symbol('swig_comment'); @@ -43,17 +34,30 @@ class PostRenderEscape { this.stored = []; } + static escapeContent(cache, flag, str) { + return ``; + } + + static restoreContent(cache) { + return (_, index) => { + assert(cache[index]); + const value = cache[index]; + cache[index] = null; + return value; + }; + } + restoreAllSwigTags(str) { - const restored = str.replace(rSwigPlaceHolder, _restoreContent(this.stored)); + const restored = str.replace(rSwigPlaceHolder, PostRenderEscape.restoreContent(this.stored)); return restored; } restoreCodeBlocks(str) { - return str.replace(rCodeBlockPlaceHolder, _restoreContent(this.stored)); + return str.replace(rCodeBlockPlaceHolder, PostRenderEscape.restoreContent(this.stored)); } escapeCodeBlocks(str) { - return str.replace(rHexoPostRenderEscape, (_, content) => _escapeContent(this.stored, 'code', content)); + return str.replace(rHexoPostRenderEscape, (_, content) => PostRenderEscape.escapeContent(this.stored, 'code', content)); } /** @@ -101,7 +105,7 @@ class PostRenderEscape { } else { swig_tag_name = ''; state = STATE_PLAINTEXT; - output += _escapeContent(this.stored, 'swig', `{%${buffer}%}`); + output += PostRenderEscape.escapeContent(this.stored, 'swig', `{%${buffer}%}`); } buffer = ''; @@ -127,7 +131,7 @@ class PostRenderEscape { if (char === '}' && next_char === '}') { idx++; state = STATE_PLAINTEXT; - output += _escapeContent(this.stored, 'swig', `{{${buffer}}}`); + output += PostRenderEscape.escapeContent(this.stored, 'swig', `{{${buffer}}}`); buffer = ''; } else { buffer = buffer + char; @@ -157,7 +161,7 @@ class PostRenderEscape { if (swig_full_tag_end_buffer.includes(`end${swig_tag_name}`)) { state = STATE_PLAINTEXT; - output += _escapeContent(this.stored, 'swig', `{%${swig_full_tag_start_buffer}%}${buffer}{%${swig_full_tag_end_buffer}%}`); + output += PostRenderEscape.escapeContent(this.stored, 'swig', `{%${swig_full_tag_start_buffer}%}${buffer}{%${swig_full_tag_end_buffer}%}`); idx = _idx; swig_full_tag_start_buffer = ''; swig_full_tag_end_buffer = ''; @@ -241,7 +245,8 @@ class Post { createAssetFolder(path, config.post_asset_folder) ]).then(() => { ctx.emit('new', result); - }).thenReturn(result); + return result; + }); }).asCallback(callback); } @@ -271,12 +276,11 @@ class Post { // Parse front-matter const obj = jsonMode ? JSON.parse(`{${frontMatter}}`) : load(frontMatter); - // Add data which are not in the front-matter - for (const key of Object.keys(data)) { - if (!preservedKeys.includes(key) && obj[key] == null) { + Object.keys(data) + .filter(key => !preservedKeys.includes(key) && obj[key] == null) + .forEach(key => { obj[key] = data[key]; - } - } + }); let content = ''; // Prepend the separator diff --git a/lib/hexo/render.js b/lib/hexo/render.js index 693cfe90dd..802d1b38f0 100644 --- a/lib/hexo/render.js +++ b/lib/hexo/render.js @@ -60,13 +60,19 @@ class Render { const ctx = this.context; let ext = ''; - return new Promise((resolve, reject) => { - if (!data) return reject(new TypeError('No input file or string!')); - if (data.text != null) return resolve(data.text); - if (!data.path) return reject(new TypeError('No input file or string!')); + let promise; - readFile(data.path).then(resolve, reject); - }).then(text => { + if (!data) return Promise.reject(new TypeError('No input file or string!')); + + if (data.text != null) { + promise = Promise.resolve(data.text); + } else if (!data.path) { + return Promise.reject(new TypeError('No input file or string!')); + } else { + promise = readFile(data.path); + } + + return promise.then(text => { data.text = text; ext = data.engine || getExtname(data.path); if (!ext || !this.isRenderable(ext)) return text; diff --git a/lib/plugins/processor/asset.js b/lib/plugins/processor/asset.js index b8e7ffc828..4131294a30 100644 --- a/lib/plugins/processor/asset.js +++ b/lib/plugins/processor/asset.js @@ -7,124 +7,124 @@ const { extname, relative } = require('path'); const { Pattern } = require('hexo-util'); module.exports = ctx => { - function processPage(file) { - const Page = ctx.model('Page'); - const { path } = file; - const doc = Page.findOne({source: path}); - const { config } = ctx; - const { timezone: timezoneCfg } = config; - // Deprecated: use_date_for_updated will be removed in future - const updated_option = config.use_date_for_updated === true ? 'date' : config.updated_option; + return { + pattern: new Pattern(path => { + if (isExcludedFile(path, ctx.config)) return; - if (file.type === 'skip' && doc) { - return; - } + return { + renderable: ctx.render.isRenderable(path) && !isMatch(path, ctx.config.skip_render) + }; + }), - if (file.type === 'delete') { - if (doc) { - return doc.remove(); + process: function assetProcessor(file) { + if (file.params.renderable) { + return processPage(ctx, file); } - return; + return processAsset(ctx, file); } + }; +}; - return Promise.all([ - file.stat(), - file.read() - ]).spread((stats, content) => { - const data = yfm(content); - const output = ctx.render.getOutput(path); - - data.source = path; - data.raw = content; +function processPage(ctx, file) { + const Page = ctx.model('Page'); + const { path } = file; + const doc = Page.findOne({source: path}); + const { config } = ctx; + const { timezone: timezoneCfg } = config; + // Deprecated: use_date_for_updated will be removed in future + const updated_option = config.use_date_for_updated === true ? 'date' : config.updated_option; + + if (file.type === 'skip' && doc) { + return; + } - data.date = toDate(data.date); + if (file.type === 'delete') { + if (doc) { + return doc.remove(); + } - if (data.date) { - if (timezoneCfg) data.date = timezone(data.date, timezoneCfg); - } else { - data.date = stats.ctime; - } + return; + } - data.updated = toDate(data.updated); + return Promise.all([ + file.stat(), + file.read() + ]).spread((stats, content) => { + const data = yfm(content); + const output = ctx.render.getOutput(path); - if (data.updated) { - if (timezoneCfg) data.updated = timezone(data.updated, timezoneCfg); - } else if (updated_option === 'date') { - data.updated = data.date; - } else if (updated_option === 'empty') { - data.updated = undefined; - } else { - data.updated = stats.mtime; - } + data.source = path; + data.raw = content; - if (data.permalink) { - data.path = data.permalink; - data.permalink = undefined; + data.date = toDate(data.date); - if (data.path.endsWith('/')) { - data.path += 'index'; - } + if (data.date) { + if (timezoneCfg) data.date = timezone(data.date, timezoneCfg); + } else { + data.date = stats.ctime; + } - if (!extname(data.path)) { - data.path += `.${output}`; - } - } else { - data.path = `${path.substring(0, path.length - extname(path).length)}.${output}`; - } + data.updated = toDate(data.updated); - if (!data.layout && output !== 'html' && output !== 'htm') { - data.layout = false; - } + if (data.updated) { + if (timezoneCfg) data.updated = timezone(data.updated, timezoneCfg); + } else if (updated_option === 'date') { + data.updated = data.date; + } else if (updated_option === 'empty') { + data.updated = undefined; + } else { + data.updated = stats.mtime; + } - // FIXME: Data may be inserted when reading files. Load it again to prevent - // race condition. We have to solve this in warehouse. - const doc = Page.findOne({source: path}); + if (data.permalink) { + data.path = data.permalink; + data.permalink = undefined; - if (doc) { - return doc.replace(data); + if (data.path.endsWith('/')) { + data.path += 'index'; } - return Page.insert(data); - }); - } - - function processAsset(file) { - const id = relative(ctx.base_dir, file.source).replace(/\\/g, '/'); - const Asset = ctx.model('Asset'); - const doc = Asset.findById(id); - - if (file.type === 'delete') { - if (doc) { - return doc.remove(); + if (!extname(data.path)) { + data.path += `.${output}`; } + } else { + data.path = `${path.substring(0, path.length - extname(path).length)}.${output}`; + } - return; + if (!data.layout && output !== 'html' && output !== 'htm') { + data.layout = false; } - return Asset.save({ - _id: id, - path: file.path, - modified: file.type !== 'skip', - renderable: file.params.renderable - }); - } + // FIXME: Data may be inserted when reading files. Load it again to prevent + // race condition. We have to solve this in warehouse. + const doc = Page.findOne({source: path}); - return { - pattern: new Pattern(path => { - if (isExcludedFile(path, ctx.config)) return; + if (doc) { + return doc.replace(data); + } - return { - renderable: ctx.render.isRenderable(path) && !isMatch(path, ctx.config.skip_render) - }; - }), + return Page.insert(data); + }); +} - process: function assetProcessor(file) { - if (file.params.renderable) { - return processPage(file); - } +function processAsset(ctx, file) { + const id = relative(ctx.base_dir, file.source).replace(/\\/g, '/'); + const Asset = ctx.model('Asset'); + const doc = Asset.findById(id); - return processAsset(file); + if (file.type === 'delete') { + if (doc) { + return doc.remove(); } - }; -}; + + return; + } + + return Asset.save({ + _id: id, + path: file.path, + modified: file.type !== 'skip', + renderable: file.params.renderable + }); +} diff --git a/lib/plugins/processor/post.js b/lib/plugins/processor/post.js index f5fba69ef5..9b4272926a 100644 --- a/lib/plugins/processor/post.js +++ b/lib/plugins/processor/post.js @@ -22,205 +22,6 @@ const preservedKeys = { }; module.exports = ctx => { - function processPost(file) { - const Post = ctx.model('Post'); - const { path } = file.params; - const doc = Post.findOne({source: file.path}); - const { config } = ctx; - const { timezone: timezoneCfg } = config; - // Deprecated: use_date_for_updated will be removed in future - const updated_option = config.use_date_for_updated === true ? 'date' : config.updated_option; - let categories, tags; - - if (file.type === 'skip' && doc) { - return; - } - - if (file.type === 'delete') { - if (doc) { - return doc.remove(); - } - - return; - } - - return Promise.all([ - file.stat(), - file.read() - ]).spread((stats, content) => { - const data = yfm(content); - const info = parseFilename(config.new_post_name, path); - const keys = Object.keys(info); - - data.source = file.path; - data.raw = content; - data.slug = info.title; - - if (file.params.published) { - if (!Object.prototype.hasOwnProperty.call(data, 'published')) data.published = true; - } else { - data.published = false; - } - - for (let i = 0, len = keys.length; i < len; i++) { - const key = keys[i]; - if (!preservedKeys[key]) data[key] = info[key]; - } - - if (data.date) { - data.date = toDate(data.date); - } else if (info && info.year && (info.month || info.i_month) && (info.day || info.i_day)) { - data.date = new Date( - info.year, - parseInt(info.month || info.i_month, 10) - 1, - parseInt(info.day || info.i_day, 10) - ); - } - - if (data.date) { - if (timezoneCfg) data.date = timezone(data.date, timezoneCfg); - } else { - data.date = stats.birthtime; - } - - data.updated = toDate(data.updated); - - if (data.updated) { - if (timezoneCfg) data.updated = timezone(data.updated, timezoneCfg); - } else if (updated_option === 'date') { - data.updated = data.date; - } else if (updated_option === 'empty') { - data.updated = undefined; - } else { - data.updated = stats.mtime; - } - - if (data.category && !data.categories) { - data.categories = data.category; - data.category = undefined; - } - - if (data.tag && !data.tags) { - data.tags = data.tag; - data.tag = undefined; - } - - categories = data.categories || []; - tags = data.tags || []; - - if (!Array.isArray(categories)) categories = [categories]; - if (!Array.isArray(tags)) tags = [tags]; - - if (data.photo && !data.photos) { - data.photos = data.photo; - data.photo = undefined; - } - - if (data.photos && !Array.isArray(data.photos)) { - data.photos = [data.photos]; - } - - if (data.link && !data.title) { - data.title = data.link.replace(/^https?:\/\/|\/$/g, ''); - } - - if (data.permalink) { - data.__permalink = data.permalink; - data.permalink = undefined; - } - - // FIXME: Data may be inserted when reading files. Load it again to prevent - // race condition. We have to solve this in warehouse. - const doc = Post.findOne({source: file.path}); - - if (doc) { - return doc.replace(data); - } - - return Post.insert(data); - }).then(doc => Promise.all([ - doc.setCategories(categories), - doc.setTags(tags), - scanAssetDir(doc) - ])); - } - - function shouldSkipAsset(post, asset) { - if (!ctx._showDrafts()) { - if (post.published === false && asset) { - // delete existing draft assets if draft posts are hidden - asset.remove(); - } - if (post.published === false) { - // skip draft assets if draft posts are hidden - return true; - } - } - - return asset !== undefined; // skip already existing assets - } - - function scanAssetDir(post) { - if (!ctx.config.post_asset_folder) return; - - const assetDir = post.asset_dir; - const baseDir = ctx.base_dir; - const baseDirLength = baseDir.length; - const PostAsset = ctx.model('PostAsset'); - - return stat(assetDir).then(stats => { - if (!stats.isDirectory()) return []; - - return listDir(assetDir); - }).catch(err => { - if (err && err.code === 'ENOENT') return []; - throw err; - }).filter(item => !isExcludedFile(item, ctx.config)).map(item => { - const id = join(assetDir, item).substring(baseDirLength).replace(/\\/g, '/'); - const asset = PostAsset.findById(id); - - if (shouldSkipAsset(post, asset)) return undefined; - - return PostAsset.save({ - _id: id, - post: post._id, - slug: item, - modified: true - }); - }); - } - - function processAsset(file) { - const PostAsset = ctx.model('PostAsset'); - const Post = ctx.model('Post'); - const id = file.source.substring(ctx.base_dir.length).replace(/\\/g, '/'); - const doc = PostAsset.findById(id); - - if (file.type === 'delete') { - if (doc) { - return doc.remove(); - } - - return; - } - - // TODO: Better post searching - const post = Post.toArray().find(post => file.source.startsWith(post.asset_dir)); - if (post != null && (post.published || ctx._showDrafts())) { - return PostAsset.save({ - _id: id, - slug: file.source.substring(post.asset_dir.length), - post: post._id, - modified: file.type !== 'skip', - renderable: file.params.renderable - }); - } - - if (doc) { - return doc.remove(); - } - } - return { pattern: new Pattern(path => { if (isTmpFile(path)) return; @@ -247,14 +48,137 @@ module.exports = ctx => { process: function postProcessor(file) { if (file.params.renderable) { - return processPost(file); + return processPost(ctx, file); } else if (ctx.config.post_asset_folder) { - return processAsset(file); + return processAsset(ctx, file); } } }; }; +function processPost(ctx, file) { + const Post = ctx.model('Post'); + const { path } = file.params; + const doc = Post.findOne({source: file.path}); + const { config } = ctx; + const { timezone: timezoneCfg } = config; + // Deprecated: use_date_for_updated will be removed in future + const updated_option = config.use_date_for_updated === true ? 'date' : config.updated_option; + let categories, tags; + + if (file.type === 'skip' && doc) { + return; + } + + if (file.type === 'delete') { + if (doc) { + return doc.remove(); + } + + return; + } + + return Promise.all([ + file.stat(), + file.read() + ]).spread((stats, content) => { + const data = yfm(content); + const info = parseFilename(config.new_post_name, path); + const keys = Object.keys(info); + + data.source = file.path; + data.raw = content; + data.slug = info.title; + + if (file.params.published) { + if (!Object.prototype.hasOwnProperty.call(data, 'published')) data.published = true; + } else { + data.published = false; + } + + for (let i = 0, len = keys.length; i < len; i++) { + const key = keys[i]; + if (!preservedKeys[key]) data[key] = info[key]; + } + + if (data.date) { + data.date = toDate(data.date); + } else if (info && info.year && (info.month || info.i_month) && (info.day || info.i_day)) { + data.date = new Date( + info.year, + parseInt(info.month || info.i_month, 10) - 1, + parseInt(info.day || info.i_day, 10) + ); + } + + if (data.date) { + if (timezoneCfg) data.date = timezone(data.date, timezoneCfg); + } else { + data.date = stats.birthtime; + } + + data.updated = toDate(data.updated); + + if (data.updated) { + if (timezoneCfg) data.updated = timezone(data.updated, timezoneCfg); + } else if (updated_option === 'date') { + data.updated = data.date; + } else if (updated_option === 'empty') { + data.updated = undefined; + } else { + data.updated = stats.mtime; + } + + if (data.category && !data.categories) { + data.categories = data.category; + data.category = undefined; + } + + if (data.tag && !data.tags) { + data.tags = data.tag; + data.tag = undefined; + } + + categories = data.categories || []; + tags = data.tags || []; + + if (!Array.isArray(categories)) categories = [categories]; + if (!Array.isArray(tags)) tags = [tags]; + + if (data.photo && !data.photos) { + data.photos = data.photo; + data.photo = undefined; + } + + if (data.photos && !Array.isArray(data.photos)) { + data.photos = [data.photos]; + } + + if (data.link && !data.title) { + data.title = data.link.replace(/^https?:\/\/|\/$/g, ''); + } + + if (data.permalink) { + data.__permalink = data.permalink; + data.permalink = undefined; + } + + // FIXME: Data may be inserted when reading files. Load it again to prevent + // race condition. We have to solve this in warehouse. + const doc = Post.findOne({source: file.path}); + + if (doc) { + return doc.replace(data); + } + + return Post.insert(data); + }).then(doc => Promise.all([ + doc.setCategories(categories), + doc.setTags(tags), + scanAssetDir(ctx, doc) + ])); +} + function parseFilename(config, path) { config = config.substring(0, config.length - extname(config).length); path = path.substring(0, path.length - extname(path).length); @@ -282,3 +206,79 @@ function parseFilename(config, path) { title: slugize(path) }; } + +function scanAssetDir(ctx, post) { + if (!ctx.config.post_asset_folder) return; + + const assetDir = post.asset_dir; + const baseDir = ctx.base_dir; + const baseDirLength = baseDir.length; + const PostAsset = ctx.model('PostAsset'); + + return stat(assetDir).then(stats => { + if (!stats.isDirectory()) return []; + + return listDir(assetDir); + }).catch(err => { + if (err && err.code === 'ENOENT') return []; + throw err; + }).filter(item => !isExcludedFile(item, ctx.config)).map(item => { + const id = join(assetDir, item).substring(baseDirLength).replace(/\\/g, '/'); + const asset = PostAsset.findById(id); + + if (shouldSkipAsset(ctx, post, asset)) return undefined; + + return PostAsset.save({ + _id: id, + post: post._id, + slug: item, + modified: true + }); + }); +} + +function shouldSkipAsset(ctx, post, asset) { + if (!ctx._showDrafts()) { + if (post.published === false && asset) { + // delete existing draft assets if draft posts are hidden + asset.remove(); + } + if (post.published === false) { + // skip draft assets if draft posts are hidden + return true; + } + } + + return asset !== undefined; // skip already existing assets +} + +function processAsset(ctx, file) { + const PostAsset = ctx.model('PostAsset'); + const Post = ctx.model('Post'); + const id = file.source.substring(ctx.base_dir.length).replace(/\\/g, '/'); + const doc = PostAsset.findById(id); + + if (file.type === 'delete') { + if (doc) { + return doc.remove(); + } + + return; + } + + // TODO: Better post searching + const post = Post.toArray().find(post => file.source.startsWith(post.asset_dir)); + if (post != null && (post.published || ctx._showDrafts())) { + return PostAsset.save({ + _id: id, + slug: file.source.substring(post.asset_dir.length), + post: post._id, + modified: file.type !== 'skip', + renderable: file.params.renderable + }); + } + + if (doc) { + return doc.remove(); + } +} From 9c9b2a5e35688d202356b5d5da5d178fbf3b6abe Mon Sep 17 00:00:00 2001 From: Sukka Date: Fri, 24 Sep 2021 20:31:31 +0800 Subject: [PATCH 012/105] fix(#4780): curly brackets (#4784) --- lib/hexo/post.js | 2 ++ test/scripts/hexo/post.js | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/lib/hexo/post.js b/lib/hexo/post.js index 77a7780312..a1c356b481 100644 --- a/lib/hexo/post.js +++ b/lib/hexo/post.js @@ -93,6 +93,8 @@ class PostRenderEscape { swig_tag_name = ''; swig_full_tag_start_buffer = ''; swig_tag_name_begin = false; // Mark if it is the first non white space char in the swig tag + } else { + output += char; } } else { output += char; diff --git a/test/scripts/hexo/post.js b/test/scripts/hexo/post.js index 6bba9a6aae..c9fdbc6060 100644 --- a/test/scripts/hexo/post.js +++ b/test/scripts/hexo/post.js @@ -902,6 +902,28 @@ describe('Post', () => { ].join('\n')); }); + it('render() - shouln\'t break curly brackets', async () => { + hexo.config.prismjs.enable = true; + hexo.config.highlight.enable = false; + + const content = [ + '\\begin{equation}', + 'E=h\\nu', + '\\end{equation}' + ].join('\n'); + + const data = await post.render(null, { + content, + engine: 'markdown' + }); + + data.content.should.include('\\begin{equation}'); + data.content.should.include('\\end{equation}'); + + hexo.config.prismjs.enable = false; + hexo.config.highlight.enable = true; + }); + // #2321 it('render() - allow backtick code block in "blockquote" tag plugin', async () => { const code = 'alert("Hello world")'; From b3bd7d4d6b6f7a01a2f302954c4441d15cbe380e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Sep 2021 15:39:21 +0800 Subject: [PATCH 013/105] chore: bump husky from 4.3.8 to 7.0.2 (#4763) --- .husky/pre-commit | 4 ++++ package.json | 10 +++------- 2 files changed, 7 insertions(+), 7 deletions(-) create mode 100755 .husky/pre-commit diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000000..36af219892 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx lint-staged diff --git a/package.json b/package.json index 2d1f32a234..5815b63e04 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "scripts": { "eslint": "eslint .", "test": "mocha test/index.js", - "test-cov": "nyc --reporter=lcovonly npm test -- --no-parallel" + "test-cov": "nyc --reporter=lcovonly npm test -- --no-parallel", + "prepare": "husky install" }, "directories": { "lib": "./lib", @@ -70,7 +71,7 @@ "eslint": "^7.0.0", "eslint-config-hexo": "^4.1.0", "hexo-renderer-marked": "^4.0.0", - "husky": "^4.2.5", + "husky": "^7.0.2", "lint-staged": "^11.0.0", "mocha": "^9.1.1", "nyc": "^15.0.0", @@ -78,10 +79,5 @@ }, "engines": { "node": ">=12.13.0" - }, - "husky": { - "hooks": { - "pre-commit": "lint-staged" - } } } From b56ba651d80807244a37677f71cfa5c2b7d7b12a Mon Sep 17 00:00:00 2001 From: Sukka Date: Fri, 1 Oct 2021 21:16:37 +0800 Subject: [PATCH 014/105] refactor/perf: use nanocolors (#4788) --- lib/box/index.js | 2 +- lib/extend/tag.js | 4 ++-- lib/hexo/index.js | 2 +- lib/hexo/load_config.js | 2 +- lib/hexo/load_plugins.js | 2 +- lib/hexo/load_theme_config.js | 2 +- lib/hexo/post.js | 2 +- lib/plugins/console/deploy.js | 2 +- lib/plugins/console/generate.js | 2 +- lib/plugins/console/list/category.js | 2 +- lib/plugins/console/list/page.js | 2 +- lib/plugins/console/list/post.js | 2 +- lib/plugins/console/list/tag.js | 2 +- lib/plugins/console/migrate.js | 2 +- lib/plugins/console/new.js | 2 +- lib/plugins/console/publish.js | 2 +- lib/plugins/console/render.js | 2 +- lib/plugins/generator/asset.js | 2 +- package.json | 2 +- test/benchmark.js | 2 +- 20 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/box/index.js b/lib/box/index.js index 0c24456944..f248dd58ee 100644 --- a/lib/box/index.js +++ b/lib/box/index.js @@ -5,7 +5,7 @@ const Promise = require('bluebird'); const File = require('./file'); const { Pattern, createSha1Hash } = require('hexo-util'); const { createReadStream, readdir, stat, watch } = require('hexo-fs'); -const { magenta } = require('chalk'); +const { magenta } = require('nanocolors'); const { EventEmitter } = require('events'); const { isMatch, makeRe } = require('micromatch'); diff --git a/lib/extend/tag.js b/lib/extend/tag.js index 6eed6e5aa7..71e8676916 100644 --- a/lib/extend/tag.js +++ b/lib/extend/tag.js @@ -1,7 +1,7 @@ 'use strict'; const { stripIndent } = require('hexo-util'); -const { cyan, magenta, red } = require('chalk'); +const { cyan, magenta, red, bold } = require('nanocolors'); const { Environment } = require('nunjucks'); const Promise = require('bluebird'); const rSwigRawFullBlock = /{% *raw *%}/; @@ -141,7 +141,7 @@ const getContext = (lines, errLine, location, type) => { .map(lnNum => { const line = ' ' + lnNum + ' | ' + lines[lnNum - 1]; if (lnNum === errLine) { - return cyan.bold(line); + return cyan(bold(line)); } return cyan(line); diff --git a/lib/hexo/index.js b/lib/hexo/index.js index 67c332ab28..1f0b4de18c 100644 --- a/lib/hexo/index.js +++ b/lib/hexo/index.js @@ -4,7 +4,7 @@ const Promise = require('bluebird'); const { sep, join, dirname } = require('path'); const tildify = require('tildify'); const Database = require('warehouse'); -const { magenta, underline } = require('chalk'); +const { magenta, underline } = require('nanocolors'); const { EventEmitter } = require('events'); const { readFile } = require('hexo-fs'); const Module = require('module'); diff --git a/lib/hexo/load_config.js b/lib/hexo/load_config.js index 258bdab0c7..41faae464d 100644 --- a/lib/hexo/load_config.js +++ b/lib/hexo/load_config.js @@ -5,7 +5,7 @@ const tildify = require('tildify'); const Theme = require('../theme'); const Source = require('./source'); const { exists, readdir } = require('hexo-fs'); -const { magenta } = require('chalk'); +const { magenta } = require('nanocolors'); const { deepMerge } = require('hexo-util'); const validateConfig = require('./validate_config'); const { external_link: externalLinkDefaultCfg } = require('./default_config'); diff --git a/lib/hexo/load_plugins.js b/lib/hexo/load_plugins.js index d48b8830cb..d2cc4d9cf9 100644 --- a/lib/hexo/load_plugins.js +++ b/lib/hexo/load_plugins.js @@ -3,7 +3,7 @@ const { join } = require('path'); const { exists, readFile, listDir } = require('hexo-fs'); const Promise = require('bluebird'); -const { magenta } = require('chalk'); +const { magenta } = require('nanocolors'); module.exports = ctx => { if (!ctx.env.init || ctx.env.safe) return; diff --git a/lib/hexo/load_theme_config.js b/lib/hexo/load_theme_config.js index 5079d78c56..697ddfdd31 100644 --- a/lib/hexo/load_theme_config.js +++ b/lib/hexo/load_theme_config.js @@ -3,7 +3,7 @@ const { join, parse } = require('path'); const tildify = require('tildify'); const { exists, readdir } = require('hexo-fs'); -const { magenta } = require('chalk'); +const { magenta } = require('nanocolors'); const { deepMerge } = require('hexo-util'); module.exports = ctx => { diff --git a/lib/hexo/post.js b/lib/hexo/post.js index a1c356b481..9db255d437 100644 --- a/lib/hexo/post.js +++ b/lib/hexo/post.js @@ -4,7 +4,7 @@ const assert = require('assert'); const moment = require('moment'); const Promise = require('bluebird'); const { join, extname, basename } = require('path'); -const { magenta } = require('chalk'); +const { magenta } = require('nanocolors'); const { load } = require('js-yaml'); const { slugize, escapeRegExp } = require('hexo-util'); const { copyDir, exists, listDir, mkdirs, readFile, rmdir, unlink, writeFile } = require('hexo-fs'); diff --git a/lib/plugins/console/deploy.js b/lib/plugins/console/deploy.js index f3c348ffe1..b48ebf06a1 100644 --- a/lib/plugins/console/deploy.js +++ b/lib/plugins/console/deploy.js @@ -1,7 +1,7 @@ 'use strict'; const { exists } = require('hexo-fs'); -const { underline, magenta } = require('chalk'); +const { underline, magenta } = require('nanocolors'); function deployConsole(args) { let config = this.config.deploy; diff --git a/lib/plugins/console/generate.js b/lib/plugins/console/generate.js index 8bf33fc13d..f32042e8ab 100644 --- a/lib/plugins/console/generate.js +++ b/lib/plugins/console/generate.js @@ -4,7 +4,7 @@ const { exists, writeFile, unlink, stat, mkdirs } = require('hexo-fs'); const { join } = require('path'); const Promise = require('bluebird'); const prettyHrtime = require('pretty-hrtime'); -const { cyan, magenta } = require('chalk'); +const { cyan, magenta } = require('nanocolors'); const tildify = require('tildify'); const { PassThrough } = require('stream'); const { createSha1Hash } = require('hexo-util'); diff --git a/lib/plugins/console/list/category.js b/lib/plugins/console/list/category.js index 06d9df4377..3691d6900f 100644 --- a/lib/plugins/console/list/category.js +++ b/lib/plugins/console/list/category.js @@ -1,6 +1,6 @@ 'use strict'; -const { underline } = require('chalk'); +const { underline } = require('nanocolors'); const table = require('text-table'); const { stringLength } = require('./common'); diff --git a/lib/plugins/console/list/page.js b/lib/plugins/console/list/page.js index 32656208da..f6bd970521 100644 --- a/lib/plugins/console/list/page.js +++ b/lib/plugins/console/list/page.js @@ -1,6 +1,6 @@ 'use strict'; -const { magenta, underline, gray } = require('chalk'); +const { magenta, underline, gray } = require('nanocolors'); const table = require('text-table'); const { stringLength } = require('./common'); diff --git a/lib/plugins/console/list/post.js b/lib/plugins/console/list/post.js index 8e72638e42..a5abc30a60 100644 --- a/lib/plugins/console/list/post.js +++ b/lib/plugins/console/list/post.js @@ -1,6 +1,6 @@ 'use strict'; -const { gray, magenta, underline } = require('chalk'); +const { gray, magenta, underline } = require('nanocolors'); const table = require('text-table'); const { stringLength } = require('./common'); diff --git a/lib/plugins/console/list/tag.js b/lib/plugins/console/list/tag.js index 4957e4341d..0c532085bf 100644 --- a/lib/plugins/console/list/tag.js +++ b/lib/plugins/console/list/tag.js @@ -1,6 +1,6 @@ 'use strict'; -const { magenta, underline } = require('chalk'); +const { magenta, underline } = require('nanocolors'); const table = require('text-table'); const { stringLength } = require('./common'); diff --git a/lib/plugins/console/migrate.js b/lib/plugins/console/migrate.js index 78d7a93b1f..a86c4dd42a 100644 --- a/lib/plugins/console/migrate.js +++ b/lib/plugins/console/migrate.js @@ -1,6 +1,6 @@ 'use strict'; -const { underline, magenta } = require('chalk'); +const { underline, magenta } = require('nanocolors'); function migrateConsole(args) { // Display help message if user didn't input any arguments diff --git a/lib/plugins/console/new.js b/lib/plugins/console/new.js index 75df6d240b..0e471ad918 100644 --- a/lib/plugins/console/new.js +++ b/lib/plugins/console/new.js @@ -1,7 +1,7 @@ 'use strict'; const tildify = require('tildify'); -const { magenta } = require('chalk'); +const { magenta } = require('nanocolors'); const reservedKeys = { _: true, diff --git a/lib/plugins/console/publish.js b/lib/plugins/console/publish.js index 46f19dd954..4f535bcf8a 100644 --- a/lib/plugins/console/publish.js +++ b/lib/plugins/console/publish.js @@ -1,7 +1,7 @@ 'use strict'; const tildify = require('tildify'); -const { magenta } = require('chalk'); +const { magenta } = require('nanocolors'); function publishConsole(args) { // Display help message if user didn't input any arguments diff --git a/lib/plugins/console/render.js b/lib/plugins/console/render.js index ff8f352eeb..44cfb666c2 100644 --- a/lib/plugins/console/render.js +++ b/lib/plugins/console/render.js @@ -4,7 +4,7 @@ const { resolve } = require('path'); const tildify = require('tildify'); const prettyHrtime = require('pretty-hrtime'); const fs = require('hexo-fs'); -const { cyan, magenta } = require('chalk'); +const { cyan, magenta } = require('nanocolors'); function renderConsole(args) { // Display help message if user didn't input any arguments diff --git a/lib/plugins/generator/asset.js b/lib/plugins/generator/asset.js index cf8b8a6ccc..7630b16689 100644 --- a/lib/plugins/generator/asset.js +++ b/lib/plugins/generator/asset.js @@ -3,7 +3,7 @@ const fs = require('hexo-fs'); const Promise = require('bluebird'); const { extname } = require('path'); -const { magenta } = require('chalk'); +const { magenta } = require('nanocolors'); const process = (name, ctx) => { return Promise.filter(ctx.model(name).toArray(), asset => fs.exists(asset.source).tap(exist => { diff --git a/package.json b/package.json index 5815b63e04..0f9f40734c 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,6 @@ "abbrev": "^1.1.1", "archy": "^1.0.0", "bluebird": "^3.5.2", - "chalk": "^4.0.0", "hexo-cli": "^4.0.0", "hexo-front-matter": "^2.0.0", "hexo-fs": "^3.1.0", @@ -53,6 +52,7 @@ "micromatch": "^4.0.2", "moment": "^2.22.2", "moment-timezone": "^0.5.21", + "nanocolors": "^0.2.12", "nunjucks": "^3.2.1", "pretty-hrtime": "^1.0.3", "resolve": "^1.8.1", diff --git a/test/benchmark.js b/test/benchmark.js index eef852a2c3..83a7ead992 100644 --- a/test/benchmark.js +++ b/test/benchmark.js @@ -6,7 +6,7 @@ const { spawn: spawnAsync } = require('hexo-util'); const { rmdir, exists } = require('hexo-fs'); const { join, resolve } = require('path'); const log = require('hexo-log')(); -const { red } = require('chalk'); +const { red } = require('nanocolors'); const hooks = [ { regex: /Hexo version/, tag: 'hexo-begin' }, { regex: /Start processing/, tag: 'processing' }, From 9edbc9971f565d9021f765794a93fa33dd6e1d21 Mon Sep 17 00:00:00 2001 From: Sukka Date: Fri, 1 Oct 2021 21:30:35 +0800 Subject: [PATCH 015/105] fix(#4780): empty tag name correction (#4786) --- lib/hexo/post.js | 2 +- test/scripts/hexo/post.js | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/hexo/post.js b/lib/hexo/post.js index 9db255d437..462f650ffc 100644 --- a/lib/hexo/post.js +++ b/lib/hexo/post.js @@ -102,7 +102,7 @@ class PostRenderEscape { } else if (state === STATE_SWIG_TAG) { if (char === '%' && next_char === '}') { // From swig back to plain text idx++; - if (str.includes(`end${swig_tag_name}`)) { + if (swig_tag_name !== '' && str.includes(`end${swig_tag_name}`)) { state = STATE_SWIG_FULL_TAG; } else { swig_tag_name = ''; diff --git a/test/scripts/hexo/post.js b/test/scripts/hexo/post.js index c9fdbc6060..ccedb78a82 100644 --- a/test/scripts/hexo/post.js +++ b/test/scripts/hexo/post.js @@ -1357,4 +1357,22 @@ describe('Post', () => { hexo.config.prismjs.enable = false; hexo.config.highlight.enable = true; }); + + it('render() - empty tag name', async () => { + hexo.config.prismjs.enable = true; + hexo.config.highlight.enable = false; + + const content = 'Disable rendering of Nunjucks tag `{{ }}` / `{% %}`'; + + const data = await post.render(null, { + content, + engine: 'markdown' + }); + + data.content.should.include(escapeSwigTag('{{ }}')); + data.content.should.include(escapeSwigTag('{% %}')); + + hexo.config.prismjs.enable = false; + hexo.config.highlight.enable = true; + }); }); From 02cbfe37e565f6c6a8fa254550b726bbff3705bf Mon Sep 17 00:00:00 2001 From: Sukka Date: Fri, 8 Oct 2021 12:27:00 +0800 Subject: [PATCH 016/105] fix(processor): remove race condition failsafe (#4791) * fix(processor): race condition * fix(processor): add race condition warning * test: avoid potential race condition --- lib/plugins/processor/asset.js | 8 +- lib/plugins/processor/post.js | 8 +- test/scripts/processors/asset.js | 12 +- test/scripts/processors/post.js | 213 +++++++++++++++++++------------ 4 files changed, 147 insertions(+), 94 deletions(-) diff --git a/lib/plugins/processor/asset.js b/lib/plugins/processor/asset.js index 4131294a30..9595ac6d2f 100644 --- a/lib/plugins/processor/asset.js +++ b/lib/plugins/processor/asset.js @@ -5,6 +5,7 @@ const Promise = require('bluebird'); const { parse: yfm } = require('hexo-front-matter'); const { extname, relative } = require('path'); const { Pattern } = require('hexo-util'); +const { magenta } = require('nanocolors'); module.exports = ctx => { return { @@ -96,11 +97,10 @@ function processPage(ctx, file) { data.layout = false; } - // FIXME: Data may be inserted when reading files. Load it again to prevent - // race condition. We have to solve this in warehouse. - const doc = Page.findOne({source: path}); - if (doc) { + if (file.type !== 'update') { + ctx.log.warn(`Trying to "create" ${magenta(file.path)}, but the file already exists!`); + } return doc.replace(data); } diff --git a/lib/plugins/processor/post.js b/lib/plugins/processor/post.js index 9b4272926a..3be1422497 100644 --- a/lib/plugins/processor/post.js +++ b/lib/plugins/processor/post.js @@ -6,6 +6,7 @@ const { parse: yfm } = require('hexo-front-matter'); const { extname, join } = require('path'); const { stat, listDir } = require('hexo-fs'); const { slugize, Pattern, Permalink } = require('hexo-util'); +const { magenta } = require('nanocolors'); const postDir = '_posts/'; const draftDir = '_drafts/'; @@ -163,11 +164,10 @@ function processPost(ctx, file) { data.permalink = undefined; } - // FIXME: Data may be inserted when reading files. Load it again to prevent - // race condition. We have to solve this in warehouse. - const doc = Post.findOne({source: file.path}); - if (doc) { + if (file.type !== 'update') { + ctx.log.warn(`Trying to "create" ${magenta(file.path)}, but the file already exists!`); + } return doc.replace(data); } diff --git a/test/scripts/processors/asset.js b/test/scripts/processors/asset.js index c77bf83f84..f47426213a 100644 --- a/test/scripts/processors/asset.js +++ b/test/scripts/processors/asset.js @@ -86,8 +86,10 @@ describe('asset', () => { asset.modified.should.be.true; asset.renderable.should.be.false; - asset.remove(); - unlink(file.source); + return Promise.all([ + asset.remove(), + unlink(file.source) + ]); }); it('asset - type: create (when source path is configed to parent directory)', async () => { @@ -136,8 +138,10 @@ describe('asset', () => { asset.modified.should.be.true; asset.renderable.should.be.false; - asset.remove(); - unlink(file.source); + return Promise.all([ + asset.remove(), + unlink(file.source) + ]); }); it('asset - type: skip', async () => { diff --git a/test/scripts/processors/post.js b/test/scripts/processors/post.js index 59d5b03dc2..63c2f377e5 100644 --- a/test/scripts/processors/post.js +++ b/test/scripts/processors/post.js @@ -300,7 +300,7 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.title.should.eql('Hello world'); post.date.format(dateFormat).should.eql('2006-01-02 15:04:05'); @@ -311,8 +311,10 @@ describe('post', () => { post.slug.should.eql('foo'); post.published.should.be.true; - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - type: update', async () => { @@ -329,17 +331,19 @@ describe('post', () => { }); - const doc = await Post.insert({source: file.path, slug: 'foo'}); + const doc = await Post.insert({ source: file.path, slug: 'foo' }); await writeFile(file.source, body); const id = doc._id; await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post._id.should.eql(id); post.title.should.eql('New world'); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - type: delete', async () => { @@ -375,13 +379,15 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.slug.should.eql('foo'); post.date.format('YYYY-MM-DD').should.eql('2006-01-02'); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - extra data in file name', async () => { @@ -401,12 +407,14 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.lang.should.eql('zh'); - post.remove(); - return unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - file name does not match to the config', async () => { @@ -426,12 +434,14 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.slug.should.eql('foo'); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - published', async () => { @@ -450,12 +460,14 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.published.should.be.false; - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - always set published: false for drafts', async () => { @@ -474,12 +486,14 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.published.should.be.false; - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - use the status of the source file if date not set', async () => { @@ -498,13 +512,15 @@ describe('post', () => { await writeFile(file.source, body); const stats = await file.stat(); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.date.toDate().setMilliseconds(0).should.eql(stats.birthtime.setMilliseconds(0)); post.updated.toDate().setMilliseconds(0).should.eql(stats.mtime.setMilliseconds(0)); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - use the date for updated if updated_option = date', async () => { @@ -526,13 +542,15 @@ describe('post', () => { await writeFile(file.source, body); const stats = await file.stat(); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.updated.toDate().setMilliseconds(0).should.eql(post.date.toDate().setMilliseconds(0)); post.updated.toDate().setMilliseconds(0).should.not.eql(stats.mtime.setMilliseconds(0)); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - use the status of the source file if updated_option = mtime', async () => { @@ -554,13 +572,15 @@ describe('post', () => { await writeFile(file.source, body); const stats = await file.stat(); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.updated.toDate().setMilliseconds(0).should.eql(stats.mtime.setMilliseconds(0)); post.updated.toDate().setMilliseconds(0).should.not.eql(post.date.toDate().setMilliseconds(0)); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - updated shouldn\'t exists if updated_option = empty', async () => { @@ -580,12 +600,14 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); should.not.exist(post.updated); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - use use_date_for_updated as a fallback', async () => { @@ -606,13 +628,15 @@ describe('post', () => { await writeFile(file.source, body); const stats = await file.stat(); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.date.toDate().setMilliseconds(0).should.eql(stats.birthtime.setMilliseconds(0)); post.updated.toDate().setMilliseconds(0).should.eql(stats.birthtime.setMilliseconds(0)); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - ignore updated_option when use_date_for_updated is set', async () => { @@ -635,13 +659,15 @@ describe('post', () => { await writeFile(file.source, body); const stats = await file.stat(); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.updated.toDate().setMilliseconds(0).should.eql(post.date.toDate().setMilliseconds(0)); post.updated.toDate().setMilliseconds(0).should.not.eql(stats.mtime.setMilliseconds(0)); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - photo is an alias for photos', async () => { @@ -662,7 +688,7 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.photos.should.eql([ 'https://hexo.io/foo.jpg', @@ -671,8 +697,10 @@ describe('post', () => { should.not.exist(post.photo); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - photos (not array)', async () => { @@ -691,14 +719,16 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.photos.should.eql([ 'https://hexo.io/foo.jpg' ]); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - link without title', async () => { @@ -716,13 +746,15 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.link.should.eql('https://hexo.io/'); post.title.should.eql('hexo.io'); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - link without title and link', async () => { @@ -737,12 +769,14 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.title.should.eql(''); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - category is an alias for categories', async () => { @@ -763,13 +797,15 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); should.not.exist(post.category); post.categories.map(item => item.name).should.eql(['foo', 'bar']); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - categories (not array)', async () => { @@ -788,12 +824,14 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.categories.map(item => item.name).should.eql(['foo']); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - categories (multiple hierarchies)', async () => { @@ -814,12 +852,14 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.categories.map(item => item.name).should.eql(['foo', 'bar', 'baz']); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - tag is an alias for tags', async () => { @@ -840,13 +880,15 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); should.not.exist(post.tag); post.tags.map(item => item.name).should.have.members(['foo', 'bar']); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - tags (not array)', async () => { @@ -865,12 +907,14 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.tags.map(item => item.name).should.eql(['foo']); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - post_asset_folder enabled', async () => { @@ -911,7 +955,7 @@ describe('post', () => { ...assetFiles.map(obj => writeFile(obj.path, obj.contents)) ]); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); const assets = assetFiles.map(obj => PostAsset.findById(obj.id)); [assets[0]].should.not.eql([undefined]); @@ -975,9 +1019,8 @@ describe('post', () => { hexo.config.render_drafts = false; - post.remove(); - await Promise.all([ + post.remove(), unlink(file.source), unlink(assetPath) ]); @@ -1001,7 +1044,7 @@ describe('post', () => { writeFile(assetPath, '') ]); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); should.not.exist(PostAsset.findById(assetId)); post.remove(); @@ -1028,13 +1071,15 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.date.format(dateFormat).should.eql('2014-04-24 00:00:00'); post.updated.format(dateFormat).should.eql('2015-05-05 00:00:00'); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - use file stats instead if date is invalid', async () => { @@ -1055,13 +1100,15 @@ describe('post', () => { await writeFile(file.source, body); const stats = await file.stat(); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.date.toDate().setMilliseconds(0).should.eql(stats.birthtime.setMilliseconds(0)); post.updated.toDate().setMilliseconds(0).should.eql(stats.mtime.setMilliseconds(0)); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); it('post - timezone', async () => { @@ -1083,7 +1130,7 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.date.utc().format(dateFormat).should.eql('2014-04-24 00:00:00'); post.updated.utc().format(dateFormat).should.eql('2015-05-05 00:00:00'); @@ -1110,7 +1157,7 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.date.utc().format(dateFormat).should.eql('2006-01-02 00:00:00'); @@ -1135,11 +1182,13 @@ describe('post', () => { await writeFile(file.source, body); await process(file); - const post = Post.findOne({source: file.path}); + const post = Post.findOne({ source: file.path }); post.__permalink.should.eql('foooo'); - post.remove(); - unlink(file.source); + return Promise.all([ + post.remove(), + unlink(file.source) + ]); }); }); From 098cf0a517924e32e111e703bcac85ae632f3e0e Mon Sep 17 00:00:00 2001 From: Sukka Date: Fri, 8 Oct 2021 12:27:16 +0800 Subject: [PATCH 017/105] perf(external_link): optimize regexp (#4790) --- lib/plugins/filter/after_post_render/external_link.js | 2 +- lib/plugins/filter/after_render/external_link.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/plugins/filter/after_post_render/external_link.js b/lib/plugins/filter/after_post_render/external_link.js index b5f9b6b520..cd7bec136c 100644 --- a/lib/plugins/filter/after_post_render/external_link.js +++ b/lib/plugins/filter/after_post_render/external_link.js @@ -2,7 +2,7 @@ const { isExternalLink } = require('hexo-util'); let EXTERNAL_LINK_POST_ENABLED = true; -const rATag = /]+\s+?)?href=["']((?:http(?:s)?:|\/\/)[^<>"']+)["'][^<>]*>/gi; +const rATag = /]+?\s+?)href=["']((?:https?:|\/\/)[^<>"']+)["'][^<>]*>/gi; const rTargetAttr = /target=/i; const rRelAttr = /rel=/i; const rRelStrAttr = /rel=["']([^<>"']*)["']/i; diff --git a/lib/plugins/filter/after_render/external_link.js b/lib/plugins/filter/after_render/external_link.js index 6d8c45b008..40ff7b6b65 100644 --- a/lib/plugins/filter/after_render/external_link.js +++ b/lib/plugins/filter/after_render/external_link.js @@ -3,7 +3,7 @@ const { isExternalLink } = require('hexo-util'); let EXTERNAL_LINK_SITE_ENABLED = true; -const rATag = /]+\s+?)?href=["']((?:http(?:s)?:|\/\/)[^<>"']+)["'][^<>]*>/gi; +const rATag = /]+?\s+?)href=["']((?:https?:|\/\/)[^<>"']+)["'][^<>]*>/gi; const rTargetAttr = /target=/i; const rRelAttr = /rel=/i; const rRelStrAttr = /rel=["']([^<>"']*)["']/i; From 0f534b2f71636cd900cd7c6db57972b2ee5c27e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 00:17:19 +0800 Subject: [PATCH 018/105] chore: bump hexo-log from 2.0.0 to 3.0.0 (#4794) Bumps [hexo-log](https://github.com/hexojs/hexo-log) from 2.0.0 to 3.0.0. - [Release notes](https://github.com/hexojs/hexo-log/releases) - [Commits](https://github.com/hexojs/hexo-log/compare/2.0.0...v3.0.0) --- updated-dependencies: - dependency-name: hexo-log dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0f9f40734c..4f8a012dbe 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "hexo-front-matter": "^2.0.0", "hexo-fs": "^3.1.0", "hexo-i18n": "^1.0.0", - "hexo-log": "^2.0.0", + "hexo-log": "^3.0.0", "hexo-util": "^2.4.0", "js-yaml": "^4.0.0", "micromatch": "^4.0.2", From 2ec4f6312b24e47411d287b65e60d947dc7224c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Oct 2021 04:09:18 +0800 Subject: [PATCH 019/105] chore: bump eslint from 7.32.0 to 8.0.0 (#4799) Bumps [eslint](https://github.com/eslint/eslint) from 7.32.0 to 8.0.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v7.32.0...v8.0.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4f8a012dbe..087fb7eb6a 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "chai": "^4.2.0", "cheerio": "0.22.0", "decache": "^4.5.1", - "eslint": "^7.0.0", + "eslint": "^8.0.0", "eslint-config-hexo": "^4.1.0", "hexo-renderer-marked": "^4.0.0", "husky": "^7.0.2", From ed0f239c967f35fa709581b7ec74fced5f1b5a1d Mon Sep 17 00:00:00 2001 From: Sukka Date: Thu, 14 Oct 2021 17:51:03 +0800 Subject: [PATCH 020/105] perf(tag/helper): memoize (#4789) --- lib/plugins/helper/css.js | 6 +++++- lib/plugins/helper/date.js | 3 ++- lib/plugins/helper/feed_tag.js | 24 ++++++++++++++---------- lib/plugins/helper/js.js | 6 +++++- lib/plugins/helper/list_tags.js | 20 +++++++++++++++++++- lib/plugins/helper/mail_to.js | 6 +++++- lib/plugins/helper/open_graph.js | 7 ++++--- lib/plugins/helper/search_form.js | 4 +++- lib/plugins/helper/tagcloud.js | 20 +++++++++++++++++++- lib/plugins/tag/index.js | 24 ++++++++++++++++++++++++ lib/plugins/tag/post_link.js | 5 ++--- lib/plugins/tag/post_path.js | 5 ++--- package.json | 1 + 13 files changed, 105 insertions(+), 26 deletions(-) diff --git a/lib/plugins/helper/css.js b/lib/plugins/helper/css.js index 94c436e10a..85ccdc9f62 100644 --- a/lib/plugins/helper/css.js +++ b/lib/plugins/helper/css.js @@ -1,6 +1,7 @@ 'use strict'; const { htmlTag, url_for } = require('hexo-util'); +const { default: moize } = require('moize'); const flatten = function(arr, result = []) { for (const i in arr) { @@ -35,4 +36,7 @@ function cssHelper(...args) { return result; } -module.exports = cssHelper; +module.exports = moize(cssHelper, { + maxSize: 10, + isDeepEqual: true +}); diff --git a/lib/plugins/helper/date.js b/lib/plugins/helper/date.js index f811e7f826..8235e5fff4 100644 --- a/lib/plugins/helper/date.js +++ b/lib/plugins/helper/date.js @@ -2,6 +2,7 @@ const moment = require('moment-timezone'); const { isMoment } = moment; +const { default: moize } = require('moize'); const isDate = value => typeof value === 'object' && value instanceof Date && !isNaN(value.getTime()); @@ -93,4 +94,4 @@ exports.full_date = fullDateHelper; exports.relative_date = relativeDateHelper; exports.time_tag = timeTagHelper; exports.moment = moment; -exports.toMomentLocale = toMomentLocale; +exports.toMomentLocale = moize.shallow(toMomentLocale); diff --git a/lib/plugins/helper/feed_tag.js b/lib/plugins/helper/feed_tag.js index b14d3803b3..a72af70f12 100644 --- a/lib/plugins/helper/feed_tag.js +++ b/lib/plugins/helper/feed_tag.js @@ -1,15 +1,15 @@ 'use strict'; const { url_for } = require('hexo-util'); +const { default: moize } = require('moize'); const feedFn = (str = '') => { if (str) return str.replace(/2$/, ''); return str; }; -function feedTagHelper(path, options = {}) { - const { config } = this; - const title = options.title || config.title; +function makeFeedTag(path, options = {}, configFeed, configTitle) { + const title = options.title || configTitle; if (path) { if (typeof path !== 'string') throw new TypeError('path must be a string!'); @@ -26,16 +26,15 @@ function feedTagHelper(path, options = {}) { return ``; } - if (config.feed) { - const { feed } = config; - if (feed.type && feed.path) { - if (typeof feed.type === 'string') { - return ``; + if (configFeed) { + if (configFeed.type && configFeed.path) { + if (typeof configFeed.type === 'string') { + return ``; } let result = ''; - for (const i in feed.type) { - result += ``; + for (const i in configFeed.type) { + result += ``; } return result; } @@ -44,4 +43,9 @@ function feedTagHelper(path, options = {}) { return ''; } +function feedTagHelper(path, options = {}) { + const { config } = this; + return moize.deep(makeFeedTag.bind(this))(path, options, config.feed, config.title); +} + module.exports = feedTagHelper; diff --git a/lib/plugins/helper/js.js b/lib/plugins/helper/js.js index cdc0c60c41..920ecd973f 100644 --- a/lib/plugins/helper/js.js +++ b/lib/plugins/helper/js.js @@ -1,6 +1,7 @@ 'use strict'; const { htmlTag, url_for } = require('hexo-util'); +const { default: moize } = require('moize'); /* flatten() to be replaced by Array.flat() after Node 10 has reached EOL */ @@ -37,4 +38,7 @@ function jsHelper(...args) { return result; } -module.exports = jsHelper; +module.exports = moize(jsHelper, { + maxSize: 10, + isDeepEqual: true +}); diff --git a/lib/plugins/helper/list_tags.js b/lib/plugins/helper/list_tags.js index 6d56850515..f9970f6c0b 100644 --- a/lib/plugins/helper/list_tags.js +++ b/lib/plugins/helper/list_tags.js @@ -1,6 +1,7 @@ 'use strict'; const { url_for, escapeHTML } = require('hexo-util'); +const { default: moize } = require('moize'); function listTagsHelper(tags, options) { if (!options && (!tags || !Object.prototype.hasOwnProperty.call(tags, 'length'))) { @@ -92,4 +93,21 @@ function listTagsHelper(tags, options) { return result; } -module.exports = listTagsHelper; +function listTagsHelperFactory(tags, options) { + const transformArgs = () => { + if (!options && (!tags || !Object.prototype.hasOwnProperty.call(tags, 'length'))) { + options = tags; + tags = this.site.tags; + } + + return [tags.toArray(), options]; + }; + + return moize(listTagsHelper.bind(this), { + maxSize: 5, + isDeepEqual: true, + transformArgs + }).call(this, tags, options); +} + +module.exports = listTagsHelperFactory; diff --git a/lib/plugins/helper/mail_to.js b/lib/plugins/helper/mail_to.js index 5443388106..8eb51a8944 100644 --- a/lib/plugins/helper/mail_to.js +++ b/lib/plugins/helper/mail_to.js @@ -2,6 +2,7 @@ const { htmlTag } = require('hexo-util'); const qs = require('querystring'); +const { default: moize } = require('moize'); function mailToHelper(path, text, options = {}) { if (Array.isArray(path)) path = path.join(','); @@ -33,4 +34,7 @@ function mailToHelper(path, text, options = {}) { return htmlTag('a', attrs, text); } -module.exports = mailToHelper; +module.exports = moize(mailToHelper, { + maxSize: 10, + isDeepEqual: true +}); diff --git a/lib/plugins/helper/open_graph.js b/lib/plugins/helper/open_graph.js index f3668382e2..9506bb00e3 100644 --- a/lib/plugins/helper/open_graph.js +++ b/lib/plugins/helper/open_graph.js @@ -2,7 +2,8 @@ const { parse, resolve } = require('url'); const { isMoment, isDate } = require('moment'); -const { encodeURL, prettyUrls, htmlTag, stripHTML, escapeHTML, Cache } = require('hexo-util'); +const { encodeURL, prettyUrls, htmlTag, stripHTML, escapeHTML } = require('hexo-util'); +const { default: moize } = require('moize'); const localeMap = { 'en': 'en_US', @@ -20,8 +21,8 @@ const localeMap = { 'tr': 'tr_TR', 'vi': 'vi_VN' }; -const localeCache = new Cache(); -const localeToTerritory = str => localeCache.apply(str, () => { + +const localeToTerritory = moize.shallow(str => { if (str.length === 2 && localeMap[str]) return localeMap[str]; if (str.length === 5) { diff --git a/lib/plugins/helper/search_form.js b/lib/plugins/helper/search_form.js index ea81c2c4c4..f90a99b6d7 100644 --- a/lib/plugins/helper/search_form.js +++ b/lib/plugins/helper/search_form.js @@ -1,5 +1,7 @@ 'use strict'; +const { default: moize } = require('moize'); + function searchFormHelper(options = {}) { const { config } = this; const className = options.class || 'search-form'; @@ -8,4 +10,4 @@ function searchFormHelper(options = {}) { return `
    ${button ? `` : ''}
    `; } -module.exports = searchFormHelper; +module.exports = moize.deep(searchFormHelper); diff --git a/lib/plugins/helper/tagcloud.js b/lib/plugins/helper/tagcloud.js index f8dff38d0a..158b84a57e 100644 --- a/lib/plugins/helper/tagcloud.js +++ b/lib/plugins/helper/tagcloud.js @@ -1,6 +1,7 @@ 'use strict'; const { Color, url_for } = require('hexo-util'); +const { default: moize } = require('moize'); function tagcloudHelper(tags, options) { if (!options && (!tags || !Object.prototype.hasOwnProperty.call(tags, 'length'))) { @@ -74,4 +75,21 @@ function tagcloudHelper(tags, options) { return result.join(separator); } -module.exports = tagcloudHelper; +function tagcloudHelperFactory(tags, options) { + const transformArgs = () => { + if (!options && (!tags || !Object.prototype.hasOwnProperty.call(tags, 'length'))) { + options = tags; + tags = this.site.tags; + } + + return [tags.toArray(), options]; + }; + + return moize(tagcloudHelper.bind(this), { + maxSize: 5, + isDeepEqual: true, + transformArgs + }).call(this, tags, options); +} + +module.exports = tagcloudHelperFactory; diff --git a/lib/plugins/tag/index.js b/lib/plugins/tag/index.js index 57e73aca15..842a2d6f50 100644 --- a/lib/plugins/tag/index.js +++ b/lib/plugins/tag/index.js @@ -1,5 +1,7 @@ 'use strict'; +const { default: moize } = require('moize'); + module.exports = ctx => { const { tag } = ctx.extend; @@ -52,3 +54,25 @@ module.exports = ctx => { tag.register('youtube', require('./youtube')); }; + +// Use WeakMap to track different ctx (in case there is any) +const moized = new WeakMap(); + +module.exports.postFindOneFactory = function postFindOneFactory(ctx) { + if (moized.has(ctx)) { + return moized.get(ctx); + } + + const moizedPostFindOne = moize(createPostFindOne(ctx), { + isDeepEqual: true, + maxSize: 20 + }); + moized.set(ctx, moizedPostFindOne); + + return moizedPostFindOne; +}; + +function createPostFindOne(ctx) { + const Post = ctx.model('Post'); + return Post.findOne.bind(Post); +} diff --git a/lib/plugins/tag/post_link.js b/lib/plugins/tag/post_link.js index a5df3fa23d..f27cec0c4e 100644 --- a/lib/plugins/tag/post_link.js +++ b/lib/plugins/tag/post_link.js @@ -2,6 +2,7 @@ const { encodeURL, escapeHTML } = require('hexo-util'); const { resolve } = require('url'); +const { postFindOneFactory } = require('./'); /** * Post link tag @@ -10,8 +11,6 @@ const { resolve } = require('url'); * {% post_link slug [title] [escape] %} */ module.exports = ctx => { - const Post = ctx.model('Post'); - return function postLinkTag(args) { const error = `Post not found: ${args.join(' ') || 'Invalid post_link'}`; const slug = args.shift(); @@ -24,7 +23,7 @@ module.exports = ctx => { escape = 'true'; } - const post = Post.findOne({slug}); + const post = postFindOneFactory(ctx)({ slug }); if (!post) return error; let title = args.length ? args.join(' ') : post.title; diff --git a/lib/plugins/tag/post_path.js b/lib/plugins/tag/post_path.js index e156c9deaa..0b81ed69e9 100644 --- a/lib/plugins/tag/post_path.js +++ b/lib/plugins/tag/post_path.js @@ -2,6 +2,7 @@ const { resolve } = require('url'); const { encodeURL } = require('hexo-util'); +const { postFindOneFactory } = require('./'); /** * Post path tag @@ -10,13 +11,11 @@ const { encodeURL } = require('hexo-util'); * {% post_path slug %} */ module.exports = ctx => { - const Post = ctx.model('Post'); - return function postPathTag(args) { const slug = args.shift(); if (!slug) return; - const post = Post.findOne({slug}); + const post = postFindOneFactory(ctx)({ slug }); if (!post) return; const link = encodeURL(resolve(ctx.config.root, post.path)); diff --git a/package.json b/package.json index 087fb7eb6a..a1c1dd1e3d 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "hexo-util": "^2.4.0", "js-yaml": "^4.0.0", "micromatch": "^4.0.2", + "moize": "^6.1.0", "moment": "^2.22.2", "moment-timezone": "^0.5.21", "nanocolors": "^0.2.12", From 0c6380caa768f892e02372c553266871a296d774 Mon Sep 17 00:00:00 2001 From: Ming Di Leom <43627182+curbengh@users.noreply.github.com> Date: Mon, 25 Oct 2021 17:24:26 +1030 Subject: [PATCH 021/105] refactor: native Array.flat() (#4806) - Node 12 feature - remove misleading "old syntax" * "old syntax" implies deprecation, but in this case, it's just different syntax --- lib/plugins/helper/css.js | 17 ++--------------- lib/plugins/helper/js.js | 19 ++----------------- 2 files changed, 4 insertions(+), 32 deletions(-) diff --git a/lib/plugins/helper/css.js b/lib/plugins/helper/css.js index 85ccdc9f62..0e728bd1b6 100644 --- a/lib/plugins/helper/css.js +++ b/lib/plugins/helper/css.js @@ -3,23 +3,10 @@ const { htmlTag, url_for } = require('hexo-util'); const { default: moize } = require('moize'); -const flatten = function(arr, result = []) { - for (const i in arr) { - const value = arr[i]; - if (Array.isArray(value)) { - flatten(value, result); - } else { - result.push(value); - } - } - return result; -}; - function cssHelper(...args) { let result = '\n'; - flatten(args).forEach(item => { - // Old syntax + args.flat(Infinity).forEach(item => { if (typeof item === 'string' || item instanceof String) { let path = item; if (!path.endsWith('.css')) { @@ -27,7 +14,7 @@ function cssHelper(...args) { } result += `\n`; } else { - // New syntax + // Custom attributes item.href = url_for.call(this, item.href); if (!item.href.endsWith('.css')) item.href += '.css'; result += htmlTag('link', { rel: 'stylesheet', ...item }) + '\n'; diff --git a/lib/plugins/helper/js.js b/lib/plugins/helper/js.js index 920ecd973f..1ab2ebcff4 100644 --- a/lib/plugins/helper/js.js +++ b/lib/plugins/helper/js.js @@ -3,25 +3,10 @@ const { htmlTag, url_for } = require('hexo-util'); const { default: moize } = require('moize'); -/* flatten() to be replaced by Array.flat() -after Node 10 has reached EOL */ -const flatten = function(arr, result = []) { - for (const i in arr) { - const value = arr[i]; - if (Array.isArray(value)) { - flatten(value, result); - } else { - result.push(value); - } - } - return result; -}; - function jsHelper(...args) { let result = '\n'; - flatten(args).forEach(item => { - // Old syntax + args.flat(Infinity).forEach(item => { if (typeof item === 'string' || item instanceof String) { let path = item; if (!path.endsWith('.js')) { @@ -29,7 +14,7 @@ function jsHelper(...args) { } result += `\n`; } else { - // New syntax + // Custom attributes item.src = url_for.call(this, item.src); if (!item.src.endsWith('.js')) item.src += '.js'; result += htmlTag('script', { ...item }, '') + '\n'; From 902cd70b308b0e3068efeeff39a430139d2354f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Nov 2021 00:50:40 +0800 Subject: [PATCH 022/105] chore: bump sinon from 11.1.2 to 12.0.1 (#4810) Bumps [sinon](https://github.com/sinonjs/sinon) from 11.1.2 to 12.0.1. - [Release notes](https://github.com/sinonjs/sinon/releases) - [Changelog](https://github.com/sinonjs/sinon/blob/master/docs/changelog.md) - [Commits](https://github.com/sinonjs/sinon/compare/v11.1.2...v12.0.1) --- updated-dependencies: - dependency-name: sinon dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a1c1dd1e3d..71bc3448dc 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "lint-staged": "^11.0.0", "mocha": "^9.1.1", "nyc": "^15.0.0", - "sinon": "^11.1.2" + "sinon": "^12.0.1" }, "engines": { "node": ">=12.13.0" From 3ed6fd9c55ab3165f2aa2d442c0f502bc94bd309 Mon Sep 17 00:00:00 2001 From: Mimi <1119186082@qq.com> Date: Tue, 23 Nov 2021 19:59:35 +0800 Subject: [PATCH 023/105] fix(post): escape swig full tag with args (#4824) --- lib/hexo/post.js | 5 ++++- test/fixtures/post_render.js | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/hexo/post.js b/lib/hexo/post.js index 462f650ffc..2251a1592a 100644 --- a/lib/hexo/post.js +++ b/lib/hexo/post.js @@ -70,6 +70,7 @@ class PostRenderEscape { let output = ''; let swig_tag_name_begin = false; + let swig_tag_name_end = false; let swig_tag_name = ''; let swig_full_tag_start_buffer = ''; @@ -93,6 +94,7 @@ class PostRenderEscape { swig_tag_name = ''; swig_full_tag_start_buffer = ''; swig_tag_name_begin = false; // Mark if it is the first non white space char in the swig tag + swig_tag_name_end = false; } else { output += char; } @@ -116,7 +118,7 @@ class PostRenderEscape { swig_full_tag_start_buffer = swig_full_tag_start_buffer + char; if (isNonWhiteSpaceChar(char)) { - if (!swig_tag_name_begin) { + if (!swig_tag_name_begin && !swig_tag_name_end) { swig_tag_name_begin = true; } @@ -126,6 +128,7 @@ class PostRenderEscape { } else { if (swig_tag_name_begin === true) { swig_tag_name_begin = false; + swig_tag_name_end = true; } } } diff --git a/test/fixtures/post_render.js b/test/fixtures/post_render.js index bca5d3c60e..d728191b60 100644 --- a/test/fixtures/post_render.js +++ b/test/fixtures/post_render.js @@ -32,7 +32,7 @@ exports.expected = [ '
    ', '

    quote content 1

    \n', '
    \n\n', - '

    quote content 2

    ', + '

    quote content 2

    \n', '
    Hello World
    ' ].join(''); From 11b145c6c4aacbb69b4c10d49245090dd1db6375 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 24 Nov 2021 14:53:58 +0100 Subject: [PATCH 024/105] Switch to picocolors (#4825) nanocolors is deprecated https://github.com/ai/nanocolors --- lib/box/index.js | 2 +- lib/extend/tag.js | 2 +- lib/hexo/index.js | 2 +- lib/hexo/load_config.js | 2 +- lib/hexo/load_plugins.js | 2 +- lib/hexo/load_theme_config.js | 2 +- lib/hexo/post.js | 2 +- lib/plugins/console/deploy.js | 2 +- lib/plugins/console/generate.js | 2 +- lib/plugins/console/list/category.js | 2 +- lib/plugins/console/list/page.js | 2 +- lib/plugins/console/list/post.js | 2 +- lib/plugins/console/list/tag.js | 2 +- lib/plugins/console/migrate.js | 2 +- lib/plugins/console/new.js | 2 +- lib/plugins/console/publish.js | 2 +- lib/plugins/console/render.js | 2 +- lib/plugins/generator/asset.js | 2 +- lib/plugins/processor/asset.js | 2 +- lib/plugins/processor/post.js | 2 +- package.json | 2 +- test/benchmark.js | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/box/index.js b/lib/box/index.js index f248dd58ee..ebea4c3864 100644 --- a/lib/box/index.js +++ b/lib/box/index.js @@ -5,7 +5,7 @@ const Promise = require('bluebird'); const File = require('./file'); const { Pattern, createSha1Hash } = require('hexo-util'); const { createReadStream, readdir, stat, watch } = require('hexo-fs'); -const { magenta } = require('nanocolors'); +const { magenta } = require('picocolors'); const { EventEmitter } = require('events'); const { isMatch, makeRe } = require('micromatch'); diff --git a/lib/extend/tag.js b/lib/extend/tag.js index 71e8676916..4334240278 100644 --- a/lib/extend/tag.js +++ b/lib/extend/tag.js @@ -1,7 +1,7 @@ 'use strict'; const { stripIndent } = require('hexo-util'); -const { cyan, magenta, red, bold } = require('nanocolors'); +const { cyan, magenta, red, bold } = require('picocolors'); const { Environment } = require('nunjucks'); const Promise = require('bluebird'); const rSwigRawFullBlock = /{% *raw *%}/; diff --git a/lib/hexo/index.js b/lib/hexo/index.js index 1f0b4de18c..578cefe914 100644 --- a/lib/hexo/index.js +++ b/lib/hexo/index.js @@ -4,7 +4,7 @@ const Promise = require('bluebird'); const { sep, join, dirname } = require('path'); const tildify = require('tildify'); const Database = require('warehouse'); -const { magenta, underline } = require('nanocolors'); +const { magenta, underline } = require('picocolors'); const { EventEmitter } = require('events'); const { readFile } = require('hexo-fs'); const Module = require('module'); diff --git a/lib/hexo/load_config.js b/lib/hexo/load_config.js index 41faae464d..c8c6f49425 100644 --- a/lib/hexo/load_config.js +++ b/lib/hexo/load_config.js @@ -5,7 +5,7 @@ const tildify = require('tildify'); const Theme = require('../theme'); const Source = require('./source'); const { exists, readdir } = require('hexo-fs'); -const { magenta } = require('nanocolors'); +const { magenta } = require('picocolors'); const { deepMerge } = require('hexo-util'); const validateConfig = require('./validate_config'); const { external_link: externalLinkDefaultCfg } = require('./default_config'); diff --git a/lib/hexo/load_plugins.js b/lib/hexo/load_plugins.js index d2cc4d9cf9..010127c74e 100644 --- a/lib/hexo/load_plugins.js +++ b/lib/hexo/load_plugins.js @@ -3,7 +3,7 @@ const { join } = require('path'); const { exists, readFile, listDir } = require('hexo-fs'); const Promise = require('bluebird'); -const { magenta } = require('nanocolors'); +const { magenta } = require('picocolors'); module.exports = ctx => { if (!ctx.env.init || ctx.env.safe) return; diff --git a/lib/hexo/load_theme_config.js b/lib/hexo/load_theme_config.js index 697ddfdd31..1abb35d842 100644 --- a/lib/hexo/load_theme_config.js +++ b/lib/hexo/load_theme_config.js @@ -3,7 +3,7 @@ const { join, parse } = require('path'); const tildify = require('tildify'); const { exists, readdir } = require('hexo-fs'); -const { magenta } = require('nanocolors'); +const { magenta } = require('picocolors'); const { deepMerge } = require('hexo-util'); module.exports = ctx => { diff --git a/lib/hexo/post.js b/lib/hexo/post.js index 2251a1592a..7f0db61bbe 100644 --- a/lib/hexo/post.js +++ b/lib/hexo/post.js @@ -4,7 +4,7 @@ const assert = require('assert'); const moment = require('moment'); const Promise = require('bluebird'); const { join, extname, basename } = require('path'); -const { magenta } = require('nanocolors'); +const { magenta } = require('picocolors'); const { load } = require('js-yaml'); const { slugize, escapeRegExp } = require('hexo-util'); const { copyDir, exists, listDir, mkdirs, readFile, rmdir, unlink, writeFile } = require('hexo-fs'); diff --git a/lib/plugins/console/deploy.js b/lib/plugins/console/deploy.js index b48ebf06a1..bd35db8a23 100644 --- a/lib/plugins/console/deploy.js +++ b/lib/plugins/console/deploy.js @@ -1,7 +1,7 @@ 'use strict'; const { exists } = require('hexo-fs'); -const { underline, magenta } = require('nanocolors'); +const { underline, magenta } = require('picocolors'); function deployConsole(args) { let config = this.config.deploy; diff --git a/lib/plugins/console/generate.js b/lib/plugins/console/generate.js index f32042e8ab..a95abb0e15 100644 --- a/lib/plugins/console/generate.js +++ b/lib/plugins/console/generate.js @@ -4,7 +4,7 @@ const { exists, writeFile, unlink, stat, mkdirs } = require('hexo-fs'); const { join } = require('path'); const Promise = require('bluebird'); const prettyHrtime = require('pretty-hrtime'); -const { cyan, magenta } = require('nanocolors'); +const { cyan, magenta } = require('picocolors'); const tildify = require('tildify'); const { PassThrough } = require('stream'); const { createSha1Hash } = require('hexo-util'); diff --git a/lib/plugins/console/list/category.js b/lib/plugins/console/list/category.js index 3691d6900f..8550ec6c1d 100644 --- a/lib/plugins/console/list/category.js +++ b/lib/plugins/console/list/category.js @@ -1,6 +1,6 @@ 'use strict'; -const { underline } = require('nanocolors'); +const { underline } = require('picocolors'); const table = require('text-table'); const { stringLength } = require('./common'); diff --git a/lib/plugins/console/list/page.js b/lib/plugins/console/list/page.js index f6bd970521..7ea880ed54 100644 --- a/lib/plugins/console/list/page.js +++ b/lib/plugins/console/list/page.js @@ -1,6 +1,6 @@ 'use strict'; -const { magenta, underline, gray } = require('nanocolors'); +const { magenta, underline, gray } = require('picocolors'); const table = require('text-table'); const { stringLength } = require('./common'); diff --git a/lib/plugins/console/list/post.js b/lib/plugins/console/list/post.js index a5abc30a60..e09a975b5e 100644 --- a/lib/plugins/console/list/post.js +++ b/lib/plugins/console/list/post.js @@ -1,6 +1,6 @@ 'use strict'; -const { gray, magenta, underline } = require('nanocolors'); +const { gray, magenta, underline } = require('picocolors'); const table = require('text-table'); const { stringLength } = require('./common'); diff --git a/lib/plugins/console/list/tag.js b/lib/plugins/console/list/tag.js index 0c532085bf..6f5dab7c69 100644 --- a/lib/plugins/console/list/tag.js +++ b/lib/plugins/console/list/tag.js @@ -1,6 +1,6 @@ 'use strict'; -const { magenta, underline } = require('nanocolors'); +const { magenta, underline } = require('picocolors'); const table = require('text-table'); const { stringLength } = require('./common'); diff --git a/lib/plugins/console/migrate.js b/lib/plugins/console/migrate.js index a86c4dd42a..707b14ba55 100644 --- a/lib/plugins/console/migrate.js +++ b/lib/plugins/console/migrate.js @@ -1,6 +1,6 @@ 'use strict'; -const { underline, magenta } = require('nanocolors'); +const { underline, magenta } = require('picocolors'); function migrateConsole(args) { // Display help message if user didn't input any arguments diff --git a/lib/plugins/console/new.js b/lib/plugins/console/new.js index 0e471ad918..3b5942c98b 100644 --- a/lib/plugins/console/new.js +++ b/lib/plugins/console/new.js @@ -1,7 +1,7 @@ 'use strict'; const tildify = require('tildify'); -const { magenta } = require('nanocolors'); +const { magenta } = require('picocolors'); const reservedKeys = { _: true, diff --git a/lib/plugins/console/publish.js b/lib/plugins/console/publish.js index 4f535bcf8a..2192e196a1 100644 --- a/lib/plugins/console/publish.js +++ b/lib/plugins/console/publish.js @@ -1,7 +1,7 @@ 'use strict'; const tildify = require('tildify'); -const { magenta } = require('nanocolors'); +const { magenta } = require('picocolors'); function publishConsole(args) { // Display help message if user didn't input any arguments diff --git a/lib/plugins/console/render.js b/lib/plugins/console/render.js index 44cfb666c2..57028ba0d1 100644 --- a/lib/plugins/console/render.js +++ b/lib/plugins/console/render.js @@ -4,7 +4,7 @@ const { resolve } = require('path'); const tildify = require('tildify'); const prettyHrtime = require('pretty-hrtime'); const fs = require('hexo-fs'); -const { cyan, magenta } = require('nanocolors'); +const { cyan, magenta } = require('picocolors'); function renderConsole(args) { // Display help message if user didn't input any arguments diff --git a/lib/plugins/generator/asset.js b/lib/plugins/generator/asset.js index 7630b16689..3fda4db77d 100644 --- a/lib/plugins/generator/asset.js +++ b/lib/plugins/generator/asset.js @@ -3,7 +3,7 @@ const fs = require('hexo-fs'); const Promise = require('bluebird'); const { extname } = require('path'); -const { magenta } = require('nanocolors'); +const { magenta } = require('picocolors'); const process = (name, ctx) => { return Promise.filter(ctx.model(name).toArray(), asset => fs.exists(asset.source).tap(exist => { diff --git a/lib/plugins/processor/asset.js b/lib/plugins/processor/asset.js index 9595ac6d2f..0dbe442446 100644 --- a/lib/plugins/processor/asset.js +++ b/lib/plugins/processor/asset.js @@ -5,7 +5,7 @@ const Promise = require('bluebird'); const { parse: yfm } = require('hexo-front-matter'); const { extname, relative } = require('path'); const { Pattern } = require('hexo-util'); -const { magenta } = require('nanocolors'); +const { magenta } = require('picocolors'); module.exports = ctx => { return { diff --git a/lib/plugins/processor/post.js b/lib/plugins/processor/post.js index 3be1422497..c931e61de5 100644 --- a/lib/plugins/processor/post.js +++ b/lib/plugins/processor/post.js @@ -6,7 +6,7 @@ const { parse: yfm } = require('hexo-front-matter'); const { extname, join } = require('path'); const { stat, listDir } = require('hexo-fs'); const { slugize, Pattern, Permalink } = require('hexo-util'); -const { magenta } = require('nanocolors'); +const { magenta } = require('picocolors'); const postDir = '_posts/'; const draftDir = '_drafts/'; diff --git a/package.json b/package.json index 71bc3448dc..6d2056eb0d 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "moize": "^6.1.0", "moment": "^2.22.2", "moment-timezone": "^0.5.21", - "nanocolors": "^0.2.12", + "picocolors": "^1.0.0", "nunjucks": "^3.2.1", "pretty-hrtime": "^1.0.3", "resolve": "^1.8.1", diff --git a/test/benchmark.js b/test/benchmark.js index 83a7ead992..b74c904549 100644 --- a/test/benchmark.js +++ b/test/benchmark.js @@ -6,7 +6,7 @@ const { spawn: spawnAsync } = require('hexo-util'); const { rmdir, exists } = require('hexo-fs'); const { join, resolve } = require('path'); const log = require('hexo-log')(); -const { red } = require('nanocolors'); +const { red } = require('picocolors'); const hooks = [ { regex: /Hexo version/, tag: 'hexo-begin' }, { regex: /Start processing/, tag: 'processing' }, From 8ac908adfa6db6c4de4d3df7c383691a633f1ba9 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 26 Nov 2021 14:31:05 +0100 Subject: [PATCH 025/105] Cleanup dependabot (#4820) And add github actions updates --- .github/dependabot.yml | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2b19176618..0a74941696 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,13 +4,7 @@ updates: directory: "/" schedule: interval: daily - open-pull-requests-limit: 20 - ignore: - - dependency-name: husky - versions: - - 5.0.9 - - 5.1.0 - - 5.1.1 - - 5.1.2 - - 5.1.3 - - 5.2.0 +- package-ecosystem: github-actions + directory: "/" + schedule: + interval: daily From 5977928c240beb714a1e90c1286ecc8290daa7cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 28 Nov 2021 06:50:31 +0800 Subject: [PATCH 026/105] chore: bump actions/stale from 3 to 4 (#4828) Bumps [actions/stale](https://github.com/actions/stale) from 3 to 4. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/stale dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 5ac86f3b11..6ba290f461 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -8,7 +8,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v3 + - uses: actions/stale@v4 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: > From c749815600051d6c3889d9c79c9fdfe33f2d6f20 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 22 Dec 2021 14:04:33 +0100 Subject: [PATCH 027/105] Hexo 6.0.0 (#4750) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6d2056eb0d..4e4dfe95b9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hexo", - "version": "5.4.0", + "version": "6.0.0", "description": "A fast, simple & powerful blog framework, powered by Node.js.", "main": "lib/hexo", "bin": { From 30da1f04084e017c4335fdca36a53e6de53e5c68 Mon Sep 17 00:00:00 2001 From: yoshinorin Date: Sat, 1 Jan 2022 15:11:35 +0900 Subject: [PATCH 028/105] chore: delete question-help ISSUTE_TEMPLATE & add discussion link for question --- .github/ISSUE_TEMPLATE/config.yml | 5 ++ .github/ISSUE_TEMPLATE/question-help.md | 65 ------------------------- 2 files changed, 5 insertions(+), 65 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/question-help.md diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..3c6bdca6bd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a Question, Help, Discuss + url: https://github.com/hexojs/hexo/discussions + about: I have a question, help for hexo (e.g. Customize) \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/question-help.md b/.github/ISSUE_TEMPLATE/question-help.md deleted file mode 100644 index 4416a4817c..0000000000 --- a/.github/ISSUE_TEMPLATE/question-help.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -name: Question/Help -about: I have a question, help for hexo (e.g. Customize) -title: '' -labels: '' -assignees: '' - ---- - - - -## Check List - -Please check followings before submitting a new issue. - -- [ ] I have already read [Docs page](https://hexo.io/docs/) & [Troubleshooting page](https://hexo.io/docs/troubleshooting) -- [ ] I have already searched existing issues and they are not help to me -- [ ] I examined error or warning messages and it's difficult to solve -- [ ] Using [the latest](https://github.com/hexojs/hexo/releases) version of Hexo (run `hexo version` to check) -- [ ] Node.js is higher than [minimum required version](https://hexo.io/docs/#Minimum-required-Node-js-version) - -## Question - - - -## Environment & Settings - -**Node.js & npm version** - -``` -``` - -**Your site `_config.yml`** (Optional) - -``` -``` - -**Your theme `_config.yml`** (Optional) - -``` -``` - -**Hexo and Plugin version(`npm ls --depth 0`)** - -``` -``` - -**Your package.json `package.json`** - -``` -``` - -## Others - - From 4fa805e16cc4197b9aa7d8df2d2546b3d63e2448 Mon Sep 17 00:00:00 2001 From: yoshinorin Date: Sat, 1 Jan 2022 18:19:02 +0900 Subject: [PATCH 029/105] test: add test for skip render in the subdir assets --- test/scripts/processors/post.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/scripts/processors/post.js b/test/scripts/processors/post.js index 59d5b03dc2..429a3e290a 100644 --- a/test/scripts/processors/post.js +++ b/test/scripts/processors/post.js @@ -88,6 +88,18 @@ describe('post', () => { hexo.config.skip_render = ['_posts/foo/**']; pattern.match('_posts/foo/bar.html').should.have.property('renderable', false); hexo.config.skip_render = []; + + // Skip render in the subdir assets if post_asset_folder is enabled + hexo.config.post_asset_folder = true; + pattern.match('_posts/foo/subdir/bar.html').should.have.property('renderable', false); + pattern.match('_posts/foo/subdir/bar.css').should.have.property('renderable', false); + pattern.match('_posts/foo/subdir/bar.js').should.have.property('renderable', false); + hexo.config.post_asset_folder = false; + + // Render in the subdir assets if post_asset_folder is disabled + pattern.match('_posts/foo/subdir/bar.html').should.have.property('renderable', true); + pattern.match('_posts/foo/subdir/bar.css').should.have.property('renderable', true); + pattern.match('_posts/foo/subdir/bar.js').should.have.property('renderable', true); }); it('asset - post_asset_folder disabled', async () => { From 8f01a02a68ad0a8e822b0089a397eef454fea385 Mon Sep 17 00:00:00 2001 From: yoshinorin Date: Sat, 1 Jan 2022 18:20:20 +0900 Subject: [PATCH 030/105] chore: lint --- lib/plugins/processor/post.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/plugins/processor/post.js b/lib/plugins/processor/post.js index 92fb3e2037..98e17acdbb 100644 --- a/lib/plugins/processor/post.js +++ b/lib/plugins/processor/post.js @@ -241,10 +241,10 @@ module.exports = ctx => { if (!result || isHiddenFile(result.path)) return; - //checks only if there is a renderer for the file type or if is included in skip_render + // checks only if there is a renderer for the file type or if is included in skip_render result.renderable = ctx.render.isRenderable(path) && !isMatch(path, ctx.config.skip_render); - //if post_asset_folder is set, restrict renderable files to default file extension + // if post_asset_folder is set, restrict renderable files to default file extension if (result.renderable && ctx.config.post_asset_folder) { result.renderable = (extname(ctx.config.new_post_name) === extname(path)); } From ead2e4ca380667856dee44314a64e9bb1e47af85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jan 2022 14:37:16 +0900 Subject: [PATCH 031/105] chore: bump 0x from 4.11.0 to 5.0.0 (#4846) Bumps [0x](https://github.com/davidmarkclements/0x) from 4.11.0 to 5.0.0. - [Release notes](https://github.com/davidmarkclements/0x/releases) - [Changelog](https://github.com/davidmarkclements/0x/blob/master/changelog.md) - [Commits](https://github.com/davidmarkclements/0x/compare/v4.11.0...v5.0.0) --- updated-dependencies: - dependency-name: 0x dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4e4dfe95b9..ae9ea87c78 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ }, "devDependencies": { "@easyops/git-exec-and-restage": "^1.0.4", - "0x": "^4.10.2", + "0x": "^5.0.0", "chai": "^4.2.0", "cheerio": "0.22.0", "decache": "^4.5.1", From 9d064d247c50ed256873cb266f4b0af9dcd53548 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Jan 2022 19:29:53 +0900 Subject: [PATCH 032/105] chore: bump hexo-front-matter from 2.0.0 to 3.0.0 (#4856) Bumps [hexo-front-matter](https://github.com/hexojs/hexo-front-matter) from 2.0.0 to 3.0.0. - [Release notes](https://github.com/hexojs/hexo-front-matter/releases) - [Commits](https://github.com/hexojs/hexo-front-matter/compare/2.0.0...3.0.0) --- updated-dependencies: - dependency-name: hexo-front-matter dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ae9ea87c78..8b71edd736 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "archy": "^1.0.0", "bluebird": "^3.5.2", "hexo-cli": "^4.0.0", - "hexo-front-matter": "^2.0.0", + "hexo-front-matter": "^3.0.0", "hexo-fs": "^3.1.0", "hexo-i18n": "^1.0.0", "hexo-log": "^3.0.0", From 4c4bd2202f11881d62416cf7eda31b00df61f641 Mon Sep 17 00:00:00 2001 From: Sukka Date: Sun, 9 Jan 2022 20:42:26 +0800 Subject: [PATCH 033/105] fix(plugin): use `require.resolve` to prevent conflict with @vercel/nft (#4863) --- lib/hexo/index.js | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/lib/hexo/index.js b/lib/hexo/index.js index 578cefe914..332080396f 100644 --- a/lib/hexo/index.js +++ b/lib/hexo/index.js @@ -23,8 +23,8 @@ const Locals = require('./locals'); const defaultConfig = require('./default_config'); const loadDatabase = require('./load_database'); const multiConfigPath = require('./multi_config_path'); -const { sync } = require('resolve'); const { deepMerge, full_url_for } = require('hexo-util'); +let resolveSync; // = require('resolve'); const libDir = dirname(__dirname); const dbVersion = 1; @@ -180,7 +180,7 @@ class Hexo extends EventEmitter { const query = {}; if (!this.config.future) { - query.date = {$lte: Date.now()}; + query.date = { $lte: Date.now() }; } if (!this._showDrafts()) { @@ -194,7 +194,7 @@ class Hexo extends EventEmitter { const query = {}; if (!this.config.future) { - query.date = {$lte: Date.now()}; + query.date = { $lte: Date.now() }; } return db.model('Page').find(query); @@ -241,7 +241,7 @@ class Hexo extends EventEmitter { 'load_config', // Load config 'load_theme_config', // Load alternate theme config 'load_plugins' // Load external plugins & scripts - ], name => require(`./${name}`)(this)).then(() => this.execFilter('after_init', null, {context: this})).then(() => { + ], name => require(`./${name}`)(this)).then(() => this.execFilter('after_init', null, { context: this })).then(() => { // Ready to go! this.emit('ready'); }); @@ -265,12 +265,19 @@ class Hexo extends EventEmitter { resolvePlugin(name, basedir) { try { - // Try to resolve the plugin with the resolve.sync. - return sync(name, { basedir }); + // Try to resolve the plugin with the Node.js's built-in require.resolve. + return require.resolve(name, { paths: [basedir] }); } catch (err) { - // There was an error (likely the plugin wasn't found), so return a possibly - // non-existing path that a later part of the resolution process will check. - return join(basedir, 'node_modules', name); + try { + // There was an error (likely the node_modules is corrupt or from early version of npm) + // Use Hexo prior 6.0.0's behavior (resolve.sync) to resolve the plugin. + resolveSync = resolveSync || require('resolve').sync; + return resolveSync(name, { basedir }); + } catch (err) { + // There was an error (likely the plugin wasn't found), so return a possibly + // non-existing path that a later part of the resolution process will check. + return join(basedir, 'node_modules', name); + } } } @@ -314,7 +321,7 @@ class Hexo extends EventEmitter { ]); }).then(() => { mergeCtxThemeConfig(this); - return this._generate({cache: false}); + return this._generate({ cache: false }); }).asCallback(callback); } @@ -329,7 +336,7 @@ class Hexo extends EventEmitter { // enable cache when run hexo server useCache = true; } - this._watchBox = debounce(() => this._generate({cache: useCache}), 100); + this._watchBox = debounce(() => this._generate({ cache: useCache }), 100); return loadDatabase(this).then(() => { this.log.info('Start processing'); @@ -347,7 +354,7 @@ class Hexo extends EventEmitter { mergeCtxThemeConfig(this); }); - return this._generate({cache: useCache}); + return this._generate({ cache: useCache }); }).asCallback(callback); } @@ -421,7 +428,7 @@ class Hexo extends EventEmitter { return path; } - return this.execFilter('template_locals', new Locals(path, data), {context: this}) + return this.execFilter('template_locals', new Locals(path, data), { context: this }) .then(locals => { route.set(path, createLoadThemeRoute(generatorResult, locals, this)); }) .thenReturn(path); }).then(newRouteList => { @@ -446,12 +453,12 @@ class Hexo extends EventEmitter { this.emit('generateBefore'); // Run before_generate filters - return this.execFilter('before_generate', this.locals.get('data'), {context: this}) + return this.execFilter('before_generate', this.locals.get('data'), { context: this }) .then(() => this._routerReflesh(this._runGenerators(), useCache)).then(() => { this.emit('generateAfter'); // Run after_generate filters - return this.execFilter('after_generate', null, {context: this}); + return this.execFilter('after_generate', null, { context: this }); }).finally(() => { this._isGenerating = false; }); @@ -460,13 +467,13 @@ class Hexo extends EventEmitter { exit(err) { if (err) { this.log.fatal( - {err}, + { err }, 'Something\'s wrong. Maybe you can find the solution here: %s', underline('https://hexo.io/docs/troubleshooting.html') ); } - return this.execFilter('before_exit', null, {context: this}).then(() => { + return this.execFilter('before_exit', null, { context: this }).then(() => { this.emit('exit', err); }); } From 133679f70f681e6755e8ca4b3277277fc0a712b0 Mon Sep 17 00:00:00 2001 From: Mimi <1119186082@qq.com> Date: Sun, 16 Jan 2022 12:24:06 +0800 Subject: [PATCH 034/105] github(pull_request_template): remove `How to test` section (#4576) * github(pull_request_template): remove `How to test` section * chore: commenter workflow * Rename to commenter --- .github/PULL_REQUEST_TEMPLATE.md | 9 --------- .github/workflows/commenter.yml | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/commenter.yml diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index df47787277..e5d76da3e4 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -6,15 +6,6 @@ Thank you for creating a pull request to contribute to Hexo code! Before you ope -## How to test - -```sh -git clone -b BRANCH https://github.com/USER/hexo.git -cd hexo -npm install -npm test -``` - ## Screenshots diff --git a/.github/workflows/commenter.yml b/.github/workflows/commenter.yml new file mode 100644 index 0000000000..05de2c3675 --- /dev/null +++ b/.github/workflows/commenter.yml @@ -0,0 +1,20 @@ +name: Commenter + +on: [pull_request_target] + +jobs: + commenter: + runs-on: ubuntu-latest + steps: + - name: Comment PR + uses: marocchino/sticky-pull-request-comment@v2 + with: + message: | + ## How to test + + ```sh + git clone -b ${{ github.head_ref }} https://github.com/${{ github.repository }}.git + cd hexo + npm install + npm test + ``` From 62d7235fa4f2ff5d32eb729f6143bbdeb03762e7 Mon Sep 17 00:00:00 2001 From: Marco Franssen Date: Wed, 19 Jan 2022 13:17:32 +0100 Subject: [PATCH 035/105] Fix js-yaml tags for v4.0.0+ (#4869) Repair backwards compatibility https://github.com/nodeca/js-yaml/blob/master/migrate_v3_to_v4.md with a bunch of plugins that use these yaml tags. e.g.: https://github.com/lavas-project/hexo-pwa#options uses these yaml tags in the routes `!!js/regexp` --- lib/plugins/renderer/yaml.js | 3 ++- package.json | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/plugins/renderer/yaml.js b/lib/plugins/renderer/yaml.js index dc3a5296cf..15d1e59b79 100644 --- a/lib/plugins/renderer/yaml.js +++ b/lib/plugins/renderer/yaml.js @@ -2,9 +2,10 @@ const yaml = require('js-yaml'); const { escape } = require('hexo-front-matter'); +const schema = yaml.DEFAULT_SCHEMA.extend(require('js-yaml-js-types').all); function yamlHelper(data) { - return yaml.load(escape(data.text)); + return yaml.load(escape(data.text), { schema }); } module.exports = yamlHelper; diff --git a/package.json b/package.json index 8b71edd736..9ea9c84b88 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "hexo-log": "^3.0.0", "hexo-util": "^2.4.0", "js-yaml": "^4.0.0", + "js-yaml-js-types": "^1.0.0", "micromatch": "^4.0.2", "moize": "^6.1.0", "moment": "^2.22.2", From d2a2a094e963edac657b6cd55f8d2d50b1b6b1e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 22 Jan 2022 17:59:11 +0800 Subject: [PATCH 036/105] chore: bump eslint-config-hexo from 4.2.0 to 5.0.0 (#4861) Bumps [eslint-config-hexo](https://github.com/hexojs/eslint-config-hexo) from 4.2.0 to 5.0.0. - [Release notes](https://github.com/hexojs/eslint-config-hexo/releases) - [Commits](https://github.com/hexojs/eslint-config-hexo/compare/4.2.0...5.0.0) --- updated-dependencies: - dependency-name: eslint-config-hexo dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9ea9c84b88..29643c6d85 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "cheerio": "0.22.0", "decache": "^4.5.1", "eslint": "^8.0.0", - "eslint-config-hexo": "^4.1.0", + "eslint-config-hexo": "^5.0.0", "hexo-renderer-marked": "^4.0.0", "husky": "^7.0.2", "lint-staged": "^11.0.0", From f12a8db03b392bb5c32c0649302a3d0cc8768832 Mon Sep 17 00:00:00 2001 From: Diana <67607236+dbelokon@users.noreply.github.com> Date: Sat, 22 Jan 2022 05:01:07 -0500 Subject: [PATCH 037/105] feat: line threshold option for code tag plugin (#4821) --- lib/plugins/tag/code.js | 33 ++++++++++++++++++++++++---- lib/plugins/tag/include_code.js | 38 +++++++++++++++++++-------------- test/scripts/tags/code.js | 30 ++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 20 deletions(-) diff --git a/lib/plugins/tag/code.js b/lib/plugins/tag/code.js index 30c7f17c5f..755ef44749 100644 --- a/lib/plugins/tag/code.js +++ b/lib/plugins/tag/code.js @@ -34,7 +34,7 @@ function parseArgs(args) { const _else = []; const len = args.length; let lang, - line_number, wrap; + line_number, line_threshold, wrap; let firstLine = 1; const mark = []; for (let i = 0; i < len; i++) { @@ -55,6 +55,9 @@ function parseArgs(args) { case 'line_number': line_number = value === 'true'; break; + case 'line_threshold': + if (!isNaN(value)) line_threshold = +value; + break; case 'first_line': if (!isNaN(value)) firstLine = +value; break; @@ -105,6 +108,7 @@ function parseArgs(args) { firstLine, caption, line_number, + line_threshold, mark, wrap }; @@ -134,14 +138,23 @@ module.exports = ctx => function codeTag(args, content) { return `
    ${escapeHTML(content)}
    `; } - const { lang, firstLine, caption, line_number, mark, wrap } = parseArgs(args); + const { lang, firstLine, caption, line_number, line_threshold, mark, wrap } = parseArgs(args); if (prismjsCfg.enable) { + const shouldUseLineNumbers = typeof line_number !== 'undefined' ? line_number : prismjsCfg.line_number; + let surpassesLineThreshold; + + if (typeof line_threshold !== 'undefined') { + surpassesLineThreshold = content.split('\n').length > line_threshold; + } else { + surpassesLineThreshold = content.split('\n').length > (prismjsCfg.line_threshold || 0); + } + const prismjsOption = { lang, firstLine, caption, - lineNumber: typeof line_number !== 'undefined' ? line_number : prismjsCfg.line_number, + lineNumber: shouldUseLineNumbers && surpassesLineThreshold, mark, tab: prismjsCfg.tab_replace, isPreprocess: prismjsCfg.preprocess @@ -151,11 +164,23 @@ module.exports = ctx => function codeTag(args, content) { content = prismHighlight(content, prismjsOption); } else { + const shouldUseLineNumbers = typeof line_number !== 'undefined' + ? line_number : hljsCfg.line_number; + let surpassesLineThreshold; + + if (typeof line_threshold !== 'undefined') { + surpassesLineThreshold + = content.split('\n').length > line_threshold; + } else { + surpassesLineThreshold + = content.split('\n').length > (hljsCfg.line_threshold || 0); + } + const hljsOption = { lang: typeof lang !== 'undefined' ? lang : '', firstLine, caption, - gutter: typeof line_number !== 'undefined' ? line_number : hljsCfg.line_number, + gutter: shouldUseLineNumbers && surpassesLineThreshold, hljs: hljsCfg.hljs, mark, tab: hljsCfg.tab_replace, diff --git a/lib/plugins/tag/include_code.js b/lib/plugins/tag/include_code.js index b1fb40df38..59e4436a16 100644 --- a/lib/plugins/tag/include_code.js +++ b/lib/plugins/tag/include_code.js @@ -60,22 +60,6 @@ module.exports = ctx => function includeCodeTag(args) { const hljsCfg = ctx.config.highlight || {}; const prismjsCfg = ctx.config.prismjs || {}; - const hljsOptions = { - lang, - caption, - gutter: hljsCfg.line_number, - hljs: hljsCfg.hljs, - tab: hljsCfg.tab_replace - }; - - const prismjsOptions = { - lang, - caption, - lineNumber: prismjsCfg.line_number, - tab: prismjsCfg.tab_replace, - isPreprocess: prismjsCfg.preprocess - }; - return exists(src).then(exist => { if (exist) return readFile(src); }).then(code => { @@ -85,10 +69,32 @@ module.exports = ctx => function includeCodeTag(args) { code = lines.slice(from, to).join('\n').trim(); if (prismjsCfg.enable) { + const line_threshold = prismjsCfg.line_threshold + ? prismjsCfg.line_threshold : 0; + + const prismjsOptions = { + lang, + caption, + lineNumber: prismjsCfg.line_number && lines.length > line_threshold, + tab: prismjsCfg.tab_replace, + isPreprocess: prismjsCfg.preprocess + }; + if (!prismHighlight) prismHighlight = require('hexo-util').prismHighlight; return prismHighlight(code, prismjsOptions); } else if (hljsCfg.enable) { + const line_threshold = hljsCfg.line_threshold + ? hljsCfg.line_threshold : 0; + + const hljsOptions = { + lang, + caption, + gutter: hljsCfg.line_number && lines.length > line_threshold, + hljs: hljsCfg.hljs, + tab: hljsCfg.tab_replace + }; + if (!highlight) highlight = require('hexo-util').highlight; return highlight(code, hljsOptions); diff --git a/test/scripts/tags/code.js b/test/scripts/tags/code.js index dd3ad10123..acf954815b 100644 --- a/test/scripts/tags/code.js +++ b/test/scripts/tags/code.js @@ -68,6 +68,21 @@ describe('code', () => { })); }); + it('line_threshold', () => { + let result = code('line_number:false line_threshold:1', fixture); + result.should.eql(highlight(fixture, { + gutter: false + })); + result = code('line_number:true line_threshold:1', fixture); + result.should.eql(highlight(fixture, { + gutter: true + })); + result = code('line_number:true line_threshold:3', fixture); + result.should.eql(highlight(fixture, { + gutter: false + })); + }); + it('highlight disable', () => { const result = code('highlight:false', fixture); result.should.eql('
    ' + escapeHTML(fixture) + '
    '); @@ -202,6 +217,21 @@ describe('code', () => { })); }); + it('line_threshold', () => { + let result = code('line_number:false line_threshold:1', fixture); + result.should.eql(prism(fixture, { + lineNumber: false + })); + result = code('line_number:true line_threshold:1', fixture); + result.should.eql(prism(fixture, { + lineNumber: true + })); + result = code('line_number:true line_threshold:3', fixture); + result.should.eql(prism(fixture, { + lineNumber: false + })); + }); + it('highlight disable', () => { const result = code('highlight:false', fixture); result.should.eql('
    ' + escapeHTML(fixture) + '
    '); From 860f1bfb77ef8306b7d3a39a4ff8481544a15360 Mon Sep 17 00:00:00 2001 From: Baoshuo Ren Date: Wed, 9 Feb 2022 19:45:16 +0800 Subject: [PATCH 038/105] fix: typo (#4896) --- lib/plugins/tag/code.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/tag/code.js b/lib/plugins/tag/code.js index 755ef44749..8bcecdd629 100644 --- a/lib/plugins/tag/code.js +++ b/lib/plugins/tag/code.js @@ -133,7 +133,7 @@ module.exports = ctx => function codeTag(args, content) { args.splice(index, 1); } - // If 'hilight: false' is given, return escaped code directly + // If 'highlight: false' is given, return escaped code directly if (!enableHighlight) { return `
    ${escapeHTML(content)}
    `; } From 1b824df04bd45a65422c52ace442c1c144fada64 Mon Sep 17 00:00:00 2001 From: Mimi <1119186082@qq.com> Date: Fri, 18 Feb 2022 18:46:05 +0800 Subject: [PATCH 039/105] chore/ci: reply flamegraph URL in pull request (#4793) --- .github/workflows/benchmark.yml | 5 +++++ .github/workflows/commenter.yml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 3bd6d76e25..28fffc4d5d 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -52,3 +52,8 @@ jobs: project: ./.tmp-hexo-theme-unit-test/0x/ login: ${{ secrets.SURGE_LOGIN }} token: ${{ secrets.SURGE_TOKEN }} + - name: Comment PR + uses: marocchino/sticky-pull-request-comment@v2 + with: + message: | + Publish flamegraph to https://${{ github.sha }}-${{ matrix.node-version }}-hexo.surge.sh/flamegraph.html diff --git a/.github/workflows/commenter.yml b/.github/workflows/commenter.yml index 05de2c3675..480f01b595 100644 --- a/.github/workflows/commenter.yml +++ b/.github/workflows/commenter.yml @@ -13,7 +13,7 @@ jobs: ## How to test ```sh - git clone -b ${{ github.head_ref }} https://github.com/${{ github.repository }}.git + git clone -b ${{ github.head_ref }} https://github.com/${{ github.event.pull_request.head.repo.full_name }}.git cd hexo npm install npm test From 3e3c4ba8df1d46178c7bccde301abea323d805f7 Mon Sep 17 00:00:00 2001 From: Sukka Date: Fri, 25 Feb 2022 20:12:41 +0800 Subject: [PATCH 040/105] chore/test: use chai#should (#4902) --- .mocharc.yml | 2 -- test/index.js | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.mocharc.yml b/.mocharc.yml index d001bb7072..0eaf156b91 100644 --- a/.mocharc.yml +++ b/.mocharc.yml @@ -3,6 +3,4 @@ reporter: spec ui: bdd full-trace: true exit: true -require: - - "chai/register-should" parallel: true diff --git a/test/index.js b/test/index.js index 29cb5450e6..5baf989398 100644 --- a/test/index.js +++ b/test/index.js @@ -1,6 +1,7 @@ 'use strict'; -require('chai'); +const chai = require('chai'); +global.should = chai.should(); describe('Hexo', () => { require('./scripts/box'); From 035c73f2926eae674033ca149f4b6d7a21d9ffb2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Feb 2022 18:08:19 +0900 Subject: [PATCH 041/105] chore: bump actions/setup-node from 2 to 3 (#4904) Bumps [actions/setup-node](https://github.com/actions/setup-node) from 2 to 3. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/benchmark.yml | 4 ++-- .github/workflows/linter.yml | 2 +- .github/workflows/tester.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 28fffc4d5d..694d06a77d 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -21,7 +21,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - name: Install dependencies @@ -38,7 +38,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - name: Install dependencies diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 6dc94c72d9..735309df11 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -8,7 +8,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Use Node.js 14.x - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: '14.x' - name: Install Dependencies diff --git a/.github/workflows/tester.yml b/.github/workflows/tester.yml index 2ca7f8408a..ddbbb1a031 100644 --- a/.github/workflows/tester.yml +++ b/.github/workflows/tester.yml @@ -13,7 +13,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - name: Install Dependencies @@ -31,7 +31,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - name: Install Dependencies From c73dbedebb6e5a270327f804485f434d838bd264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=80=E6=9E=9A=E5=B0=8F=E7=8C=BF?= Date: Sun, 27 Feb 2022 20:16:09 +0800 Subject: [PATCH 042/105] Exclude some languages in code highlghting. (#3865) Signed-off-by: corvofeng --- lib/hexo/default_config.js | 1 + .../before_post_render/backtick_code_block.js | 7 +++++++ test/scripts/filters/backtick_code_block.js | 20 +++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/lib/hexo/default_config.js b/lib/hexo/default_config.js index e5eb3ac60a..ecf688faf8 100644 --- a/lib/hexo/default_config.js +++ b/lib/hexo/default_config.js @@ -46,6 +46,7 @@ module.exports = { line_number: true, tab_replace: '', wrap: true, + exclude_languages: [], hljs: false }, prismjs: { diff --git a/lib/plugins/filter/before_post_render/backtick_code_block.js b/lib/plugins/filter/before_post_render/backtick_code_block.js index 6b4c660a76..6d3b7ed1ee 100644 --- a/lib/plugins/filter/before_post_render/backtick_code_block.js +++ b/lib/plugins/filter/before_post_render/backtick_code_block.js @@ -89,6 +89,13 @@ function backtickCodeBlock(data) { } } + if (Array.isArray(hljsCfg.exclude_languages) && hljsCfg.exclude_languages.includes(options.lang)) { + // Only wrap with
    + options.wrap = false; + options.gutter = false; + options.autoDetect = false; + } + content = highlight(content, options); } diff --git a/test/scripts/filters/backtick_code_block.js b/test/scripts/filters/backtick_code_block.js index cbe23d0770..990084b19c 100644 --- a/test/scripts/filters/backtick_code_block.js +++ b/test/scripts/filters/backtick_code_block.js @@ -241,6 +241,26 @@ describe('Backtick code block', () => { data.content.should.eql('' + expected + ''); }); + it('only wrap with pre and code', () => { + hexo.config.highlight.exclude_languages = ['js']; + hexo.config.highlight.hljs = true; + const data = { + content: [ + '``` js', + code, + '```' + ].join('\n') + }; + const expected = highlight(code, { + lang: 'js', + gutter: false, + hljs: true, + wrap: false + }); + codeBlock(data); + data.content.should.eql('' + expected + ''); + }); + it('line number false, don`t care first_line_number inilne', () => { hexo.config.highlight.line_number = false; hexo.config.highlight.first_line_number = 'inilne'; From 19d8a6a537147026cf0c2f7fed6f4b142d7f6d42 Mon Sep 17 00:00:00 2001 From: yoshinorin Date: Wed, 2 Mar 2022 19:54:25 +0900 Subject: [PATCH 043/105] chore(deps): update some dependencies minor & patch version (#4898) --- package.json | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 29643c6d85..0cb29dc77a 100644 --- a/package.json +++ b/package.json @@ -41,42 +41,42 @@ "dependencies": { "abbrev": "^1.1.1", "archy": "^1.0.0", - "bluebird": "^3.5.2", - "hexo-cli": "^4.0.0", + "bluebird": "^3.7.2", + "hexo-cli": "^4.3.0", "hexo-front-matter": "^3.0.0", "hexo-fs": "^3.1.0", "hexo-i18n": "^1.0.0", "hexo-log": "^3.0.0", - "hexo-util": "^2.4.0", - "js-yaml": "^4.0.0", + "hexo-util": "^2.5.0", + "js-yaml": "^4.1.0", "js-yaml-js-types": "^1.0.0", - "micromatch": "^4.0.2", + "micromatch": "^4.0.4", "moize": "^6.1.0", - "moment": "^2.22.2", - "moment-timezone": "^0.5.21", + "moment": "^2.29.1", + "moment-timezone": "^0.5.34", "picocolors": "^1.0.0", - "nunjucks": "^3.2.1", + "nunjucks": "^3.2.3", "pretty-hrtime": "^1.0.3", - "resolve": "^1.8.1", + "resolve": "^1.22.0", "strip-ansi": "^6.0.0", "text-table": "^0.2.0", "tildify": "^2.0.0", - "titlecase": "^1.1.2", + "titlecase": "^1.1.3", "warehouse": "^4.0.0" }, "devDependencies": { "@easyops/git-exec-and-restage": "^1.0.4", - "0x": "^5.0.0", - "chai": "^4.2.0", + "0x": "^5.1.2", + "chai": "^4.3.6", "cheerio": "0.22.0", - "decache": "^4.5.1", - "eslint": "^8.0.0", + "decache": "^4.6.1", + "eslint": "^8.8.0", "eslint-config-hexo": "^5.0.0", "hexo-renderer-marked": "^4.0.0", - "husky": "^7.0.2", + "husky": "^7.0.4", "lint-staged": "^11.0.0", - "mocha": "^9.1.1", - "nyc": "^15.0.0", + "mocha": "^9.2.0", + "nyc": "^15.1.0", "sinon": "^12.0.1" }, "engines": { From f281fc2c93212ae8f00e93088642dd7afad396c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Mar 2022 23:23:46 +0800 Subject: [PATCH 044/105] chore: bump hexo-renderer-marked from 4.1.0 to 5.0.0 (#4891) Bumps [hexo-renderer-marked](https://github.com/hexojs/hexo-renderer-marked) from 4.1.0 to 5.0.0. - [Release notes](https://github.com/hexojs/hexo-renderer-marked/releases) - [Commits](https://github.com/hexojs/hexo-renderer-marked/compare/v4.1.0...5.0.0) --- updated-dependencies: - dependency-name: hexo-renderer-marked dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0cb29dc77a..b1b9b746fb 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "decache": "^4.6.1", "eslint": "^8.8.0", "eslint-config-hexo": "^5.0.0", - "hexo-renderer-marked": "^4.0.0", + "hexo-renderer-marked": "^5.0.0", "husky": "^7.0.4", "lint-staged": "^11.0.0", "mocha": "^9.2.0", From 14d8edc43d5df8a9585cca32fc5c996fdedead7a Mon Sep 17 00:00:00 2001 From: Sukka Date: Sun, 6 Mar 2022 11:14:03 +0800 Subject: [PATCH 045/105] chore/fix(benchmark): fix benchmark on node 16+ (#4906) --- test/benchmark.js | 71 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/test/benchmark.js b/test/benchmark.js index b74c904549..5993632f6d 100644 --- a/test/benchmark.js +++ b/test/benchmark.js @@ -55,16 +55,30 @@ if (!isProfiling && !isBenchmark) { })(); async function run_benchmark(name) { + let measureFinished = false; + return new Promise(resolve => { const result = {}; const obs = new PerformanceObserver(list => { - const { name, duration: _duration } = list.getEntries()[0]; - const duration = _duration / 1000; - result[name] = { - 'Cost time (s)': `${duration.toFixed(2)}s` - }; - if (duration > 20) { - log.fatal(red('!! Performance regression detected !!')); + list + .getEntries() + .sort((a, b) => a.detail - b.detail) + .forEach(entry => { + const { name, duration: _duration } = entry; + const duration = _duration / 1000; + result[name] = { + 'Cost time (s)': `${duration.toFixed(2)}s` + }; + if (duration > 20) { + log.fatal(red('!! Performance regression detected !!')); + } + }); + + if (measureFinished) { + obs.disconnect(); + console.log(name); + console.table(result); + resolve(result); } }); obs.observe({ entryTypes: ['measure'] }); @@ -84,19 +98,42 @@ async function run_benchmark(name) { performance.measure('Load Plugin/Scripts/Database', 'hexo-begin', 'processing'); if (name === 'Hot processing') { - performance.measure('Process Source', 'processing', 'file-loaded'); + performance.measure('Process Source', { + start: 'processing', + end: 'file-loaded', + detail: 0 + }); } else { - performance.measure('Process Source', 'processing', 'render-post'); - performance.measure('Render Posts', 'render-post', 'file-loaded'); + performance.measure('Process Source', { + start: 'processing', + end: 'render-post', + detail: 1 + }); + performance.measure('Render Posts', { + start: 'render-post', + end: 'file-loaded', + detail: 2 + }); } - performance.measure('Render Files', 'file-loaded', 'generated'); - performance.measure('Save Database', 'generated', 'database-saved'); - performance.measure('Total time', 'hexo-begin', 'database-saved'); - console.log(name); - console.table(result); - obs.disconnect(); - resolve(); + performance.measure('Render Files', { + start: 'file-loaded', + end: 'generated', + detail: 3 + }); + performance.measure('Save Database', { + start: 'generated', + end: 'database-saved', + detail: 4 + }); + + performance.measure('Total time', { + start: 'hexo-begin', + end: 'database-saved', + detail: 5 + }); + + measureFinished = true; }); }); } From 57c54f236ad17f7cc6ef2172cd5174333f10f8b6 Mon Sep 17 00:00:00 2001 From: yoshinorin Date: Sun, 6 Mar 2022 20:44:48 +0900 Subject: [PATCH 046/105] chore(deps): bump hexo-util from 2.5.0 to 2.6.0 (#4908) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b1b9b746fb..27688a3202 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "hexo-fs": "^3.1.0", "hexo-i18n": "^1.0.0", "hexo-log": "^3.0.0", - "hexo-util": "^2.5.0", + "hexo-util": "^2.6.0", "js-yaml": "^4.1.0", "js-yaml-js-types": "^1.0.0", "micromatch": "^4.0.4", From 24d8c3139877fbef2b78ef0670f60ced928bbf80 Mon Sep 17 00:00:00 2001 From: Cerallin <66366855+Cerallin@users.noreply.github.com> Date: Sun, 6 Mar 2022 21:57:18 +0800 Subject: [PATCH 047/105] feat(toc): Support unnumbered headings (#4871) --- lib/plugins/helper/toc.js | 7 ++++--- test/scripts/helpers/toc.js | 42 +++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/lib/plugins/helper/toc.js b/lib/plugins/helper/toc.js index 4713fc350d..e6e4fa8c7b 100644 --- a/lib/plugins/helper/toc.js +++ b/lib/plugins/helper/toc.js @@ -28,7 +28,9 @@ function tocHelper(str, options = {}) { const { level, id, text } = el; const href = id ? `#${encodeURL(id)}` : null; - lastNumber[level - 1]++; + if (!el.unnumbered) { + lastNumber[level - 1]++; + } for (let i = level; i <= 5; i++) { lastNumber[i] = 0; @@ -55,8 +57,7 @@ function tocHelper(str, options = {}) { result += ``; } - - if (listNumber) { + if (listNumber && !el.unnumbered) { result += ``; for (let i = firstLevel - 1; i < level; i++) { diff --git a/test/scripts/helpers/toc.js b/test/scripts/helpers/toc.js index 32ac67215e..08d7c3ebeb 100644 --- a/test/scripts/helpers/toc.js +++ b/test/scripts/helpers/toc.js @@ -434,4 +434,46 @@ describe('toc', () => { toc(input).should.eql(''); }); + + it('unnumbered headings', () => { + const className = 'toc'; + + const input = [ + '

    Title 1

    ', + '

    Title 2

    ', + '

    Title 2.1

    ', + '

    Reference

    ' + ].join(''); + + const expected = [ + `
      `, + `
    1. `, + `1. `, + `Title 1`, + '', + '
    2. ', + `
    3. `, + ``, + `2. `, + `Title 2`, + '', + `
        `, + `
      1. `, + ``, + `2.1. `, + `Title 2.1`, + '', + '
      2. ', + '
      ', + '
    4. ', + `
    5. `, + ``, + `Reference`, + '', + '
    6. ', + '
    ' + ].join(''); + + toc(input, { list_number: true, class: className }).should.eql(expected); + }); }); From 1ff6ba01e4019c89ab1b2c6430e1ae4f9a0dee19 Mon Sep 17 00:00:00 2001 From: yoshinorin Date: Fri, 11 Mar 2022 21:00:41 +0900 Subject: [PATCH 048/105] chore: bump version from 6.0.0 to 6.1.0 (#4877) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 27688a3202..766f275935 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hexo", - "version": "6.0.0", + "version": "6.1.0", "description": "A fast, simple & powerful blog framework, powered by Node.js.", "main": "lib/hexo", "bin": { From f4a5f04bf098357124ab7a354d50453404db3233 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Mar 2022 01:50:24 +0800 Subject: [PATCH 049/105] chore: bump actions/checkout from 2 to 3 (#4905) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/benchmark.yml | 4 ++-- .github/workflows/linter.yml | 2 +- .github/workflows/tester.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 694d06a77d..9390e83c51 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -19,7 +19,7 @@ jobs: node-version: ['12', '14', '16'] fail-fast: false steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: @@ -36,7 +36,7 @@ jobs: node-version: ['12', '14', '16'] fail-fast: false steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 735309df11..6273035b57 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -6,7 +6,7 @@ jobs: linter: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Use Node.js 14.x uses: actions/setup-node@v3 with: diff --git a/.github/workflows/tester.yml b/.github/workflows/tester.yml index ddbbb1a031..64f7130856 100644 --- a/.github/workflows/tester.yml +++ b/.github/workflows/tester.yml @@ -11,7 +11,7 @@ jobs: node-version: ['12.x', '14.x', '16.x'] fail-fast: false steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: @@ -29,7 +29,7 @@ jobs: os: [ubuntu-latest] node-version: ['14.x'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: From ba81258b5b6a1a7ae02033c056d82198c6d92ec9 Mon Sep 17 00:00:00 2001 From: CommanderRoot Date: Thu, 24 Mar 2022 19:01:10 +0100 Subject: [PATCH 050/105] chore: replace deprecated String.prototype.substr() (#4918) String.prototype.substr() is deprecated so we replace it with functions which work similarily but aren't deprecated Signed-off-by: Tobias Speicher --- lib/plugins/helper/number_format.js | 6 +++--- lib/plugins/tag/code.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/plugins/helper/number_format.js b/lib/plugins/helper/number_format.js index 0db807eb76..7a1495f67f 100644 --- a/lib/plugins/helper/number_format.js +++ b/lib/plugins/helper/number_format.js @@ -13,10 +13,10 @@ function numberFormatHelper(num, options = {}) { const beforeLength = before.length; const beforeFirst = beforeLength % 3; - if (beforeFirst) beforeArr.push(before.substr(0, beforeFirst)); + if (beforeFirst) beforeArr.push(before.slice(0, beforeFirst)); for (let i = beforeFirst; i < beforeLength; i += 3) { - beforeArr.push(before.substr(i, 3)); + beforeArr.push(before.slice(i, i + 3)); } before = beforeArr.join(delimiter); @@ -30,7 +30,7 @@ function numberFormatHelper(num, options = {}) { const afterLast = after[precision]; const last = parseInt(after[precision - 1], 10); - afterResult = after.substr(0, precision - 1) + (afterLast < 5 ? last : last + 1); + afterResult = after.substring(0, precision - 1) + (afterLast < 5 ? last : last + 1); } else { afterResult = after; for (let i = 0, len = precision - afterLength; i < len; i++) { diff --git a/lib/plugins/tag/code.js b/lib/plugins/tag/code.js index 8bcecdd629..4b2fdd07f0 100644 --- a/lib/plugins/tag/code.js +++ b/lib/plugins/tag/code.js @@ -68,8 +68,8 @@ function parseArgs(args) { for (const cur of value.split(',')) { const hyphen = cur.indexOf('-'); if (hyphen !== -1) { - let a = +cur.substr(0, hyphen); - let b = +cur.substr(hyphen + 1); + let a = +cur.slice(0, hyphen); + let b = +cur.slice(hyphen + 1); if (Number.isNaN(a) || Number.isNaN(b)) continue; if (b < a) { // switch a & b const temp = a; From 8d2102745fb804278238df4ba7832b871a78d899 Mon Sep 17 00:00:00 2001 From: yoshinorin Date: Mon, 28 Mar 2022 20:22:28 +0900 Subject: [PATCH 051/105] chore(bot): delete stale bot (#4901) --- .github/workflows/stale.yml | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml deleted file mode 100644 index 6ba290f461..0000000000 --- a/.github/workflows/stale.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Close Stale Issues - -on: - schedule: - - cron: '0 0 * * *' - -jobs: - stale: - runs-on: ubuntu-latest - steps: - - uses: actions/stale@v4 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - stale-issue-message: > - This issue has been automatically marked as stale because lack of - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. - exempt-issue-labels: bug,discussion,help wanted,need-verify,need-investigation,pending-reply,proposal,feature-request,enhancement,documentation From e15ad72978a0ce08b3876f2bd410f83c4d745854 Mon Sep 17 00:00:00 2001 From: yoshinorin Date: Mon, 4 Apr 2022 21:38:49 +0900 Subject: [PATCH 052/105] fix(#4917): suppress YAMLException when load js-yaml (#4927) * it is just a workaround --- lib/plugins/renderer/yaml.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/plugins/renderer/yaml.js b/lib/plugins/renderer/yaml.js index 15d1e59b79..81f58547e6 100644 --- a/lib/plugins/renderer/yaml.js +++ b/lib/plugins/renderer/yaml.js @@ -2,7 +2,19 @@ const yaml = require('js-yaml'); const { escape } = require('hexo-front-matter'); -const schema = yaml.DEFAULT_SCHEMA.extend(require('js-yaml-js-types').all); +const log = require('hexo-log')(); + +let schema = {}; +// FIXME: workaround for https://github.com/hexojs/hexo/issues/4917 +try { + schema = yaml.DEFAULT_SCHEMA.extend(require('js-yaml-js-types').all); +} catch (e) { + if (e instanceof yaml.YAMLException) { + log.warn('YAMLException: please see https://github.com/hexojs/hexo/issues/4917'); + } else { + throw e; + } +} function yamlHelper(data) { return yaml.load(escape(data.text), { schema }); From d9ff35bd4af0b7f4e48a727298506ad55825ac9c Mon Sep 17 00:00:00 2001 From: yoshinorin Date: Thu, 7 Apr 2022 16:49:03 +0900 Subject: [PATCH 053/105] chore(ISSUE_TEMPLATE): delete `Your theme _config.yml` section (#4931) --- .github/ISSUE_TEMPLATE/bug_report.md | 5 ----- .github/ISSUE_TEMPLATE/other.md | 5 ----- 2 files changed, 10 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 112b503d54..353fae2aca 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -70,11 +70,6 @@ Please paste the content of your _config.yml between two "```" provided below ```yaml ``` -**Your theme `_config.yml`** (Optional) - -``` -``` - **Hexo and Plugin version(`npm ls --depth 0`)**