@shopify/polaris-icons-audit
Version:
[](https://www.npmjs.com/package/@shopify/polaris-icons-audit)
108 lines (90 loc) • 3.26 kB
JavaScript
const crypto = require('crypto');
const tryRequire = require('try-require');
// We need to have React in scope as it is used when we eval the svgr output
// eslint-disable-next-line no-unused-vars
const React = require('react');
const {renderToStaticMarkup} = require('react-dom/server');
const {default: convert} = require('@svgr/core');
// If @shopify/polaris-icons is available to be required, check them too
const polarisIcons = tryRequire('@shopify/polaris-icons') || {};
function audit({filenames, contentPerFilename}) {
const polarisIconsComponentsPerFilename = Object.entries(polarisIcons).reduce(
(memo, [importKey, component]) => {
memo[`@shopify/polaris-icons/${importKey}.svg`] = component;
return memo;
},
{},
);
filenames.unshift(...Object.keys(polarisIconsComponentsPerFilename));
const contentsPerFilename = filenames.map((filename) => {
const reactComponent = filename.startsWith('@shopify/polaris-icons')
? polarisIconsComponentsPerFilename[filename]
: componentFromSvgFile(filename, contentPerFilename);
return renderToStaticMarkup(reactComponent());
});
const dependentsByHash = filenames.reduce((memo, filename, i) => {
const fileContents = contentsPerFilename[i];
const contentHash = md5String(JSON.stringify(fileContents));
// eslint-disable-next-line no-prototype-builtins
if (!memo.hasOwnProperty(contentHash)) {
memo[contentHash] = [];
}
memo[contentHash].push(filename);
return memo;
}, {});
const duplicatedDependentsByHash = Object.entries(dependentsByHash).reduce(
(memo, [filename, dependents]) => {
if (dependents.length > 1) {
memo[filename] = dependents;
}
return memo;
},
{},
);
const duplicatedHashesCount = Object.keys(duplicatedDependentsByHash).length;
return {
summary: `Found ${duplicatedHashesCount} content hashes shared by multiple files`,
status: duplicatedHashesCount > 0 ? 'error' : 'pass',
info: Object.entries(duplicatedDependentsByHash)
.map(([hash, duplicatedDependents]) => {
const count = duplicatedDependents.length;
const filesStr = duplicatedDependents
.map((file) => ` ${file}`)
.join('\n');
return ` ${hash} matches content used in ${count} files:\n${filesStr}`;
})
.join('\n'),
};
}
function md5String(string) {
return crypto.createHash('md5').update(string).digest('hex');
}
function componentFromSvgFile(filename, contentPerFilename) {
const source = contentPerFilename[filename];
const svgrOutput = convert.sync(
source,
{
plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx'],
svgoConfig: {
plugins: [{removeTitle: true}, {removeViewBox: false}],
},
replaceAttrValues: {
'#5C5F62': '{undefined}',
'#5c5f62': '{undefined}',
},
template: ({template}, opts, {jsx}) => template.ast`(props) => ${jsx}`,
jsx: {
babelConfig: {
plugins: [['@babel/plugin-transform-react-jsx']],
},
},
},
{
filePath: filename,
},
);
// eslint-disable-next-line no-eval
return eval(svgrOutput);
}
audit.auditName = 'duplicate-content';
module.exports = audit;