Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions docs/rules/order-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,28 @@ Examples of **correct** code for this rule:

### Options

Pass an array of top-level package properties to lint sorting on only those
properties. All properties not in this collection will be ignored.
```js
"package-json/order-properties": ["error", {
order: "sort-package-json"
}]
```

Example:
#### Order

```js
"package-json/order-properties": ["error", [
"name",
"version" // Ensure only that name precedes version
]]
The `order` property specifies the sorting order of package properties. Pass in:

- "legacy" - to order properties specified by [npm documentation](https://docs.npmjs.com/cli/v10/configuring-npm/package-json).
- "sort-package-json" - to order properties by the default order specified in [sort-package-json](https://github.com/keithamus/sort-package-json).
- Array<string> - to specify an array of top-level package properties to lint sorting on only those
properties. All properties not in this collection will be sorted by "sort-package-json" specifications.

```tsx
interface {
order?: "legacy" | "sort-package-json" | Array<string>
}
```

Defaults:
Default: `legacy`

```json
[
Expand Down
88 changes: 39 additions & 49 deletions lib/rules/order-properties.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict';
const disparity = require('disparity');
const sortPackageJson = require('sort-package-json');
const {
isPackageJson,
extractPackageObjectFromAST
Expand Down Expand Up @@ -40,12 +41,6 @@ const standardOrder = [
'cpu'
];

const toIndexMap = (arr) =>
arr.reduce((indexMap, value, index) => {
indexMap[value] = index;
return indexMap;
}, {});

module.exports = {
meta: {
docs: {
Expand All @@ -57,74 +52,69 @@ module.exports = {
fixable: 'code', // or "code" or "whitespace"
schema: [
{
type: 'array',
items: {
type: 'string'
type: 'object',
properties: {
order: {
anyOf: [
{
type: ['string'],
enum: ['legacy', 'sort-package-json']
},
{
type: ['array'],
items: {
type: ['string']
}
}
]
}
}
}
]
},

create(context) {
return {
'Program:exit': (node) => {
'Program:exit': node => {
const options = context.options[0] || { order: 'legacy' };
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 requiredOrder =
options.order === 'legacy' ? standardOrder : options.order;

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 orderedSource = sortPackageJson(
original,
requiredOrder === 'sort-package-json'
? undefined
: {
sortOrder: requiredOrder
}
);

const diff = disparity.unified(
orderedSource,
JSON.stringify(original, null, 2) + '\n'
JSON.stringify(orderedSource, null, 2),
JSON.stringify(original, null, 2)
);
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')
diff: diff
.split('\n')
.slice(3)
.join('\n')
},
fix(fixer) {
return fixer.replaceText(node, orderedSource);
return fixer.replaceText(
node,
JSON.stringify(orderedSource, null, 2) + `\n`
);
}
});
}
Expand Down
17 changes: 9 additions & 8 deletions lib/rules/sort-collections.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ module.exports = {
]
},

create: function (context) {
create: function(context) {
const toSort = context.options[0] || defaultCollections;
return {
'Property:exit': (node) => {
'Property:exit': node => {
if (!isPackageJson(context.getFilename())) {
return;
}
Expand Down Expand Up @@ -63,12 +63,13 @@ module.exports = {
collection,
JSON.stringify(
desiredOrder.reduce((out, property) => {
out[property.key.value] =
JSON.parse(
context
.getSourceCode()
.getText(property.value)
);
out[
property.key.value
] = JSON.parse(
context
.getSourceCode()
.getText(property.value)
);
return out;
}, {}),
null,
Expand Down
10 changes: 5 additions & 5 deletions lib/rules/valid-package-def.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ const unusedErrorPatterns = [
/^author field should have name/i
];

const isUsableError = (errorText) =>
unusedErrorPatterns.every((pattern) => !pattern.test(errorText));
const isUsableError = errorText =>
unusedErrorPatterns.every(pattern => !pattern.test(errorText));

module.exports = {
meta: {
Expand All @@ -32,9 +32,9 @@ module.exports = {
fixable: null // or "code" or "whitespace"
},

create: function (context) {
create: function(context) {
return {
'Program:exit': (node) => {
'Program:exit': node => {
if (!isPackageJson(context.getFilename())) {
return;
}
Expand All @@ -47,7 +47,7 @@ module.exports = {
if (critical || errors) {
const allErrors = [...(critical || []), ...errors];
allErrors.filter(isUsableError).forEach(
(message) =>
message =>
message &&
context.report({
node: packageRoot,
Expand Down
Loading