From 0c1c172487ec12d353a7cf476cf967e89117d06f Mon Sep 17 00:00:00 2001 From: awongCM Date: Fri, 10 Jun 2022 23:47:05 +1000 Subject: [PATCH 1/5] added unpublish command along with its unit test --- lib/hexo/post.js | 53 ++++++++++++ lib/plugins/console/unpublish.js | 20 +++++ test/scripts/console/index.js | 1 + test/scripts/console/unpublish.js | 139 ++++++++++++++++++++++++++++++ 4 files changed, 213 insertions(+) create mode 100644 lib/plugins/console/unpublish.js create mode 100644 test/scripts/console/unpublish.js diff --git a/lib/hexo/post.js b/lib/hexo/post.js index 7f0db61b..8d5f98c5 100644 --- a/lib/hexo/post.js +++ b/lib/hexo/post.js @@ -306,6 +306,59 @@ class Post { }); } + unpublish(data, replace, callback) { + if (!callback && typeof replace === 'function') { + callback = replace; + replace = false; + } + + if (data.layout === 'post') data.layout = 'draft'; + + const ctx = this.context; + const { config } = ctx; + const postDir = join(ctx.source_dir, '_posts'); + const slug = slugize(data.slug.toString(), { transform: config.filename_case }); + data.slug = slug; + const regex = new RegExp(`^${escapeRegExp(slug)}(?:[^\\/\\\\]+)`); + let src = ''; + const result = {}; + + data.layout = (data.layout || config.default_layout).toLowerCase(); + + // Find the original post + return listDir(postDir).then(list => { + const item = list.find(item => regex.test(item)); + if (!item) throw new Error(`Post "${slug}" does not exist.`); + + // Read the post content + src = join(postDir, item); + return readFile(src); + }).then(content => { + // Create draft + Object.assign(data, yfmParse(content)); + data.content = data._content; + data._content = undefined; + + return this.create(data, replace); + }).then(post => { + result.path = post.path; + result.content = post.content; + return unlink(src); + }).then(() => { // Remove the original post file + if (!config.post_asset_folder) return; + + // Copy assets + const assetSrc = removeExtname(src); + const assetDest = removeExtname(result.path); + + return exists(assetSrc).then(exist => { + if (!exist) return; + + return copyDir(assetSrc, assetDest).then(() => rmdir(assetSrc)); + }); + }).thenReturn(result).asCallback(callback); + } + publish(data, replace, callback) { if (!callback && typeof replace === 'function') { callback = replace; diff --git a/lib/plugins/console/unpublish.js b/lib/plugins/console/unpublish.js new file mode 100644 index 00000000..2fdce813 --- /dev/null +++ b/lib/plugins/console/unpublish.js @@ -0,0 +1,20 @@ +'use strict'; + +const tildify = require('tildify'); +const { magenta } = require('picocolors'); + +function unPublishConsole(args) { + // Display help message if user didn't input any arguments + if (!args._.length) { + return this.call('help', {_: ['unpublish']}); + } + + return this.post.unpublish({ + slug: args._.pop(), + layout: args._.length ? args._[0] : this.config.default_layout + }, args.r || args.replace).then(post => { + this.log.info('Unpublished: %s', magenta(tildify(post.path))); + }); +} + +module.exports = unPublishConsole; diff --git a/test/scripts/console/index.js b/test/scripts/console/index.js index 2c9a1d22..c638a24b 100644 --- a/test/scripts/console/index.js +++ b/test/scripts/console/index.js @@ -8,6 +8,7 @@ describe('Console', () => { require('./migrate'); require('./new'); require('./publish'); + require('./unpublish'); require('./render'); require('./list'); require('./list_post'); diff --git a/test/scripts/console/unpublish.js b/test/scripts/console/unpublish.js new file mode 100644 index 00000000..433be4e7 --- /dev/null +++ b/test/scripts/console/unpublish.js @@ -0,0 +1,139 @@ +'use strict'; + +const { exists, mkdirs, readFile, rmdir, unlink } = require('hexo-fs'); +const moment = require('moment'); +const { join } = require('path'); +const Promise = require('bluebird'); +const { useFakeTimers, spy } = require('sinon'); + +describe('unpublish', () => { + const Hexo = require('../../../lib/hexo'); + const hexo = new Hexo(join(__dirname, 'unpublish_test'), {silent: true}); + const unpublish = require('../../../lib/plugins/console/unpublish').bind(hexo); + const post = hexo.post; + const now = Date.now(); + let clock; + + before(async () => { + clock = useFakeTimers(now); + + await mkdirs(hexo.base_dir); + await hexo.init(); + await hexo.scaffold.set('post', [ + '---', + 'title: {{ title }}', + 'date: {{ date }}', + 'tags:', + '---' + ].join('\n')); + await hexo.scaffold.set('draft', [ + '---', + 'title: {{ title }}', + 'tags:', + '---' + ].join('\n')); + }); + + after(() => { + clock.restore(); + return rmdir(hexo.base_dir); + }); + + beforeEach(() => post.create({ + title: 'Hello World', + layout: 'post', + date: moment(now).format('YYYY-MM-DD HH:mm:ss') + })); + + it('slug', async () => { + const postPath = join(hexo.source_dir, '_posts', 'Hello-World.md'); + const path = join(hexo.source_dir, '_drafts', 'Hello-World.md'); + + const content = [ + '---', + 'title: Hello World', + 'tags:', + '---' + ].join('\n') + '\n'; + + await unpublish({ + _: ['Hello-World'] + }); + + const exist = await exists(postPath); + const data = await readFile(path); + + exist.should.be.false; + data.should.eql(content); + + await unlink(path); + }); + + it('no args', async () => { + const hexo = new Hexo(join(__dirname, 'unpublish_test'), {silent: true}); + hexo.call = spy(); + const unpublish = require('../../../lib/plugins/console/unpublish').bind(hexo); + + await unpublish({_: []}); + + hexo.call.calledOnce.should.be.true; + hexo.call.args[0][0].should.eql('help'); + hexo.call.args[0][1]._[0].should.eql('unpublish'); + }); + + it('layout', async () => { + const path = join(hexo.source_dir, '_posts', 'Hello-World.md'); + const date = moment(now); + + const content = [ + '---', + 'title: Hello World', + 'date: ' + date.format('YYYY-MM-DD HH:mm:ss'), + 'tags:', + '---' + ].join('\n') + '\n'; + + await unpublish({ + _: ['photo', 'Hello-World'] + }); + const data = await readFile(path); + data.should.eql(content); + + await unlink(path); + }); + + it('rename if target existed', async () => { + const path = join(hexo.source_dir, '_posts', 'Hello-World.md'); + + await post.create({ + title: 'Hello World' + }); + await unpublish({ + _: ['Hello-World'] + }); + + const exist = await exists(path); + exist.should.be.true; + + await Promise.all([ + unlink(path), + unlink(join(hexo.source_dir, '_posts', 'Hello-World.md')) + ]); + }); + + it('replace existing target', async () => { + const path = join(hexo.source_dir, '_posts', 'Hello-World.md'); + + await post.create({ + title: 'Hello World' + }); + await unpublish({ + _: ['Hello-World'], + replace: true + }); + const exist = await exists(join(hexo.source_dir, '_drafts', 'Hello-World-1.md')); + exist.should.be.false; + + await unlink(path); + }); +}); From 34b46ade629f979230aaa1f15e782ace99997af5 Mon Sep 17 00:00:00 2001 From: awongCM Date: Sat, 11 Jun 2022 17:27:22 +1000 Subject: [PATCH 2/5] register unpublish command to console --- lib/plugins/console/index.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/plugins/console/index.js b/lib/plugins/console/index.js index 3a34b7b0..fb466a1b 100644 --- a/lib/plugins/console/index.js +++ b/lib/plugins/console/index.js @@ -67,6 +67,14 @@ module.exports = function(ctx) { ] }, require('./publish')); + console.register('unpublish', 'Moves a published post from _posts to _drafts folder.', { + usage: '[layout] ', + arguments: [ + {name: 'layout', desc: 'Post layout. Use post, page, draft or whatever you want.'}, + {name: 'filename', desc: 'Post filename. "hello-world" for example.'} + ] + }, require('./publish')); + console.register('render', 'Render files with renderer plugins.', { init: true, desc: 'Render files with renderer plugins (e.g. Markdown) and save them at the specified path.', From 5480574a93c6c73eff99bef95f857948fe3e6f00 Mon Sep 17 00:00:00 2001 From: Baoshuo Ren Date: Thu, 1 Sep 2022 18:47:07 +0800 Subject: [PATCH 3/5] Apply suggestions from code review Co-authored-by: yoshinorin --- lib/plugins/console/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/console/index.js b/lib/plugins/console/index.js index fb466a1b..40960cac 100644 --- a/lib/plugins/console/index.js +++ b/lib/plugins/console/index.js @@ -73,7 +73,7 @@ module.exports = function(ctx) { {name: 'layout', desc: 'Post layout. Use post, page, draft or whatever you want.'}, {name: 'filename', desc: 'Post filename. "hello-world" for example.'} ] - }, require('./publish')); + }, require('./unpublish')); console.register('render', 'Render files with renderer plugins.', { init: true, From 1799ed954c44ac5144b39721d305f19f67e59a65 Mon Sep 17 00:00:00 2001 From: awongCM Date: Mon, 5 Dec 2022 22:12:06 +1100 Subject: [PATCH 4/5] updated as per review --- lib/plugins/console/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/console/index.js b/lib/plugins/console/index.js index fb466a1b..40960cac 100644 --- a/lib/plugins/console/index.js +++ b/lib/plugins/console/index.js @@ -73,7 +73,7 @@ module.exports = function(ctx) { {name: 'layout', desc: 'Post layout. Use post, page, draft or whatever you want.'}, {name: 'filename', desc: 'Post filename. "hello-world" for example.'} ] - }, require('./publish')); + }, require('./unpublish')); console.register('render', 'Render files with renderer plugins.', { init: true, From f63505547ee02e1f31eb9d0f5493c80f43625d8e Mon Sep 17 00:00:00 2001 From: awongCM Date: Mon, 29 Apr 2024 23:43:36 +1000 Subject: [PATCH 5/5] fixed failing test suite --- lib/plugins/console/unpublish.js | 2 +- test/scripts/console/unpublish.js | 97 ++++++++++++++++++------------- 2 files changed, 56 insertions(+), 43 deletions(-) diff --git a/lib/plugins/console/unpublish.js b/lib/plugins/console/unpublish.js index 2fdce813..4cb8fd8c 100644 --- a/lib/plugins/console/unpublish.js +++ b/lib/plugins/console/unpublish.js @@ -11,7 +11,7 @@ function unPublishConsole(args) { return this.post.unpublish({ slug: args._.pop(), - layout: args._.length ? args._[0] : this.config.default_layout + layout: 'draft' // assumes only published posts to be reverteds }, args.r || args.replace).then(post => { this.log.info('Unpublished: %s', magenta(tildify(post.path))); }); diff --git a/test/scripts/console/unpublish.js b/test/scripts/console/unpublish.js index 433be4e7..7145edbf 100644 --- a/test/scripts/console/unpublish.js +++ b/test/scripts/console/unpublish.js @@ -8,8 +8,10 @@ const { useFakeTimers, spy } = require('sinon'); describe('unpublish', () => { const Hexo = require('../../../lib/hexo'); - const hexo = new Hexo(join(__dirname, 'unpublish_test'), {silent: true}); - const unpublish = require('../../../lib/plugins/console/unpublish').bind(hexo); + const hexo = new Hexo(join(__dirname, 'unpublish_test'), { silent: true }); + const unpublish = require('../../../lib/plugins/console/unpublish').bind( + hexo + ); const post = hexo.post; const now = Date.now(); let clock; @@ -19,19 +21,16 @@ describe('unpublish', () => { await mkdirs(hexo.base_dir); await hexo.init(); - await hexo.scaffold.set('post', [ - '---', - 'title: {{ title }}', - 'date: {{ date }}', - 'tags:', - '---' - ].join('\n')); - await hexo.scaffold.set('draft', [ - '---', - 'title: {{ title }}', - 'tags:', - '---' - ].join('\n')); + await hexo.scaffold.set( + 'post', + ['---', 'title: {{ title }}', 'date: {{ date }}', 'tags:', '---'].join( + '\n' + ) + ); + await hexo.scaffold.set( + 'draft', + ['---', 'title: {{ title }}', 'tags:', '---'].join('\n') + ); }); after(() => { @@ -39,22 +38,20 @@ describe('unpublish', () => { return rmdir(hexo.base_dir); }); - beforeEach(() => post.create({ - title: 'Hello World', - layout: 'post', - date: moment(now).format('YYYY-MM-DD HH:mm:ss') - })); + beforeEach(() => + post.create({ + title: 'Hello World', + layout: 'post', + date: moment(now).format('YYYY-MM-DD HH:mm:ss') + }) + ); it('slug', async () => { const postPath = join(hexo.source_dir, '_posts', 'Hello-World.md'); const path = join(hexo.source_dir, '_drafts', 'Hello-World.md'); - const content = [ - '---', - 'title: Hello World', - 'tags:', - '---' - ].join('\n') + '\n'; + const content + = ['---', 'title: Hello World', 'tags:', '---'].join('\n') + '\n'; await unpublish({ _: ['Hello-World'] @@ -70,11 +67,13 @@ describe('unpublish', () => { }); it('no args', async () => { - const hexo = new Hexo(join(__dirname, 'unpublish_test'), {silent: true}); + const hexo = new Hexo(join(__dirname, 'unpublish_test'), { silent: true }); hexo.call = spy(); - const unpublish = require('../../../lib/plugins/console/unpublish').bind(hexo); + const unpublish = require('../../../lib/plugins/console/unpublish').bind( + hexo + ); - await unpublish({_: []}); + await unpublish({ _: [] }); hexo.call.calledOnce.should.be.true; hexo.call.args[0][0].should.eql('help'); @@ -85,13 +84,14 @@ describe('unpublish', () => { const path = join(hexo.source_dir, '_posts', 'Hello-World.md'); const date = moment(now); - const content = [ - '---', - 'title: Hello World', - 'date: ' + date.format('YYYY-MM-DD HH:mm:ss'), - 'tags:', - '---' - ].join('\n') + '\n'; + const content + = [ + '---', + 'title: Hello World', + 'date: ' + date.format('YYYY-MM-DD HH:mm:ss'), + 'tags:', + '---' + ].join('\n') + '\n'; await unpublish({ _: ['photo', 'Hello-World'] @@ -103,10 +103,15 @@ describe('unpublish', () => { }); it('rename if target existed', async () => { - const path = join(hexo.source_dir, '_posts', 'Hello-World.md'); + const path = join(hexo.source_dir, '_drafts', 'Hello-World-1.md'); + const publish = require('../../../lib/plugins/console/publish').bind(hexo); await post.create({ - title: 'Hello World' + title: 'Hello World', + layout: 'draft' + }); + await publish({ + _: ['Hello-World'] }); await unpublish({ _: ['Hello-World'] @@ -117,21 +122,29 @@ describe('unpublish', () => { await Promise.all([ unlink(path), - unlink(join(hexo.source_dir, '_posts', 'Hello-World.md')) + unlink(join(hexo.source_dir, '_drafts', 'Hello-World.md')) ]); }); it('replace existing target', async () => { - const path = join(hexo.source_dir, '_posts', 'Hello-World.md'); + const path = join(hexo.source_dir, '_drafts', 'Hello-World.md'); + const publish = require('../../../lib/plugins/console/publish').bind(hexo); await post.create({ - title: 'Hello World' + title: 'Hello World', + layout: 'draft' + }); + await publish({ + _: ['Hello-World'] }); await unpublish({ _: ['Hello-World'], replace: true }); - const exist = await exists(join(hexo.source_dir, '_drafts', 'Hello-World-1.md')); + + const exist = await exists( + join(hexo.source_dir, '_drafts', 'Hello-World-1.md') + ); exist.should.be.false; await unlink(path);