Skip to content

Commit

Permalink
support for multiple config files (#2210) (#2211)
Browse files Browse the repository at this point in the history
* multiconfig working

* rename and fix warning

* switched to hexo-log

* passing context to multiconfig for slilencing capability

* added tests

* formatting and undoing random change at line 328

* fixed logging output testing

* style error fixes... ✨
  • Loading branch information
pirtleshell authored and NoahDragon committed Nov 4, 2016
1 parent cdb5f95 commit 4676ca2
Show file tree
Hide file tree
Showing 5 changed files with 290 additions and 1 deletion.
3 changes: 2 additions & 1 deletion lib/hexo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ function Hexo(base, args) {
init: false
};

this.config_path = args.config ? pathFn.resolve(base, args.config)
var multiConfigPath = require('./multi_config_path')(this);
this.config_path = args.config ? multiConfigPath(base, args.config)
: pathFn.join(base, '_config.yml');

this.extend = {
Expand Down
74 changes: 74 additions & 0 deletions lib/hexo/multi_config_path.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
'use strict';

var pathFn = require('path');
var fs = require('hexo-fs');
var deepAssign = require('deep-assign');
var yml = require('js-yaml');

module.exports = function(ctx) {
return function multiConfigPath(base, configPaths) {
var log = ctx.log;

var defaultPath = pathFn.join(base, '_config.yml');
var paths;

if (!configPaths) {
log.w('No config file entered.');
return pathFn.join(base, '_config.yml');
}

// determine if comma or space separated
if (configPaths.indexOf(',') > -1) {
paths = configPaths.replace(' ', '').split(',');

} else {
// only one config
if (!fs.existsSync(pathFn.join(base, configPaths))) {
log.w('Config file ' + configPaths + ' not found, using default.');
return defaultPath;
}

return pathFn.resolve(base, configPaths);
}

var numPaths = paths.length;

// combine files
var combinedConfig = {};
var count = 0;
for (var i = 0; i < numPaths; i++) {
if (!fs.existsSync(pathFn.join(base, paths[i]))) {
log.w('Config file ' + paths[i] + ' not found.');
continue;
}

// files read synchronously to ensure proper overwrite order
var file = fs.readFileSync(pathFn.join(base, paths[i]));
var ext = pathFn.extname(paths[i]).toLowerCase();

if (ext === '.yml') {
deepAssign(combinedConfig, yml.safeLoad(file));
count++;
} else if (ext === '.json') {
deepAssign(combinedConfig, yml.safeLoad(file, {json: true}));
count++;
} else {
log.w('Config file ' + paths[i] +
' not supported type.');
}
}

if (count === 0) {
log.e('No config files found. Using _config.yml.');
return defaultPath;
}

log.i('Config based on', count, 'files');

var outputPath = pathFn.join(base, '_multiconfig.yml');
fs.writeFileSync(outputPath, yml.dump(combinedConfig));

// write file and return path
return outputPath;
};
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"bluebird": "^3.4.0",
"chalk": "^1.1.3",
"cheerio": "^0.20.0",
"deep-assign": "^2.0.0",
"hexo-cli": "^1.0.2",
"hexo-front-matter": "^0.2.2",
"hexo-fs": "^0.1.5",
Expand Down
1 change: 1 addition & 0 deletions test/scripts/hexo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ describe('Core', function() {
require('./load_database');
require('./load_plugins');
require('./locals');
require('./multi_config_path');
require('./post');
require('./render');
require('./router');
Expand Down
212 changes: 212 additions & 0 deletions test/scripts/hexo/multi_config_path.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
'use strict';

var pathFn = require('path');
var should = require('chai').should(); // eslint-disable-line
var fs = require('hexo-fs');
var yml = require('js-yaml');

describe('config flag handling', function() {
var Hexo = require('../../../lib/hexo');
var hexo = new Hexo(pathFn.join(__dirname, 'test_dir'));

var mcp = require('../../../lib/hexo/multi_config_path')(hexo);
var base = hexo.base_dir;

function ConsoleReader() {
this.reader = [];
this.i = function() {
var type = 'info';
var message = '';
for (var i = 0; i < arguments.length;) {
message += arguments[i];
if (++i < arguments.length) {
message += ' ';
}
}

this.reader.push({
type: type,
msg: message
});
}.bind(this);

this.w = function() {
var type = 'warning';
var message = '';
for (var i = 0; i < arguments.length;) {
message += arguments[i];
if (++i < arguments.length) {
message += ' ';
}
}

this.reader.push({
type: type,
msg: message
});
}.bind(this);

this.e = function() {
var type = 'error';
var message = '';
for (var i = 0; i < arguments.length;) {
message += arguments[i];
if (++i < arguments.length) {
message += ' ';
}
}

this.reader.push({
type: type,
msg: message
});
}.bind(this);
}

hexo.log = new ConsoleReader();

var testYaml1 = [
'author: foo',
'type: dinosaur',
'favorites:',
' food: sushi',
' color: purple'
].join('\n');

var testYaml2 = [
'author: bar',
'favorites:',
' food: candy',
' ice_cream: chocolate'
].join('\n');

var testJson1 = [
'{',
'"author": "dinosaur",',
'"type": "elephant",',
'"favorites": {"food": "burgers"}',
'}'
].join('\n');

var testJson2 = [
'{',
'"author": "waldo",',
'"favorites": {',
' "food": "ice cream",',
' "ice_cream": "strawberry"',
' }',
'}'
].join('\n');

before(function() {
fs.writeFileSync(base + 'test1.yml', testYaml1);
fs.writeFileSync(base + 'test2.yml', testYaml2);
fs.writeFileSync(base + 'test1.json', testJson1);
fs.writeFileSync(base + 'test2.json', testJson2);
return;
});

afterEach(function() {
hexo.log.reader = [];
return;
});

after(function() {
return fs.rmdir(hexo.base_dir);
});

it('no file', function() {
mcp(base).should.equal(base + '_config.yml');
hexo.log.reader[0].type.should.eql('warning');
hexo.log.reader[0].msg.should.eql('No config file entered.');
});

it('1 file', function() {
mcp(base, 'test1.yml').should.eql(
pathFn.resolve(base + 'test1.yml'));

mcp(base, 'test1.json').should.eql(
pathFn.resolve(base + 'test1.json'));
});

it('1 not found file warning', function() {
var notFile = 'not_a_file.json';

mcp(base, notFile).should.eql(pathFn.join(base, '_config.yml'));
hexo.log.reader[0].type.should.eql('warning');
hexo.log.reader[0].msg.should.eql('Config file ' + notFile +
' not found, using default.');
});

it('combined config output', function() {
var combinedPath = pathFn.join(base, '_multiconfig.yml');

mcp(base, 'test1.yml').should.not.eql(combinedPath);
mcp(base, 'test1.yml,test2.yml').should.eql(combinedPath);
mcp(base, 'test1.yml,test1.json').should.eql(combinedPath);
mcp(base, 'test1.json,test2.json').should.eql(combinedPath);
mcp(base, 'notafile.yml,test1.json').should.eql(combinedPath);

hexo.log.reader[0].type.should.eql('info');
hexo.log.reader[0].msg.should.eql('Config based on 2 files');
hexo.log.reader[3].type.should.eql('warning');
hexo.log.reader[3].msg.should.eql('Config file notafile.yml not found.');
hexo.log.reader[4].type.should.eql('info');
hexo.log.reader[4].msg.should.eql('Config based on 1 files');
// because who cares about grammar anyway?

mcp(base, 'notafile.yml,alsonotafile.json').should.not.eql(combinedPath);
hexo.log.reader[7].type.should.eql('error');
hexo.log.reader[7].msg.should.eql('No config files found.' +
' Using _config.yml.');
});

it('2 YAML overwrite', function() {
var configFile = mcp(base, 'test1.yml,test2.yml');
var config = fs.readFileSync(configFile);
config = yml.safeLoad(config);

config.author.should.eql('bar');
config.favorites.food.should.eql('candy');
config.type.should.eql('dinosaur');

config = fs.readFileSync(mcp(base, 'test2.yml,test1.yml'));
config = yml.safeLoad(config);

config.author.should.eql('foo');
config.favorites.food.should.eql('sushi');
config.type.should.eql('dinosaur');
});

it('2 JSON overwrite', function() {
var config = fs.readFileSync(mcp(base, 'test1.json,test2.json'));
config = yml.safeLoad(config);

config.author.should.eql('waldo');
config.favorites.food.should.eql('ice cream');
config.type.should.eql('elephant');

config = fs.readFileSync(mcp(base, 'test2.json,test1.json'));
config = yml.safeLoad(config);

config.author.should.eql('dinosaur');
config.favorites.food.should.eql('burgers');
config.type.should.eql('elephant');
});

it('JSON \& YAML overwrite', function() {
var config = fs.readFileSync(mcp(base, 'test1.yml,test1.json'));
config = yml.safeLoad(config);

config.author.should.eql('dinosaur');
config.favorites.food.should.eql('burgers');
config.type.should.eql('elephant');

config = fs.readFileSync(mcp(base, 'test1.json,test1.yml'));
config = yml.safeLoad(config);

config.author.should.eql('foo');
config.favorites.food.should.eql('sushi');
config.type.should.eql('dinosaur');
});
});

0 comments on commit 4676ca2

Please sign in to comment.