@kendallgassner/eslint-plugin-package-json
Version:
Rules for valid, consistent, and readable package.json files
141 lines (133 loc) • 4.54 kB
JavaScript
/**
* @fileoverview Package properties must be declared in standard order
* @author Magento Commerce
*/
;
const disparity = require('disparity');
const {
isPackageJson,
extractPackageObjectFromAST
} = require('../processors/PackageJsonProcessor');
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
const standardOrder = [
'name',
'version',
'private',
'publishConfig',
'description',
'main',
'browser',
'files',
'bin',
'directories',
'man',
'scripts',
'repository',
'keywords',
'author',
'license',
'bugs',
'homepage',
'config',
'dependencies',
'devDependencies',
'peerDependencies',
'optionalDependencies',
'bundledDependencies',
'engines',
'os',
'cpu'
];
const toIndexMap = arr =>
arr.reduce((indexMap, value, index) => {
indexMap[value] = index;
return indexMap;
}, {});
module.exports = {
meta: {
docs: {
description:
'Package properties must be declared in standard order',
category: 'Best Practices',
recommended: true
},
fixable: 'code', // or "code" or "whitespace"
schema: [
{
type: 'array',
items: {
type: 'string'
}
}
]
},
create(context) {
return {
'Program:exit': node => {
if (!isPackageJson(context.getFilename())) {
return;
}
const sourceCode = context.getSourceCode();
const packageRoot = extractPackageObjectFromAST(node);
const original = JSON.parse(sourceCode.getText(packageRoot));
const originalIndexMap = toIndexMap(Object.keys(original));
const requiredOrder = context.options[0] || standardOrder;
const requiredIndexMap = toIndexMap(requiredOrder);
const orderedSource =
JSON.stringify(
Object.entries(original)
.sort(([a], [b]) => {
const aIndex = requiredIndexMap[a];
const bIndex = requiredIndexMap[b];
const notRequired = {
a: isNaN(aIndex),
b: isNaN(bIndex)
};
if (notRequired.a && notRequired.b) {
// istanbul ignore next: node almost never compares
return originalIndexMap[a] >
originalIndexMap[b]
? 1
: -1;
}
if (notRequired.a) {
return 1;
}
if (notRequired.b) {
return -1;
}
return aIndex > bIndex ? 1 : -1;
})
.reduce((out, [key, value]) => {
out[key] = value;
return out;
}, {}),
null,
2
) + '\n';
const diff = disparity.unified(
orderedSource,
JSON.stringify(original, null, 2) + '\n'
);
if (diff) {
context.report({
node: packageRoot,
message:
'Package top-level properties are not ordered in the NPM standard way:\n\n{{ diff }}',
data: {
diff: diff
.split('\n')
.slice(3)
.join('\n')
},
fix(fixer) {
return fixer.replaceText(node, orderedSource);
}
});
}
}
};
}
};