Skip to content

Commit ae7ac0a

Browse files
committed
working draft
1 parent 10d6c4b commit ae7ac0a

File tree

13 files changed

+3268
-0
lines changed

13 files changed

+3268
-0
lines changed

.editorconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
indent_style = space
6+
indent_size = 2
7+
end_of_line = lf
8+
insert_final_newline = true
9+
trim_trailing_whitespace = true
10+
quote_type = single

.eslintrc.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module.exports = {
2+
parserOptions: {
3+
sourceType: 'script',
4+
},
5+
extends: [
6+
'airbnb-base',
7+
'prettier/standard',
8+
'plugin:prettier/recommended',
9+
'plugin:node/recommended',
10+
],
11+
rules: {
12+
'object-shorthand': 0,
13+
},
14+
};

.gitignore

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
8+
# Runtime data
9+
pids
10+
*.pid
11+
*.seed
12+
*.pid.lock
13+
14+
# Directory for instrumented libs generated by jscoverage/JSCover
15+
lib-cov
16+
17+
# Coverage directory used by tools like istanbul
18+
coverage
19+
20+
# nyc test coverage
21+
.nyc_output
22+
23+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
24+
.grunt
25+
26+
# Bower dependency directory (https://bower.io/)
27+
bower_components
28+
29+
# node-waf configuration
30+
.lock-wscript
31+
32+
# Compiled binary addons (https://nodejs.org/api/addons.html)
33+
build/Release
34+
35+
# Dependency directories
36+
node_modules/
37+
jspm_packages/
38+
39+
# TypeScript v1 declaration files
40+
typings/
41+
42+
# Optional npm cache directory
43+
.npm
44+
45+
# Optional eslint cache
46+
.eslintcache
47+
48+
# Optional REPL history
49+
.node_repl_history
50+
51+
# Output of 'npm pack'
52+
*.tgz
53+
54+
# Yarn Integrity file
55+
.yarn-integrity
56+
57+
# dotenv environment variables file
58+
.env
59+
.env.test
60+
61+
# parcel-bundler cache (https://parceljs.org/)
62+
.cache
63+
64+
# next.js build output
65+
.next
66+
67+
# nuxt.js build output
68+
.nuxt
69+
70+
# vuepress build output
71+
.vuepress/dist
72+
73+
# Serverless directories
74+
.serverless/
75+
76+
# FuseBox cache
77+
.fusebox/
78+
79+
# DynamoDB Local files
80+
.dynamodb/

.prettierrc.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
trailingComma: 'all',
3+
};

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# eslint-plugin-typescript-sort-keys
2+
3+
Sort interface keys
4+
5+
## Installation
6+
7+
You'll first need to install [ESLint](http://eslint.org):
8+
9+
```
10+
$ npm i eslint --save-dev
11+
```
12+
13+
Next, install `eslint-plugin-typescript-sort-keys`:
14+
15+
```
16+
$ npm install eslint-plugin-typescript-sort-keys --save-dev
17+
```
18+
19+
**Note:** If you installed ESLint globally (using the `-g` flag) then you must also install `eslint-plugin-typescript-sort-keys` globally.
20+
21+
## Usage
22+
23+
Add `typescript-sort-keys` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix:
24+
25+
```json
26+
{
27+
"plugins": ["typescript-sort-keys"]
28+
}
29+
```
30+
31+
Then configure the rules you want to use under the rules section.
32+
33+
```json
34+
{
35+
"rules": {
36+
"typescript-sort-keys/rule-name": 2
37+
}
38+
}
39+
```
40+
41+
## Supported Rules
42+
43+
- Fill in provided rules here

lib/index.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @fileoverview Sort interface keys
3+
* @author infctr
4+
*/
5+
6+
'use strict';
7+
8+
//------------------------------------------------------------------------------
9+
// Requirements
10+
//------------------------------------------------------------------------------
11+
12+
const requireIndex = require('requireindex');
13+
const path = require('path');
14+
15+
//------------------------------------------------------------------------------
16+
// Plugin Definition
17+
//------------------------------------------------------------------------------
18+
19+
// import all rules in lib/rules
20+
module.exports.rules = requireIndex(path.join(__dirname, 'rules'));

lib/rules/interface.js

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
'use strict';
2+
3+
const naturalCompare = require('natural-compare-lite');
4+
const astUtils = require('../utils/ast');
5+
6+
/**
7+
* Gets the property name of the given `Property` node.
8+
*
9+
* - If the property's key is an `Identifier` node, this returns the key's name
10+
* whether it's a computed property or not.
11+
* - If the property has a static name, this returns the static name.
12+
* - Otherwise, this returns null.
13+
*
14+
* @param {ASTNode} node - The `Property` node to get.
15+
* @returns {string|null} The property name or null.
16+
* @private
17+
*/
18+
function getPropertyName(node) {
19+
return astUtils.getStaticPropertyName(node) || node.key.name || null;
20+
}
21+
22+
/**
23+
* Functions which check that the given 2 names are in specific order.
24+
*
25+
* Postfix `I` is meant insensitive.
26+
* Postfix `N` is meant natural.
27+
*
28+
* @private
29+
*/
30+
const isValidOrders = {
31+
asc(a, b) {
32+
return a <= b;
33+
},
34+
ascI(a, b) {
35+
return a.toLowerCase() <= b.toLowerCase();
36+
},
37+
ascN(a, b) {
38+
return naturalCompare(a, b) <= 0;
39+
},
40+
ascIN(a, b) {
41+
return naturalCompare(a.toLowerCase(), b.toLowerCase()) <= 0;
42+
},
43+
desc(a, b) {
44+
return isValidOrders.asc(b, a);
45+
},
46+
descI(a, b) {
47+
return isValidOrders.ascI(b, a);
48+
},
49+
descN(a, b) {
50+
return isValidOrders.ascN(b, a);
51+
},
52+
descIN(a, b) {
53+
return isValidOrders.ascIN(b, a);
54+
},
55+
};
56+
57+
module.exports = {
58+
meta: {
59+
type: 'suggestion',
60+
61+
docs: {
62+
description: 'require interface keys to be sorted',
63+
category: 'Stylistic Issues',
64+
recommended: false,
65+
url: 'https://eslint.org/docs/rules/sort-keys', // TODO
66+
},
67+
// fixable: 'code',
68+
69+
schema: [
70+
{
71+
enum: ['asc', 'desc'],
72+
},
73+
{
74+
type: 'object',
75+
properties: {
76+
caseSensitive: {
77+
type: 'boolean',
78+
},
79+
natural: {
80+
type: 'boolean',
81+
},
82+
},
83+
additionalProperties: false,
84+
},
85+
],
86+
},
87+
create: function create(context) {
88+
// Parse options.
89+
const order = context.options[0] || 'asc';
90+
const options = context.options[1];
91+
const insensitive = (options && options.caseSensitive) === false;
92+
const natural = Boolean(options && options.natural);
93+
const computedOrder = [order, insensitive && 'I', natural && 'N']
94+
.filter(Boolean)
95+
.join('');
96+
const isValidOrder = isValidOrders[computedOrder];
97+
98+
// The stack to save the previous property's name for each object literals.
99+
let stack = null;
100+
101+
const visitor = node => {
102+
const { prevName } = stack;
103+
const thisName = getPropertyName(node);
104+
105+
stack.prevName = thisName || prevName;
106+
107+
if (!prevName || !thisName) {
108+
return;
109+
}
110+
111+
if (!isValidOrder(prevName, thisName)) {
112+
context.report({
113+
node,
114+
loc: node.key.loc,
115+
message:
116+
"Expected interface keys to be in {{natural}}{{insensitive}}{{order}}ending order. '{{thisName}}' should be before '{{prevName}}'.",
117+
data: {
118+
thisName,
119+
prevName,
120+
order,
121+
insensitive: insensitive ? 'insensitive ' : '',
122+
natural: natural ? 'natural ' : '',
123+
},
124+
});
125+
}
126+
};
127+
128+
return {
129+
TSInterfaceDeclaration() {
130+
stack = {
131+
upper: stack,
132+
prevName: null,
133+
};
134+
},
135+
136+
'TSInterfaceDeclaration:exit'() {
137+
stack = stack.upper;
138+
},
139+
140+
TSPropertySignature(node) {
141+
return visitor(node);
142+
},
143+
144+
TSMethodSignature(node) {
145+
return visitor(node);
146+
},
147+
};
148+
},
149+
};

0 commit comments

Comments
 (0)