Skip to content

Commit

Permalink
Merge pull request #6 from license2e/add-merge
Browse files Browse the repository at this point in the history
Add $merge feature
  • Loading branch information
license2e authored Oct 20, 2016
2 parents 6fbdf6e + 95876e0 commit e9266f0
Show file tree
Hide file tree
Showing 12 changed files with 225 additions and 133 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# refs

Compile YAML, JSON or INI config files together through file path references using `$ref` setting
Compile and merge YAML, JSON or INI config files together through file path references

### Install:

Expand Down
45 changes: 7 additions & 38 deletions lib/processor-ini.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,19 @@
'use strict';

const Promise = require('bluebird');
const path = require('path');
const fs = require('fs');
const ini = require('ini');
const extract = require('./utils/extract');
const transform = require('./utils/transform');

function process(filePath, key) {
return new Promise((resolve, reject) => {
let fileData = '';
let data = {};
try {
fileData = fs.readFileSync(filePath, 'utf-8');
data = ini.parse(fileData);
} catch (err) {
let returnErr = err;
if (fileData === '') {
returnErr = 'Empty file, nothing to process.';
return extract(filePath, ini.parse)
.then((dataString) => {
if (dataString.indexOf('$merge') !== -1) {
return Promise.reject('INI config does not support $merge settings.');
}
return reject(returnErr);
}
let dataString = JSON.stringify(data);
const refMatches = dataString.match(/{"\$ref":"(.*?)"}/g);
const baseDir = path.dirname(filePath);
if (refMatches && refMatches.length > 0) {
const iniFileList = [];
refMatches.forEach((matchKey) => {
const iniFile = matchKey.match(/{"\$ref":"(.*)"}/)[1];
const refFilePath = path.resolve(`${baseDir}/${iniFile}`);
iniFileList.push(process(refFilePath, matchKey));
});
return Promise.all(iniFileList)
.then((results) => {
results.forEach((result) => {
dataString = dataString.replace(result.key, result.dataString);
});
resolve({
dataString,
key,
});
});
}
return resolve({
dataString,
key,
return transform(dataString, key, filePath, process);
});
});
}

function write(outputFile, compiled) {
Expand Down
45 changes: 5 additions & 40 deletions lib/processor-json.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,14 @@
'use strict';

const Promise = require('bluebird');
const path = require('path');
// const path = require('path');
const fs = require('fs');
const extract = require('./utils/extract');
const transform = require('./utils/transform');

function process(filePath, key) {
return new Promise((resolve, reject) => {
let dataString = '';
try {
dataString = fs.readFileSync(filePath, 'utf-8');
const parsedData = JSON.parse(dataString);
dataString = JSON.stringify(parsedData);
} catch (err) {
let returnErr = err;
if (dataString === '') {
returnErr = 'Empty file, nothing to process.';
}
return reject(returnErr);
}

const refMatches = dataString.match(/{"\$ref":"(.*?)"}/g);
const baseDir = path.dirname(filePath);
if (refMatches && refMatches.length > 0) {
const jsonFileList = [];
refMatches.forEach((matchKey) => {
const jsonFile = matchKey.match(/{"\$ref":"(.*)"}/)[1];
const refFilePath = path.resolve(`${baseDir}/${jsonFile}`);
jsonFileList.push(process(refFilePath, matchKey));
});
return Promise.all(jsonFileList)
.then((results) => {
results.forEach((result) => {
dataString = dataString.replace(result.key, result.dataString);
});
resolve({
dataString,
key,
});
});
}
return resolve({
dataString,
key,
});
});
return extract(filePath, JSON.parse)
.then(dataString => transform(dataString, key, filePath, process));
}

function write(outputFile, compiled) {
Expand Down
46 changes: 9 additions & 37 deletions lib/processor-yaml.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,22 @@

const yaml = require('node-yaml');
const Promise = require('bluebird');
const path = require('path');
// const path = require('path');
const extract = require('./utils/extract');
const transform = require('./utils/transform');

const options = {
encoding: 'utf8',
schema: yaml.schema.defaultFull,
};

function parser(data) {
return yaml.parse(data, options);
}

function process(filePath, key) {
return new Promise((resolve, reject) => {
yaml.read(filePath, options, (err, data) => {
let returnErr = err;
if (!err && data === undefined) {
returnErr = 'Empty file, nothing to process.';
}
if (returnErr) {
return reject(returnErr);
}
let dataString = JSON.stringify(data);
const refMatches = dataString.match(/{"\$ref":"(.*?)"}/g);
const baseDir = path.dirname(filePath);
if (refMatches && refMatches.length > 0) {
const yamlFileList = [];
refMatches.forEach((matchKey) => {
const yamlFile = matchKey.match(/{"\$ref":"(.*)"}/)[1];
const refFilePath = path.resolve(`${baseDir}/${yamlFile}`);
yamlFileList.push(process(refFilePath, matchKey));
});
return Promise.all(yamlFileList)
.then((results) => {
results.forEach((result) => {
dataString = dataString.replace(result.key, result.dataString);
});
resolve({
dataString,
key,
});
});
}
return resolve({
dataString,
key,
});
});
});
return extract(filePath, parser)
.then(dataString => transform(dataString, key, filePath, process));
}

function write(outputFile, compiled) {
Expand Down
27 changes: 27 additions & 0 deletions lib/utils/extract.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use strict';

const Promise = require('bluebird');
const fs = require('fs');


function extract(filePath, parser) {
return new Promise((resolve, reject) => {
if (!filePath || filePath === undefined) {
return reject('Requires a file path to process.');
}
let fileData = '';
let data = {};
try {
fileData = fs.readFileSync(filePath, 'utf-8');
if (fileData === '') {
throw new Error('Empty file, nothing to process.');
}
data = parser(fileData);
} catch (err) {
return reject(err.message);
}
return resolve(JSON.stringify(data));
});
}

module.exports = extract;
66 changes: 66 additions & 0 deletions lib/utils/transform.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
'use strict';

const Promise = require('bluebird');
const path = require('path');

function transform(dataStr, key, filePath, process) {
return new Promise((resolve, reject) => {
let dataString = dataStr;
const refMatches = dataString.match(/{"\$ref":"(.*?)"}/g);
const baseDir = path.dirname(filePath);
if (refMatches && refMatches.length > 0) {
const refFileList = [];
refMatches.forEach((matchKey) => {
const refFile = matchKey.match(/{"\$ref":"(.*)"}/)[1];
const refFilePath = path.resolve(`${baseDir}/${refFile}`);
refFileList.push(process(refFilePath, matchKey));
});
return Promise.all(refFileList)
.then((results) => {
const mergeMatches = dataString.match(/{"\$merge":\[(.*?)\]}/g);
if (mergeMatches && mergeMatches.length > 0) {
mergeMatches.forEach((mergeMatchKey) => {
const localMatches = [];
const localRefMatches = mergeMatchKey.match(/{"\$ref":"(.*?)"}/g);
if (!localRefMatches || localRefMatches.length === 0) {
reject('Malformed merge setting, please check the input file.');
return;
}
localRefMatches.forEach((refMatchKey) => {
const j = results.length;
for (let i = 0; i < j; i += 1) {
const result = results[i];
if (result.key === refMatchKey) {
const end = (result.dataString.length - 2);
localMatches.push(result.dataString.substr(1, end));
break;
}
}
});
const localMatchesStr = `{${localMatches.join(',')}}`;
dataString = dataString.replace(mergeMatchKey, localMatchesStr);
return;
});
} else {
results.forEach((result) => {
dataString = dataString.replace(result.key, result.dataString);
});
}
return resolve({
dataString,
key,
});
});
}
const mergeMatches = dataString.match(/{"\$merge":\[(.*?)\]}/g);
if (mergeMatches && mergeMatches.length > 0) {
return reject('Malformed merge setting, please check the input file.');
}
return resolve({
dataString,
key,
});
});
}

module.exports = transform;
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "refs",
"version": "0.9.0",
"description": "Compile YAML, JSON or INI config files together through file path references using `$ref` setting",
"version": "0.9.1",
"description": "Compile and merge YAML, JSON or INI config files together through file path references",
"main": "index.js",
"preferGlobal": true,
"bin": {
Expand Down
3 changes: 3 additions & 0 deletions test/data/file-merge.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[test]
$merge[]=$ref:./file.yaml
$merge[]=$ref:./file-refs.yaml
4 changes: 4 additions & 0 deletions test/data/file-merge.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
test:
$merge:
- $ref: ./file.yaml
- $ref: ./file-refs.yaml
37 changes: 28 additions & 9 deletions test/test-lib-ini-processor.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const td = require('testdouble');
describe('INI Tests', () => {
const INI_FILE = '/tmp/file.ini';
const INI_REF_FILE = '/tmp/file-refs.ini';
const INI_MERGE_FILE = '/tmp/file-merge.ini';
const INI_FILE_WRITE = '/tmp/file-write.ini';

beforeEach(() => {});
Expand All @@ -24,6 +25,11 @@ describe('INI Tests', () => {
} catch (e) {
// suppress error
}
try {
fs.unlinkSync(INI_MERGE_FILE);
} catch (e) {
// suppress error
}
try {
fs.unlinkSync(INI_FILE_WRITE);
} catch (e) {
Expand All @@ -39,7 +45,7 @@ describe('INI Tests', () => {
done('Rejection failed.');
})
.catch((err) => {
should(err).be.eql('Empty file, nothing to process.');
should(err).be.eql('Requires a file path to process.');
done();
});
});
Expand All @@ -61,7 +67,7 @@ describe('INI Tests', () => {
});
});

it('process: should process the file with no refs', (done) => {
it('process: should process the file with no ref settings', (done) => {
const iniContent = fs.readFileSync(INI_FILE.replace('/tmp', `${__dirname}/data`), 'utf-8');
fs.writeFileSync(INI_FILE, iniContent, 'utf-8');
const iniProcessor = require('../lib/processor-ini');
Expand All @@ -79,7 +85,7 @@ describe('INI Tests', () => {
});
});

it('process: should process the file with refs', (done) => {
it('process: should process the file with ref settings', (done) => {
const iniContent = fs.readFileSync(INI_FILE.replace('/tmp', `${__dirname}/data`), 'utf-8');
const iniRefContent = fs.readFileSync(INI_REF_FILE.replace('/tmp', `${__dirname}/data`), 'utf-8');
fs.writeFileSync(INI_FILE, iniContent, 'utf-8');
Expand All @@ -99,6 +105,25 @@ describe('INI Tests', () => {
});
});

it('process: should throw error with merge settings', (done) => {
const iniContent = fs.readFileSync(INI_FILE.replace('/tmp', `${__dirname}/data`), 'utf-8');
const iniRefContent = fs.readFileSync(INI_REF_FILE.replace('/tmp', `${__dirname}/data`), 'utf-8');
const iniMergeContent = fs.readFileSync(INI_MERGE_FILE.replace('/tmp', `${__dirname}/data`), 'utf-8');
fs.writeFileSync(INI_FILE, iniContent, 'utf-8');
fs.writeFileSync(INI_REF_FILE, iniRefContent, 'utf-8');
fs.writeFileSync(INI_MERGE_FILE, iniMergeContent, 'utf-8');
const iniProcessor = require('../lib/processor-ini');

iniProcessor.process(INI_MERGE_FILE)
.then(() => {
done('Rejection failed.');
})
.catch((err) => {
should(err).be.eql('INI config does not support $merge settings.');
done();
});
});

it('write: should throw error on write', (done) => {
td.replace(fs, 'writeFile', (outputFile, compiled, options, cb) => cb('An error occurred.'));
const iniProcessor = require('../lib/processor-ini');
Expand Down Expand Up @@ -146,12 +171,6 @@ describe('INI Tests', () => {
});

it('dump: should dump to output file', (done) => {
// td.replace('node-ini', {
// dump: compiled => JSON.stringify(compiled),
// schema: {
// defaultFull: ini.schema.defaultFull,
// },
// });
const iniProcessor = require('../lib/processor-ini');

iniProcessor.dump({ test: true })
Expand Down
Loading

0 comments on commit e9266f0

Please sign in to comment.