Skip to content

Commit

Permalink
Update order-properties to use sort-package-json (#55)
Browse files Browse the repository at this point in the history
closes:
#31


### What

- Replaced the `order-properties` sorting algorithm to use
[sort-package-json](https://github.com/keithamus/sort-package-json).
- Changed the type of `options` from an `Array<string>` to 
```tsx
{
    order?: "legacy" | "sort-package-json" | Array<string>
}
```
- defaulted the sort order to `legacy`
- Add additional testing to test options: "legacy" | "sort-package-json"
.


### Callouts

#### Option: `order: Array<string>`

Previously, any property that was not provided in the `order:
Array<string>` collection was ignored by the sort algorithm. Now that we
are using `sort-package-json`, [a property that is not provided in the
`order: Array<string>` will be sorted by the `defaultSortOrder` provided
by the
library.](https://github.com/keithamus/sort-package-json#optionssortorder)

#### Sort-package-json version
the latest version of sort-package-json is [an esm
module](https://github.com/keithamus/sort-package-json/blob/main/package.json#L20)
so I opted to use an earlier version to avoid heavy refactorign. This
package can be bumped when #33 is merged.

---------

Co-authored-by: Josh Goldberg <[email protected]>
  • Loading branch information
kendallgassner and JoshuaKGoldberg authored Oct 17, 2023
1 parent 10c03c2 commit 6857660
Show file tree
Hide file tree
Showing 5 changed files with 695 additions and 103 deletions.
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
Loading

0 comments on commit 6857660

Please sign in to comment.