refactor: use singleton config for execution

It will be easy to not put config as parameter each time
This commit is contained in:
Anton Shchekota 2021-05-06 19:27:48 +03:00 committed by Anton
parent 806defa191
commit 4b711da2b2
5 changed files with 172 additions and 148 deletions

View File

@ -1,5 +1,4 @@
const path = require('path');
const os = require('os');
const exec = require('child_process').exec;
const tmp = require('tmp');
const fs = require('fs-extra');

View File

@ -1,42 +1,46 @@
const path = require('path');
const _ = require('lodash');
const config = require('../../src/config');
const mergeConfig = require('../../src/merge_config');
test('bad config', async function() {
try {
await mergeConfig({ config: 'DOES-NOT-EXIST' });
} catch (err) {
expect(err).toBeTruthy();
}
});
describe('single config tests', function () {
beforeEach(function () {
config.reset();
});
test('right merging package configuration', async function() {
// Omit configuration from output, for simplicity
const nc = _.curryRight(_.omit, 2)([
'config',
'no-package',
'parseExtension',
'project-homepage',
'project-version',
'project-description'
]);
return mergeConfig({
config: path.join(__dirname, '../config_fixture/config.json'),
'no-package': true,
'project-name': 'cool Documentation'
})
.then(nc)
.then(res => {
expect(res).toEqual({
'project-name': 'cool Documentation',
foo: 'bar'
});
test('Should be failed on bad config', async function () {
try {
await mergeConfig({ config: 'DOES-NOT-EXIST' });
} catch (err) {
expect(err).toBeTruthy();
return;
}
return Promise.reject(new Error('should be failed on bad config'));
});
test('right merging package configuration', async function () {
const list = [
'config',
'no-package',
'parseExtension',
'project-homepage',
'project-version',
'project-description'
];
await mergeConfig({
config: path.join(__dirname, '../config_fixture/config.json'),
'no-package': true,
'project-name': 'cool Documentation'
});
});
test('nc(mergeConfig)', async function() {
// Omit configuration from output, for simplicity
const nc = _.curryRight(_.omit, 2)([
const res = config.globalConfig;
list.forEach(key => delete res[key]);
expect(res).toEqual({
'project-name': 'cool Documentation',
foo: 'bar'
});
});
const list = [
'config',
'no-package',
'parseExtension',
@ -44,60 +48,61 @@ test('nc(mergeConfig)', async function() {
'project-name',
'project-version',
'project-description'
]);
];
return Promise.all(
[
[
[
{ config: path.join(__dirname, '../config_fixture/config.json') },
{ foo: 'bar' }
],
[
{
passThrough: true,
config: path.join(__dirname, '../config_fixture/config.json')
},
{ foo: 'bar', passThrough: true }
],
[
{
config: path.join(__dirname, '../config_fixture/config_comments.json')
},
{ foo: 'bar' }
],
[
{ config: path.join(__dirname, '../config_fixture/config.yaml') },
{ foo: 'bar' }
],
[
{ config: path.join(__dirname, '../config_fixture/config.yml') },
{ foo: 'bar' }
],
[
{ config: path.join(__dirname, '../config_fixture/config') },
{ foo: 'bar' }
],
[
{ config: path.join(__dirname, '../config_fixture/config_links.yml') },
{ foo: 'hello [link](https://github.com/my/link) world' }
],
[
{ config: path.join(__dirname, '../config_fixture/config_file.yml') },
{
toc: [
{
name: 'snowflake',
file: path.join(__dirname, '../fixture/snowflake.md')
}
]
}
]
].map(pair =>
mergeConfig(Object.assign(pair[0], { 'no-package': true }))
.then(nc)
.then(res => {
expect(res).toEqual(pair[1]);
})
)
);
{ config: path.join(__dirname, '../config_fixture/config.json') },
{ foo: 'bar' }
],
[
{
passThrough: true,
config: path.join(__dirname, '../config_fixture/config.json')
},
{ foo: 'bar', passThrough: true }
],
[
{
config: path.join(__dirname, '../config_fixture/config_comments.json')
},
{ foo: 'bar' }
],
[
{ config: path.join(__dirname, '../config_fixture/config.yaml') },
{ foo: 'bar' }
],
[
{ config: path.join(__dirname, '../config_fixture/config.yml') },
{ foo: 'bar' }
],
[
{ config: path.join(__dirname, '../config_fixture/config') },
{ foo: 'bar' }
],
[
{
config: path.join(__dirname, '../config_fixture/config_links.yml')
},
{ foo: 'hello [link](https://github.com/my/link) world' }
],
[
{ config: path.join(__dirname, '../config_fixture/config_file.yml') },
{
toc: [
{
name: 'snowflake',
file: path.join(__dirname, '../fixture/snowflake.md')
}
]
}
]
].forEach((pair, index) => {
test(`nc(mergeConfig) ${index}`, async function () {
await mergeConfig(Object.assign(pair[0], { 'no-package': true }));
const res = config.globalConfig;
list.forEach(key => delete res[key]);
expect(res).toEqual(pair[1]);
});
});
});

View File

@ -11,6 +11,7 @@ const path = require('path');
const fs = require('fs');
const _ = require('lodash');
const chdir = require('chdir');
const config = require('../src/config');
const UPDATE = !!process.env.UPDATE;
@ -30,6 +31,10 @@ function readOptionsFromFile(file) {
return {};
}
beforeEach(function () {
config.reset();
});
if (fs.existsSync(path.join(__dirname, '../.git'))) {
test('git option', async function () {
jest.setTimeout(10000); // 10 second timeout. After update flow.js on 0.56 version the test is executed more time.
@ -95,10 +100,8 @@ describe('html', function () {
.sync(path.join(__dirname, 'fixture/html', '*.input.js'))
.forEach(function (file) {
test(path.basename(file), async function () {
const result = await documentation.build(
[file],
readOptionsFromFile(file)
);
const options = readOptionsFromFile(file);
const result = await documentation.build([file], options);
const html = await outputHtml(result, {});
const clean = html
.sort((a, b) => a.path > b.path)

29
src/config.js Normal file
View File

@ -0,0 +1,29 @@
const defaultConfig = {
// package.json ignored and don't get project infromation
'no-package': false,
// Extenstions which by dafault are parse
parseExtension: ['mjs', 'js', 'jsx', 'es5', 'es6', 'vue']
};
function normalaze(config, global) {
if (config.parseExtension) {
config.parseExtension = Array.from(
new Set([...config.parseExtension, ...global.parseExtension])
);
}
return config;
}
module.exports = {
globalConfig: {
...defaultConfig
},
reset() {
this.globalConfig = { ...defaultConfig };
this.globalConfig.parseExtension = [...defaultConfig.parseExtension];
},
add(parameters) {
Object.assign(this.globalConfig, normalaze(parameters, this.globalConfig));
}
};

View File

@ -1,3 +1,4 @@
const conf = require('./config');
const yaml = require('js-yaml');
const fs = require('fs');
const pify = require('pify');
@ -5,16 +6,15 @@ const readPkgUp = require('read-pkg-up');
const path = require('path');
const stripComments = require('strip-json-comments');
function processToc(config, absFilePath) {
function normalizeToc(config, basePath) {
if (!config || !config.toc) {
return config;
}
config.toc = config.toc.map(entry => {
if (entry && entry.file) {
entry.file = path.join(path.dirname(absFilePath), entry.file);
entry.file = path.join(basePath, entry.file);
}
return entry;
});
@ -25,72 +25,60 @@ function processToc(config, absFilePath) {
* Use the nearest package.json file for the default
* values of `name` and `version` config.
*
* @param {Object} config the user-provided config, usually via argv
* @param {boolean} noPackage options which prevent ge info about project from package.json
* @returns {Promise<Object>} configuration with inferred parameters
* @throws {Error} if the file cannot be read.
*/
function mergePackage(config) {
if (config.noPackage) {
return Promise.resolve(config);
async function readPackage(noPackage) {
const global = conf.globalConfig;
if (noPackage) {
return {};
}
const param = ['name', 'homepage', 'version', 'description'];
try {
const { pkg } = await readPkgUp();
return param.reduce((res, key) => {
res[`project-${key}`] = global[key] || pkg[key];
return res;
}, {});
} catch (e) {
return {};
}
return (
readPkgUp()
.then(pkg => {
['name', 'homepage', 'version', 'description'].forEach(key => {
config[`project-${key}`] = config[`project-${key}`] || pkg.pkg[key];
});
return config;
})
// Allow this to fail: this inference is not required.
.catch(() => config)
);
}
/**
* Merge a configuration file into program config, assuming that the location
* of the configuration file is given as one of those config.
*
* @param {Object} config the user-provided config, usually via argv
* @returns {Promise<Object>} configuration, if it can be parsed
* @param {String} config the user-provided config path, usually via argv
* @returns {Promise<Object>} configuration, which are parsed
* @throws {Error} if the file cannot be read.
*/
function mergeConfigFile(config) {
if (config && typeof config.config === 'string') {
const filePath = config.config;
const ext = path.extname(filePath);
const absFilePath = path.resolve(process.cwd(), filePath);
return pify(fs)
.readFile(absFilePath, 'utf8')
.then(rawFile => {
if (ext === '.json') {
return Object.assign(
{},
config,
processToc(JSON.parse(stripComments(rawFile)), absFilePath)
);
}
return Object.assign(
{},
config,
processToc(yaml.safeLoad(rawFile), absFilePath)
);
});
async function readConfigFile(config) {
if (typeof config !== 'string') {
return {};
}
const filePath = config;
const absFilePath = path.resolve(process.cwd(), filePath);
const rawFile = await pify(fs).readFile(absFilePath, 'utf8');
const basePath = path.dirname(absFilePath);
return Promise.resolve(config || {});
let obj = null;
if (path.extname(filePath) === '.json') {
obj = JSON.parse(stripComments(rawFile));
} else {
obj = yaml.safeLoad(rawFile);
}
if ('noPackage' in obj) {
obj['no-package'] = obj.noPackage;
delete obj.noPackage;
}
return normalizeToc(obj, basePath);
}
function mergeConfig(config) {
config.parseExtension = (config.parseExtension || []).concat([
'mjs',
'js',
'jsx',
'es5',
'es6',
'vue'
]);
module.exports = async function mergeConfig(config = {}) {
conf.add(config);
conf.add(await readConfigFile(conf.globalConfig.config));
conf.add(await readPackage(conf.globalConfig['no-package']));
return mergeConfigFile(config).then(mergePackage);
}
module.exports = mergeConfig;
return conf.globalConfig;
};