From 45d58cd8cf82247cbc1c72ee46d21217ce444ea8 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Fri, 26 Jun 2020 18:13:53 +0800 Subject: [PATCH 1/5] feat(config): validate config after load_config --- lib/hexo/index.js | 1 + lib/hexo/validate_config.js | 33 +++++++++ test/scripts/hexo/index.js | 1 + test/scripts/hexo/validate_config.js | 102 +++++++++++++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 lib/hexo/validate_config.js create mode 100644 test/scripts/hexo/validate_config.js diff --git a/lib/hexo/index.js b/lib/hexo/index.js index e6d92ec33b..9068048e78 100644 --- a/lib/hexo/index.js +++ b/lib/hexo/index.js @@ -229,6 +229,7 @@ class Hexo extends EventEmitter { return Promise.each([ 'update_package', // Update package.json 'load_config', // Load config + 'validate_config', // Validate 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(() => { diff --git a/lib/hexo/validate_config.js b/lib/hexo/validate_config.js new file mode 100644 index 0000000000..26d6182063 --- /dev/null +++ b/lib/hexo/validate_config.js @@ -0,0 +1,33 @@ +'use strict'; + +module.exports = ctx => { + const { config, log } = ctx; + + log.info('Validating config'); + + // Validation for config.url && config.root + if (typeof config.url !== 'string') { + throw new TypeError('_config.yml validation failed, "url" should be string!'); + } + if (config.url.trim().length <= 0) { + throw new TypeError('_config.yml validation failed, "url" should not be empty!'); + } + + if (typeof config.root !== 'string') { + throw new TypeError('_config.yml validation failed, "root" should be string!'); + } + if (config.root.trim().length <= 0) { + throw new TypeError('_config.yml validation failed, "root" should not be empty!'); + } + + // Soft deprecate use_date_for_updated + if (typeof config.use_date_for_updated === 'boolean') { + log.warn('_config.yml validation: "use_date_for_updated" is deprecated, please use "updated_option" instead. See https://hexo.io/docs/configuration for more details.'); + } + + // Soft deprecate external_link Boolean + if (typeof config.external_link === 'boolean') { + log.warn('_config.yml validation: "external_link" with a Boolean value is deprecated. See https://hexo.io/docs/configuration for more details.'); + } +}; + diff --git a/test/scripts/hexo/index.js b/test/scripts/hexo/index.js index e654455d9b..94c443d65a 100644 --- a/test/scripts/hexo/index.js +++ b/test/scripts/hexo/index.js @@ -3,6 +3,7 @@ describe('Core', () => { require('./hexo'); require('./load_config'); + require('./validate_config'); require('./load_database'); require('./load_plugins'); require('./load_theme_config'); diff --git a/test/scripts/hexo/validate_config.js b/test/scripts/hexo/validate_config.js new file mode 100644 index 0000000000..03b072ad96 --- /dev/null +++ b/test/scripts/hexo/validate_config.js @@ -0,0 +1,102 @@ +'use strict'; + +const { spy } = require('sinon'); + +describe('Validate config', () => { + const Hexo = require('../../../lib/hexo'); + const hexo = new Hexo(); + const validateConfig = require('../../../lib/hexo/validate_config'); + const defaultConfig = require('../../../lib/hexo/default_config'); + let logSpy; + + beforeEach(() => { + logSpy = spy(); + hexo.config = JSON.parse(JSON.stringify(defaultConfig)); + hexo.log.warn = logSpy; + hexo.log.info = spy(); + }); + + it('config.url - undefined', () => { + delete hexo.config.url; + + try { + validateConfig(hexo); + } catch (e) { + e.name.should.eql('TypeError'); + e.message.should.eql('_config.yml validation failed, "url" should be string!'); + } + }); + + it('config.url - wrong type', () => { + hexo.config.url = true; + + try { + validateConfig(hexo); + } catch (e) { + e.name.should.eql('TypeError'); + e.message.should.eql('_config.yml validation failed, "url" should be string!'); + } + }); + + it('config.url - empty', () => { + hexo.config.url = ' '; + + try { + validateConfig(hexo); + } catch (e) { + e.name.should.eql('TypeError'); + e.message.should.eql('_config.yml validation failed, "url" should not be empty!'); + } + }); + + it('config.root - undefined', () => { + delete hexo.config.root; + + try { + validateConfig(hexo); + } catch (e) { + e.name.should.eql('TypeError'); + e.message.should.eql('_config.yml validation failed, "root" should be string!'); + } + }); + + it('config.root - wrong type', () => { + hexo.config.root = true; + + try { + validateConfig(hexo); + } catch (e) { + e.name.should.eql('TypeError'); + e.message.should.eql('_config.yml validation failed, "root" should be string!'); + } + }); + + it('config.root - empty', () => { + hexo.config.root = ' '; + + try { + validateConfig(hexo); + } catch (e) { + e.name.should.eql('TypeError'); + e.message.should.eql('_config.yml validation failed, "root" should not be empty!'); + } + }); + + it('config.use_date_for_updated - depreacte', () => { + hexo.config.use_date_for_updated = true; + + validateConfig(hexo); + + logSpy.calledOnce.should.be.true; + logSpy.calledWith('_config.yml validation: "use_date_for_updated" is deprecated, please use "updated_option" instead. See https://hexo.io/docs/configuration for more details.').should.be.true; + }); + + it('config.external_link - depreacte Boolean value', () => { + hexo.config.external_link = false; + + validateConfig(hexo); + + logSpy.calledOnce.should.be.true; + logSpy.calledWith('_config.yml validation: "external_link" with a Boolean value is deprecated. See https://hexo.io/docs/configuration for more details.').should.be.true; + }); +}); From e47c903515142d3ad77743c3c2eafe144af8d500 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Sat, 27 Jun 2020 12:49:16 +0800 Subject: [PATCH 2/5] feat(config): update validation message --- lib/hexo/validate_config.js | 12 ++++++------ test/scripts/hexo/validate_config.js | 16 ++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/hexo/validate_config.js b/lib/hexo/validate_config.js index 26d6182063..9aa51e725c 100644 --- a/lib/hexo/validate_config.js +++ b/lib/hexo/validate_config.js @@ -7,27 +7,27 @@ module.exports = ctx => { // Validation for config.url && config.root if (typeof config.url !== 'string') { - throw new TypeError('_config.yml validation failed, "url" should be string!'); + throw new TypeError('Invalid config detected: "url" should be string!'); } if (config.url.trim().length <= 0) { - throw new TypeError('_config.yml validation failed, "url" should not be empty!'); + throw new TypeError('Invalid config detected: "url" should not be empty!'); } if (typeof config.root !== 'string') { - throw new TypeError('_config.yml validation failed, "root" should be string!'); + throw new TypeError('Invalid config detected: "root" should be string!'); } if (config.root.trim().length <= 0) { - throw new TypeError('_config.yml validation failed, "root" should not be empty!'); + throw new TypeError('Invalid config detected: "root" should not be empty!'); } // Soft deprecate use_date_for_updated if (typeof config.use_date_for_updated === 'boolean') { - log.warn('_config.yml validation: "use_date_for_updated" is deprecated, please use "updated_option" instead. See https://hexo.io/docs/configuration for more details.'); + log.warn('Deprecated config detected: "use_date_for_updated" is deprecated, please use "updated_option" instead. See https://hexo.io/docs/configuration for more details.'); } // Soft deprecate external_link Boolean if (typeof config.external_link === 'boolean') { - log.warn('_config.yml validation: "external_link" with a Boolean value is deprecated. See https://hexo.io/docs/configuration for more details.'); + log.warn('Deprecated config detected: "external_link" with a Boolean value is deprecated. See https://hexo.io/docs/configuration for more details.'); } }; diff --git a/test/scripts/hexo/validate_config.js b/test/scripts/hexo/validate_config.js index 03b072ad96..e5dcf76ab8 100644 --- a/test/scripts/hexo/validate_config.js +++ b/test/scripts/hexo/validate_config.js @@ -23,7 +23,7 @@ describe('Validate config', () => { validateConfig(hexo); } catch (e) { e.name.should.eql('TypeError'); - e.message.should.eql('_config.yml validation failed, "url" should be string!'); + e.message.should.eql('Invalid config detected: "url" should be string!'); } }); @@ -34,7 +34,7 @@ describe('Validate config', () => { validateConfig(hexo); } catch (e) { e.name.should.eql('TypeError'); - e.message.should.eql('_config.yml validation failed, "url" should be string!'); + e.message.should.eql('Invalid config detected: "url" should be string!'); } }); @@ -45,7 +45,7 @@ describe('Validate config', () => { validateConfig(hexo); } catch (e) { e.name.should.eql('TypeError'); - e.message.should.eql('_config.yml validation failed, "url" should not be empty!'); + e.message.should.eql('Invalid config detected: "url" should not be empty!'); } }); @@ -56,7 +56,7 @@ describe('Validate config', () => { validateConfig(hexo); } catch (e) { e.name.should.eql('TypeError'); - e.message.should.eql('_config.yml validation failed, "root" should be string!'); + e.message.should.eql('Invalid config detected: "root" should be string!'); } }); @@ -67,7 +67,7 @@ describe('Validate config', () => { validateConfig(hexo); } catch (e) { e.name.should.eql('TypeError'); - e.message.should.eql('_config.yml validation failed, "root" should be string!'); + e.message.should.eql('Invalid config detected: "root" should be string!'); } }); @@ -78,7 +78,7 @@ describe('Validate config', () => { validateConfig(hexo); } catch (e) { e.name.should.eql('TypeError'); - e.message.should.eql('_config.yml validation failed, "root" should not be empty!'); + e.message.should.eql('Invalid config detected: "root" should not be empty!'); } }); @@ -88,7 +88,7 @@ describe('Validate config', () => { validateConfig(hexo); logSpy.calledOnce.should.be.true; - logSpy.calledWith('_config.yml validation: "use_date_for_updated" is deprecated, please use "updated_option" instead. See https://hexo.io/docs/configuration for more details.').should.be.true; + logSpy.calledWith('Deprecated config detected: "use_date_for_updated" is deprecated, please use "updated_option" instead. See https://hexo.io/docs/configuration for more details.').should.be.true; }); it('config.external_link - depreacte Boolean value', () => { @@ -97,6 +97,6 @@ describe('Validate config', () => { validateConfig(hexo); logSpy.calledOnce.should.be.true; - logSpy.calledWith('_config.yml validation: "external_link" with a Boolean value is deprecated. See https://hexo.io/docs/configuration for more details.').should.be.true; + logSpy.calledWith('Deprecated config detected: "external_link" with a Boolean value is deprecated. See https://hexo.io/docs/configuration for more details.').should.be.true; }); }); From f11b93f98a70f423cfddb536da16436a03c3b982 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Tue, 30 Jun 2020 11:16:15 +0800 Subject: [PATCH 3/5] refactor(config): validate config before modify it --- lib/hexo/index.js | 1 - lib/hexo/load_config.js | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/hexo/index.js b/lib/hexo/index.js index 9068048e78..e6d92ec33b 100644 --- a/lib/hexo/index.js +++ b/lib/hexo/index.js @@ -229,7 +229,6 @@ class Hexo extends EventEmitter { return Promise.each([ 'update_package', // Update package.json 'load_config', // Load config - 'validate_config', // Validate 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(() => { diff --git a/lib/hexo/load_config.js b/lib/hexo/load_config.js index 6d0d974a43..86778a4dc2 100644 --- a/lib/hexo/load_config.js +++ b/lib/hexo/load_config.js @@ -7,6 +7,7 @@ const Source = require('./source'); const { exists, readdir } = require('hexo-fs'); const { magenta } = require('chalk'); const { deepMerge } = require('hexo-util'); +const validateConfig = require('./validate_config'); module.exports = async ctx => { if (!ctx.env.init) return; @@ -26,6 +27,8 @@ module.exports = async ctx => { ctx.config = deepMerge(ctx.config, config); config = ctx.config; + validateConfig(ctx); + ctx.config_path = configPath; config.root = config.root.replace(/\/*$/, '/'); From c2d34280969fc774e62c3c8894732be195f5912b Mon Sep 17 00:00:00 2001 From: SukkaW Date: Tue, 30 Jun 2020 22:56:12 +0800 Subject: [PATCH 4/5] test(config): add should#fail for test cases --- test/scripts/hexo/validate_config.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/scripts/hexo/validate_config.js b/test/scripts/hexo/validate_config.js index e5dcf76ab8..78781eca96 100644 --- a/test/scripts/hexo/validate_config.js +++ b/test/scripts/hexo/validate_config.js @@ -21,6 +21,7 @@ describe('Validate config', () => { try { validateConfig(hexo); + should.fail(); } catch (e) { e.name.should.eql('TypeError'); e.message.should.eql('Invalid config detected: "url" should be string!'); @@ -32,6 +33,7 @@ describe('Validate config', () => { try { validateConfig(hexo); + should.fail(); } catch (e) { e.name.should.eql('TypeError'); e.message.should.eql('Invalid config detected: "url" should be string!'); @@ -43,6 +45,7 @@ describe('Validate config', () => { try { validateConfig(hexo); + should.fail(); } catch (e) { e.name.should.eql('TypeError'); e.message.should.eql('Invalid config detected: "url" should not be empty!'); @@ -54,6 +57,7 @@ describe('Validate config', () => { try { validateConfig(hexo); + should.fail(); } catch (e) { e.name.should.eql('TypeError'); e.message.should.eql('Invalid config detected: "root" should be string!'); @@ -65,6 +69,7 @@ describe('Validate config', () => { try { validateConfig(hexo); + should.fail(); } catch (e) { e.name.should.eql('TypeError'); e.message.should.eql('Invalid config detected: "root" should be string!'); @@ -76,6 +81,7 @@ describe('Validate config', () => { try { validateConfig(hexo); + should.fail(); } catch (e) { e.name.should.eql('TypeError'); e.message.should.eql('Invalid config detected: "root" should not be empty!'); From 3889f39da4bc6a1e2c741d842bfdc5d629863f92 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Thu, 2 Jul 2020 15:35:28 +0800 Subject: [PATCH 5/5] refactor(vlidata_config): update error message --- lib/hexo/validate_config.js | 4 ++-- test/scripts/hexo/validate_config.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/hexo/validate_config.js b/lib/hexo/validate_config.js index 9aa51e725c..67cac859f7 100644 --- a/lib/hexo/validate_config.js +++ b/lib/hexo/validate_config.js @@ -7,14 +7,14 @@ module.exports = ctx => { // Validation for config.url && config.root if (typeof config.url !== 'string') { - throw new TypeError('Invalid config detected: "url" should be string!'); + throw new TypeError(`Invalid config detected: "url" should be string, not ${typeof config.url}!`); } if (config.url.trim().length <= 0) { throw new TypeError('Invalid config detected: "url" should not be empty!'); } if (typeof config.root !== 'string') { - throw new TypeError('Invalid config detected: "root" should be string!'); + throw new TypeError(`Invalid config detected: "root" should be string, not ${typeof config.root}!`); } if (config.root.trim().length <= 0) { throw new TypeError('Invalid config detected: "root" should not be empty!'); diff --git a/test/scripts/hexo/validate_config.js b/test/scripts/hexo/validate_config.js index 78781eca96..c3813ead63 100644 --- a/test/scripts/hexo/validate_config.js +++ b/test/scripts/hexo/validate_config.js @@ -24,7 +24,7 @@ describe('Validate config', () => { should.fail(); } catch (e) { e.name.should.eql('TypeError'); - e.message.should.eql('Invalid config detected: "url" should be string!'); + e.message.should.eql('Invalid config detected: "url" should be string, not undefined!'); } }); @@ -36,7 +36,7 @@ describe('Validate config', () => { should.fail(); } catch (e) { e.name.should.eql('TypeError'); - e.message.should.eql('Invalid config detected: "url" should be string!'); + e.message.should.eql('Invalid config detected: "url" should be string, not boolean!'); } }); @@ -60,7 +60,7 @@ describe('Validate config', () => { should.fail(); } catch (e) { e.name.should.eql('TypeError'); - e.message.should.eql('Invalid config detected: "root" should be string!'); + e.message.should.eql('Invalid config detected: "root" should be string, not undefined!'); } }); @@ -72,7 +72,7 @@ describe('Validate config', () => { should.fail(); } catch (e) { e.name.should.eql('TypeError'); - e.message.should.eql('Invalid config detected: "root" should be string!'); + e.message.should.eql('Invalid config detected: "root" should be string, not boolean!'); } });