From 4b711da2b2bae87e2a6df3520b14d0f69febc8b6 Mon Sep 17 00:00:00 2001 From: Anton Shchekota Date: Thu, 6 May 2021 19:27:48 +0300 Subject: [PATCH] refactor: use singleton config for execution It will be easy to not put config as parameter each time --- __tests__/bin-readme.js | 1 - __tests__/lib/merge_config.js | 179 +++++++++++++++++----------------- __tests__/test.js | 11 ++- src/config.js | 29 ++++++ src/merge_config.js | 100 +++++++++---------- 5 files changed, 172 insertions(+), 148 deletions(-) create mode 100644 src/config.js diff --git a/__tests__/bin-readme.js b/__tests__/bin-readme.js index e147ff5..d2b6708 100644 --- a/__tests__/bin-readme.js +++ b/__tests__/bin-readme.js @@ -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'); diff --git a/__tests__/lib/merge_config.js b/__tests__/lib/merge_config.js index 5c08770..af4abd6 100644 --- a/__tests__/lib/merge_config.js +++ b/__tests__/lib/merge_config.js @@ -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]); + }); + }); }); diff --git a/__tests__/test.js b/__tests__/test.js index c3c19f0..05e9914 100644 --- a/__tests__/test.js +++ b/__tests__/test.js @@ -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) diff --git a/src/config.js b/src/config.js new file mode 100644 index 0000000..910f566 --- /dev/null +++ b/src/config.js @@ -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)); + } +}; diff --git a/src/merge_config.js b/src/merge_config.js index 292d887..5d47cb1 100644 --- a/src/merge_config.js +++ b/src/merge_config.js @@ -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} 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} configuration, if it can be parsed + * @param {String} config the user-provided config path, usually via argv + * @returns {Promise} 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; +};