mirror of
https://github.com/svg/svgo.git
synced 2025-08-09 02:22:08 +03:00
Implement loadConfig utility (#1328)
Ref https://github.com/svg/svgo/issues/1327 Config file now can only be js. `svgo.config.js` is searched by default. Otherwise any js module specified in `--config` cli flag. Config loader is exposed in entry point as `loadConfig(configFile, cwd)`.
This commit is contained in:
54
lib/svgo-node.js
Normal file
54
lib/svgo-node.js
Normal file
@@ -0,0 +1,54 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const {
|
||||
extendDefaultPlugins,
|
||||
optimize,
|
||||
createContentItem,
|
||||
} = require('./svgo.js');
|
||||
|
||||
const importConfig = async configFile => {
|
||||
try {
|
||||
const config = require(configFile);
|
||||
if (config == null || typeof config !== 'object' || Array.isArray(config)) {
|
||||
throw Error(`Invalid config file "${configFile}"`);
|
||||
}
|
||||
return config;
|
||||
} catch (error) {
|
||||
if (error.code === 'MODULE_NOT_FOUND') {
|
||||
return null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const loadConfig = async (configFile, cwd = process.cwd()) => {
|
||||
if (configFile != null) {
|
||||
if (path.isAbsolute(configFile)) {
|
||||
return await importConfig(configFile);
|
||||
} else {
|
||||
return await importConfig(path.join(cwd, configFile));
|
||||
}
|
||||
}
|
||||
let dir = cwd;
|
||||
while (true) {
|
||||
try {
|
||||
const file = path.join(dir, "svgo.config.js");
|
||||
const stats = await fs.promises.stat(file);
|
||||
if (stats.isFile()) {
|
||||
return await importConfig(file);
|
||||
}
|
||||
} catch {}
|
||||
const parent = path.dirname(dir);
|
||||
if (dir === parent) {
|
||||
return null;
|
||||
}
|
||||
dir = parent;
|
||||
}
|
||||
};
|
||||
|
||||
exports.loadConfig = loadConfig;
|
||||
exports.extendDefaultPlugins = extendDefaultPlugins;
|
||||
exports.optimize = optimize;
|
||||
exports.createContentItem = createContentItem;
|
@@ -23,7 +23,10 @@ const { encodeSVGDatauri } = require('./svgo/tools.js');
|
||||
|
||||
exports.extendDefaultPlugins = extendDefaultPlugins;
|
||||
|
||||
const optimize = (svgstr, config = {}) => {
|
||||
const optimize = (svgstr, config) => {
|
||||
if (config == null) {
|
||||
config = {};
|
||||
}
|
||||
if (typeof config !== 'object') {
|
||||
throw Error('Config should be an object')
|
||||
}
|
||||
|
@@ -4,9 +4,8 @@
|
||||
const FS = require('fs');
|
||||
const PATH = require('path');
|
||||
const chalk = require('chalk');
|
||||
const { optimize } = require('../svgo.js');
|
||||
const { loadConfig, optimize } = require('../svgo-node.js');
|
||||
const pluginsMap = require('../../plugins/plugins.js');
|
||||
const YAML = require('js-yaml');
|
||||
const PKG = require('../../package.json');
|
||||
const { encodeSVGDatauri, decodeSVGDatauri } = require('./tools.js');
|
||||
const regSVGFile = /\.svg$/;
|
||||
@@ -50,7 +49,7 @@ module.exports = function makeProgram(program) {
|
||||
.action(action);
|
||||
}
|
||||
|
||||
function action(args, opts) {
|
||||
async function action(args, opts) {
|
||||
var input = opts.input || args;
|
||||
var output = opts.output;
|
||||
var config = {}
|
||||
@@ -105,26 +104,13 @@ function action(args, opts) {
|
||||
}
|
||||
|
||||
// --config
|
||||
if (opts.config) {
|
||||
var configPath = PATH.resolve(opts.config),
|
||||
configData;
|
||||
try {
|
||||
// require() adds some weird output on YML files
|
||||
configData = FS.readFileSync(configPath, 'utf8');
|
||||
config = JSON.parse(configData);
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
return printErrorAndExit(`Error: couldn't find config file '${opts.config}'.`);
|
||||
} else if (err.code === 'EISDIR') {
|
||||
return printErrorAndExit(`Error: directory '${opts.config}' is not a config file.`);
|
||||
}
|
||||
config = YAML.safeLoad(configData);
|
||||
config.__DIR = PATH.dirname(configPath); // will use it to resolve custom plugins defined via path
|
||||
|
||||
if (!config || Array.isArray(config)) {
|
||||
return printErrorAndExit(`Error: invalid config file '${opts.config}'.`);
|
||||
}
|
||||
const loadedConfig = await loadConfig(opts.config);
|
||||
if (loadedConfig != null) {
|
||||
config = loadedConfig;
|
||||
}
|
||||
} catch (error) {
|
||||
return printErrorAndExit(error.message);
|
||||
}
|
||||
|
||||
// --quiet
|
||||
|
8
package-lock.json
generated
8
package-lock.json
generated
@@ -537,6 +537,7 @@
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
@@ -992,7 +993,8 @@
|
||||
"esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||
"dev": true
|
||||
},
|
||||
"estree-walker": {
|
||||
"version": "2.0.2",
|
||||
@@ -1510,6 +1512,7 @@
|
||||
"version": "3.13.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
|
||||
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
@@ -2404,7 +2407,8 @@
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
||||
"dev": true
|
||||
},
|
||||
"stable": {
|
||||
"version": "0.1.8",
|
||||
|
@@ -38,7 +38,7 @@
|
||||
"type": "git",
|
||||
"url": "git://github.com/svg/svgo.git"
|
||||
},
|
||||
"main": "./lib/svgo.js",
|
||||
"main": "./lib/svgo-node.js",
|
||||
"bin": {
|
||||
"svgo": "./bin/svgo"
|
||||
},
|
||||
@@ -59,7 +59,6 @@
|
||||
"css-select-base-adapter": "^0.1.1",
|
||||
"css-tree": "^1.1.2",
|
||||
"csso": "^4.2.0",
|
||||
"js-yaml": "^3.13.1",
|
||||
"sax": "~1.2.4",
|
||||
"stable": "^0.1.8"
|
||||
},
|
||||
|
@@ -121,7 +121,7 @@ describe('coa', function() {
|
||||
|
||||
const stdin = require('mock-stdin').stdin();
|
||||
|
||||
setTimeout(() => { stdin.send(initialFile, 'ascii').end(); }, 0);
|
||||
setTimeout(() => { stdin.send(initialFile, 'ascii').end(); }, 1000);
|
||||
|
||||
runProgram(['--input', '-', '--output', 'temp.svg', '--string', fs.readFileSync(svgPath, 'utf8'), '--quiet'])
|
||||
.then(onComplete, onComplete);
|
||||
|
@@ -1,6 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const { expect } = require('chai');
|
||||
const { loadConfig } = require('../../lib/svgo-node.js');
|
||||
const {
|
||||
resolvePluginConfig,
|
||||
extendDefaultPlugins
|
||||
@@ -182,6 +184,66 @@ describe('config', function() {
|
||||
expect(extendedPlugins[extendedPlugins.length - 1].name).to.equal('customPlugin');
|
||||
});
|
||||
});
|
||||
|
||||
describe('config', () => {
|
||||
it('is loaded by absolute path', async () => {
|
||||
const config = await loadConfig(
|
||||
path.join(process.cwd(), './test/config/fixtures/one/two/config.js'),
|
||||
);
|
||||
expect(config).to.deep.equal({ plugins: [] });
|
||||
});
|
||||
it('is loaded by relative path to cwd', async () => {
|
||||
const config = await loadConfig(
|
||||
'one/two/config.js',
|
||||
path.join(process.cwd(), './test/config/fixtures'),
|
||||
);
|
||||
expect(config).to.deep.equal({ plugins: [] });
|
||||
});
|
||||
it('is searched in cwd and up', async () => {
|
||||
const config = await loadConfig(
|
||||
null,
|
||||
path.join(process.cwd(), './test/config/fixtures/one/two'),
|
||||
);
|
||||
expect(config).to.deep.equal({ plugins: [] });
|
||||
});
|
||||
it('gives null module does not exist', async () => {
|
||||
const absoluteConfig = await loadConfig(
|
||||
path.join(process.cwd(), './test/config/fixtures/config.js'),
|
||||
);
|
||||
expect(absoluteConfig).to.equal(null);
|
||||
const searchedConfig = await loadConfig(
|
||||
null,
|
||||
path.join(process.cwd(), './test/config'),
|
||||
);
|
||||
expect(searchedConfig).to.equal(null);
|
||||
});
|
||||
it('is failed to load when module exports not an object', async () => {
|
||||
try {
|
||||
await loadConfig(
|
||||
path.join(process.cwd(), './test/config/fixtures/invalid-null.js'),
|
||||
);
|
||||
expect.fail('Config is loaded successfully');
|
||||
} catch (error) {
|
||||
expect(error.message).to.match(/Invalid config file/);
|
||||
}
|
||||
try {
|
||||
await loadConfig(
|
||||
path.join(process.cwd(), './test/config/fixtures/invalid-array.js'),
|
||||
);
|
||||
expect.fail('Config is loaded successfully');
|
||||
} catch (error) {
|
||||
expect(error.message).to.match(/Invalid config file/);
|
||||
}
|
||||
try {
|
||||
await loadConfig(
|
||||
path.join(process.cwd(), './test/config/fixtures/invalid-string.js'),
|
||||
);
|
||||
expect.fail('Config is loaded successfully');
|
||||
} catch (error) {
|
||||
expect(error.message).to.match(/Invalid config file/);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function getPlugin(name, plugins) {
|
||||
|
1
test/config/fixtures/invalid-array.js
Normal file
1
test/config/fixtures/invalid-array.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = [];
|
1
test/config/fixtures/invalid-null.js
Normal file
1
test/config/fixtures/invalid-null.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = null;
|
1
test/config/fixtures/invalid-string.js
Normal file
1
test/config/fixtures/invalid-string.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = '';
|
1
test/config/fixtures/one/two/config.js
Normal file
1
test/config/fixtures/one/two/config.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = { plugins: [] };
|
1
test/config/fixtures/svgo.config.js
Normal file
1
test/config/fixtures/svgo.config.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = { plugins: [] };
|
Reference in New Issue
Block a user