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;
|
exports.extendDefaultPlugins = extendDefaultPlugins;
|
||||||
|
|
||||||
const optimize = (svgstr, config = {}) => {
|
const optimize = (svgstr, config) => {
|
||||||
|
if (config == null) {
|
||||||
|
config = {};
|
||||||
|
}
|
||||||
if (typeof config !== 'object') {
|
if (typeof config !== 'object') {
|
||||||
throw Error('Config should be an object')
|
throw Error('Config should be an object')
|
||||||
}
|
}
|
||||||
|
@@ -4,9 +4,8 @@
|
|||||||
const FS = require('fs');
|
const FS = require('fs');
|
||||||
const PATH = require('path');
|
const PATH = require('path');
|
||||||
const chalk = require('chalk');
|
const chalk = require('chalk');
|
||||||
const { optimize } = require('../svgo.js');
|
const { loadConfig, optimize } = require('../svgo-node.js');
|
||||||
const pluginsMap = require('../../plugins/plugins.js');
|
const pluginsMap = require('../../plugins/plugins.js');
|
||||||
const YAML = require('js-yaml');
|
|
||||||
const PKG = require('../../package.json');
|
const PKG = require('../../package.json');
|
||||||
const { encodeSVGDatauri, decodeSVGDatauri } = require('./tools.js');
|
const { encodeSVGDatauri, decodeSVGDatauri } = require('./tools.js');
|
||||||
const regSVGFile = /\.svg$/;
|
const regSVGFile = /\.svg$/;
|
||||||
@@ -50,7 +49,7 @@ module.exports = function makeProgram(program) {
|
|||||||
.action(action);
|
.action(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
function action(args, opts) {
|
async function action(args, opts) {
|
||||||
var input = opts.input || args;
|
var input = opts.input || args;
|
||||||
var output = opts.output;
|
var output = opts.output;
|
||||||
var config = {}
|
var config = {}
|
||||||
@@ -105,26 +104,13 @@ function action(args, opts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --config
|
// --config
|
||||||
if (opts.config) {
|
try {
|
||||||
var configPath = PATH.resolve(opts.config),
|
const loadedConfig = await loadConfig(opts.config);
|
||||||
configData;
|
if (loadedConfig != null) {
|
||||||
try {
|
config = loadedConfig;
|
||||||
// require() adds some weird output on YML files
|
}
|
||||||
configData = FS.readFileSync(configPath, 'utf8');
|
} catch (error) {
|
||||||
config = JSON.parse(configData);
|
return printErrorAndExit(error.message);
|
||||||
} 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}'.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --quiet
|
// --quiet
|
||||||
|
8
package-lock.json
generated
8
package-lock.json
generated
@@ -537,6 +537,7 @@
|
|||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"sprintf-js": "~1.0.2"
|
"sprintf-js": "~1.0.2"
|
||||||
}
|
}
|
||||||
@@ -992,7 +993,8 @@
|
|||||||
"esprima": {
|
"esprima": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
"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": {
|
"estree-walker": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
@@ -1510,6 +1512,7 @@
|
|||||||
"version": "3.13.1",
|
"version": "3.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
|
||||||
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
|
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"argparse": "^1.0.7",
|
"argparse": "^1.0.7",
|
||||||
"esprima": "^4.0.0"
|
"esprima": "^4.0.0"
|
||||||
@@ -2404,7 +2407,8 @@
|
|||||||
"sprintf-js": {
|
"sprintf-js": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"stable": {
|
"stable": {
|
||||||
"version": "0.1.8",
|
"version": "0.1.8",
|
||||||
|
@@ -38,7 +38,7 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/svg/svgo.git"
|
"url": "git://github.com/svg/svgo.git"
|
||||||
},
|
},
|
||||||
"main": "./lib/svgo.js",
|
"main": "./lib/svgo-node.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
"svgo": "./bin/svgo"
|
"svgo": "./bin/svgo"
|
||||||
},
|
},
|
||||||
@@ -59,7 +59,6 @@
|
|||||||
"css-select-base-adapter": "^0.1.1",
|
"css-select-base-adapter": "^0.1.1",
|
||||||
"css-tree": "^1.1.2",
|
"css-tree": "^1.1.2",
|
||||||
"csso": "^4.2.0",
|
"csso": "^4.2.0",
|
||||||
"js-yaml": "^3.13.1",
|
|
||||||
"sax": "~1.2.4",
|
"sax": "~1.2.4",
|
||||||
"stable": "^0.1.8"
|
"stable": "^0.1.8"
|
||||||
},
|
},
|
||||||
|
@@ -121,7 +121,7 @@ describe('coa', function() {
|
|||||||
|
|
||||||
const stdin = require('mock-stdin').stdin();
|
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'])
|
runProgram(['--input', '-', '--output', 'temp.svg', '--string', fs.readFileSync(svgPath, 'utf8'), '--quiet'])
|
||||||
.then(onComplete, onComplete);
|
.then(onComplete, onComplete);
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
const { expect } = require('chai');
|
const { expect } = require('chai');
|
||||||
|
const { loadConfig } = require('../../lib/svgo-node.js');
|
||||||
const {
|
const {
|
||||||
resolvePluginConfig,
|
resolvePluginConfig,
|
||||||
extendDefaultPlugins
|
extendDefaultPlugins
|
||||||
@@ -182,6 +184,66 @@ describe('config', function() {
|
|||||||
expect(extendedPlugins[extendedPlugins.length - 1].name).to.equal('customPlugin');
|
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) {
|
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