├── .babelrc
├── .circleci
└── config.yml
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .prettierrc
├── LICENCE
├── README.md
├── index.js
├── package-lock.json
├── package.json
└── test
└── require-sort.test.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/env",
5 | {
6 | "targets": [
7 | "last 2 versions"
8 | ]
9 | }
10 | ]
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | working_directory: ~/eslint-plugin-require-sort
5 | docker:
6 | - image: circleci/node:latest
7 | steps:
8 | - checkout
9 | - restore_cache:
10 | key: npm-cache-v1-{{ checksum "package-lock.json" }}
11 | - run:
12 | name: Install Dependencies
13 | command: npm ci
14 | - save_cache:
15 | key: npm-cache-v1-{{ checksum "package-lock.json" }}
16 | paths:
17 | - /home/circleci/.npm
18 | - run:
19 | name: Run Tests
20 | command: npm run test
21 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
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 |
11 | [*.md]
12 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist
2 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | parser: 'babel-eslint',
5 | env: { es6: true },
6 | parserOptions: {
7 | ecmaVersion: 2020
8 | },
9 | plugins: ['eslint-plugin'],
10 | extends: [
11 | '@extensionengine/eslint-config/base',
12 | 'plugin:eslint-plugin/recommended'
13 | ]
14 | };
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .vscode
3 | .DS_Store
4 | dist
5 | coverage
6 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": false,
3 | "semi": true,
4 | "singleQuote": true
5 | }
6 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020 Zdravko Ćurić
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # eslint-plugin-require-sort
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | - [Introduction](#introduction)
10 | - [Installation](#installation)
11 | - [Usage](#usage)
12 | - [Rule Details](#rule-details)
13 | - [Options](#options)
14 | - [Examples](#examples)
15 | - [Default settings](#default-settings)
16 | - [`ignoreCase`](#ignorecase)
17 | - [`ignoreDeclarationSort`](#ignoredeclarationsort)
18 | - [`ignorePropertySort`](#ignorepropertysort)
19 | - [`propertySyntaxSortOrder`](#propertysyntaxsortorder)
20 | - [Credits](#credits)
21 | - [Contributions](#contributions)
22 | - [License](#license)
23 |
24 | ## Introduction
25 | What is `require`? From latest [Node documentation](https://nodejs.org/api/modules.html) require is a `function`. As Node follows CommonJS module system, `require` function is the easiest way to include modules that exist in separate files - more on that [here](https://nodejs.org/en/knowledge/getting-started/what-is-require/).
26 |
27 | Difference between CommonJS and ES2015 modules is very nicely explained [in this short talk](https://www.youtube.com/watch?v=8O_H2JgV7EQ).
28 |
29 | [From AST point of view](https://astexplorer.net/#/gist/577afe7c245364a40a495051b8289508/9dc2d43629dd548417fb26b4c2aa9ae68578b7d1) `const foo = require('bar')` is a `VariableDeclaration` and `require` is `CallExpression`. And in this eslint plugin is treated as such.
30 | `foo` is in this case an `Identifier`.
31 | In case of destructuring `const { foo, bar } = require('baz')`, `foo` and `bar` are `properties` in `ObjectPattern`.
32 |
33 | This is important for nomenclature in the plugin, because it's not straight forward as in [`sort-imports`](https://eslint.org/docs/rules/sort-imports) rule provided by `eslint`.
34 |
35 | In this plugin all `Identifiers` are called `properties` to simplify things. The goal is to follow the AST as closely as possible. What `member` is to `sort-imports` rule, `properties` are to this plugin.
36 |
37 | Hopefully this all makes sense.
38 |
39 | ## Installation
40 |
41 | Install [ESLint](http://eslint.org):
42 |
43 | ```
44 | $ npm install eslint --save-dev
45 | ```
46 |
47 | Install `eslint-plugin-require-sort`:
48 |
49 | ```
50 | $ npm install eslint-plugin-require-sort --save-dev
51 | ```
52 |
53 | **Note:** If you installed ESLint globally (using the `-g` flag) then you must
54 | also install `eslint-plugin-require-sort` globally.
55 |
56 | ## Usage
57 |
58 | Add `require-sort` to the plugins and rules section of your `.eslintrc(.js|.json|.yaml)` configuration
59 | file:
60 |
61 | ```json
62 | {
63 | "plugins": ["require-sort"],
64 | "rules": {
65 | "require-sort/require-sort": "error"
66 | }
67 | }
68 | ```
69 |
70 | ## Rule Details
71 |
72 | This rule checks all declarations and verifies that all are first sorted by the used property syntax and then alphabetically by the first property or alias name.
73 |
74 | The `--fix` option on the command line automatically fixes some problems reported by this rule: multiple properties on a single line are automatically sorted (e.g. `const { b, a } = require('foo')` is corrected to `const { a, b } = require('foo')`), but multiple lines are not reordered.
75 |
76 | Rule ignores `require` functions inside functions, `if` statements, etc. For example,
77 | ```js
78 | function foo() {
79 | const bar = require('baz');
80 | }
81 | ```
82 | will be ignored. Only top level variable declarations with `require` are considered.
83 |
84 | ## Options
85 |
86 | This rule accepts an object with its properties as
87 |
88 | * `ignoreCase` (default: `false`)
89 | * `ignoreDeclarationOrder` (default: `false`)
90 | * `ignorePropertySort` (default: `false`)
91 | * `propertySyntaxSortOrder` (default: `["none", "multiple", "single"]`); all items must be present in the array, but you can change the order.
92 |
93 | Default option settings are:
94 |
95 | ```json
96 | {
97 | "require-sort/require-sort": ["error", {
98 | "ignoreCase": false,
99 | "ignoreDeclarationSort": false,
100 | "ignorePropertySort": false,
101 | "propertySyntaxSortOrder": ["none", "multiple", "single"]
102 | }]
103 | }
104 | ```
105 |
106 | ## Examples
107 |
108 | ### Default settings
109 |
110 | Examples of **correct** code for this rule when using default options:
111 |
112 | ```js
113 | /*eslint require-sort: "error"*/
114 | const { alpha, beta } = require('alpha');
115 | const { delta, gamma } = require('delta');
116 | const a = require('baz');
117 | const b = require('qux');
118 |
119 | /*eslint require-sort: "error"*/
120 | const a = require('foo');
121 | const b = require('bar');
122 | const c = require('baz');
123 |
124 | /*eslint require-sort: "error"*/
125 | const { a, b } = require('baz');
126 | const c = require('qux');
127 |
128 | /*eslint require-sort: "error"*/
129 | const { a, b, c } = require('foo)'
130 | ```
131 |
132 | Examples of **incorrect** code for this rule when using default options:
133 |
134 | ```js
135 | /*eslint require-sort: "error"*/
136 | const b = require('foo');
137 | const a = require('bar');
138 |
139 | /*eslint require-sort: "error"*/
140 | const a = require('foo');
141 | const A = require('bar');
142 |
143 | /*eslint require-sort: "error"*/
144 | const { b, c } = require('foo');
145 | const { a, b } = require('bar');
146 |
147 | /*eslint require-sort: "error"*/
148 | const a = require('foo');
149 | const { b, c } = require('bar');
150 |
151 | /*eslint require-sort: "error"*/
152 | const a = require('foo');
153 |
154 | /*eslint require-sort: "error"*/
155 | const { b, a, c } = require('foo');
156 | ```
157 |
158 | ### `ignoreCase`
159 |
160 | When `true` the rule ignores the case-sensitivity of the declaration
161 |
162 | Examples of **incorrect** code for this rule with the `{ "ignoreCase": true }` option:
163 |
164 | ```js
165 | /*eslint require-sort: ["error", { "ignoreCase": true }]*/
166 |
167 | const b = require('foo');
168 | const a = require('bar');
169 | ```
170 |
171 | Examples of **correct** code for this rule with the `{ "ignoreCase": true }` option:
172 |
173 | ```js
174 | /*eslint require-sort: ["error", { "ignoreCase": true }]*/
175 |
176 | const a = require('foo');
177 | const B = require('bar');
178 | const c = require('baz');
179 | ```
180 |
181 | Default is `false`.
182 |
183 | ### `ignoreDeclarationSort`
184 |
185 | Ignores the sorting of variable declarations with `require`.
186 |
187 | Examples of **incorrect** code for this rule with the default `{ "ignoreDeclarationSort": false }` option:
188 |
189 | ```js
190 | /*eslint require-sort: ["error", { "ignoreDeclarationSort": false }]*/
191 | const b = require('foo');
192 | const a = require('bar');
193 | ```
194 |
195 | Examples of **correct** code for this rule with the `{ "ignoreDeclarationSort": true }` option:
196 |
197 | ```js
198 | /*eslint require-sort: ["error", { "ignoreDeclarationSort": true }]*/
199 | const a = require('foo');
200 | const b = require('bar');
201 | ```
202 |
203 | ```js
204 | /*eslint require-sort: ["error", { "ignoreDeclarationSort": true }]*/
205 | const b = require('foo');
206 | const a = require('bar');
207 | ```
208 |
209 | Default is `false`.
210 |
211 | ### `ignorePropertySort`
212 |
213 | Ignores the property sorting within a `multiple` property in declaration.
214 |
215 | Examples of **incorrect** code for this rule with the default `{ "ignorePropertySort": false }` option:
216 |
217 | ```js
218 | /*eslint require-sort: ["error", { "ignorePropertySort": false }]*/
219 | const { b, a, c } = require('foo');
220 | ```
221 |
222 | Examples of **correct** code for this rule with the `{ "ignorePropertySort": true }` option:
223 |
224 | ```js
225 | /*eslint require-sort: ["error", { "ignorePropertySort": true }]*/
226 | const { b, a, c } = require('foo');
227 | ```
228 |
229 | Default is `false`.
230 |
231 | ### `propertySyntaxSortOrder`
232 |
233 | There are three different styles and the default property syntax sort order is:
234 | s.
235 | * `none` - just a require expression
236 | * `multiple` - require multiple properties.
237 | * `single` - require single property.
238 |
239 | Both options must be specified in the array, but you can customize their order.
240 |
241 | Examples of **correct** code for this rule with the `{ "propertySyntaxSortOrder": ["none", "single", "multiple"] }` option:
242 |
243 | ```js
244 | /*eslint require-sort: ["error", { "propertySyntaxSortOrder": ["none", "single", "multiple"] }]*/
245 | require('bar');
246 | const z = require('zoo');
247 | const { a, b } = require('foo');
248 | ```
249 |
250 | Default is `["none", "multiple", "single"]`.
251 |
252 | ## Credits
253 | - This plugin is [inspired by `sort-imports` rule](https://github.com/eslint/eslint/blob/master/lib/rules/sort-imports.js). Credits to authors an maintainers of that rule.
254 | - [@vladimyr](https://github.com/vladimyr) who pointed me to AST explorer.
255 | - [@kronicker](https://github.com/kronicker) who said it that this won't work in some cases :stuck_out_tongue_winking_eye:
256 |
257 | ## Contributions
258 | - All contributions, suggestions are welcome.
259 |
260 | ## License
261 |
262 | MIT @ Zdravko Ćurić [(zcuric)](https://github.com/zcuric)
263 |
264 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | rules: {
5 | 'require-sort': {
6 | meta: {
7 | type: 'suggestion',
8 | docs: {
9 | description: 'enforce sorted require declarations within modules',
10 | category: 'ECMAScript 6',
11 | recommended: false,
12 | url: 'https://github.com/zcuric/eslint-plugin-require-sort'
13 | },
14 | schema: [
15 | {
16 | type: 'object',
17 | properties: {
18 | ignoreCase: {
19 | type: 'boolean',
20 | default: false
21 | },
22 | propertySyntaxSortOrder: {
23 | type: 'array',
24 | items: {
25 | enum: ['none', 'multiple', 'single']
26 | },
27 | uniqueItems: true,
28 | minItems: 3,
29 | maxItems: 3
30 | },
31 | ignoreDeclarationSort: {
32 | type: 'boolean',
33 | default: false
34 | },
35 | ignorePropertySort: {
36 | type: 'boolean',
37 | default: false
38 | }
39 | },
40 | additionalProperties: false
41 | }
42 | ],
43 | fixable: 'code'
44 | },
45 | create(context) {
46 | const configuration = context.options[0] || {};
47 | const {
48 | ignoreCase = false,
49 | ignoreDeclarationSort = false,
50 | ignorePropertySort = false,
51 | propertySyntaxSortOrder = ['none', 'multiple', 'single']
52 | } = configuration;
53 | const sourceCode = context.getSourceCode();
54 | const nodes = [];
55 | let previousNode = null;
56 |
57 | const handleDeclarationSort = node => {
58 | if (previousNode) {
59 | const currentIndex = getPropertySyntaxIndex(node);
60 | const previousIndex = getPropertySyntaxIndex(previousNode);
61 | /*
62 | * When the current declaration uses a different property syntax,
63 | * then check if the ordering is correct.
64 | * Otherwise, make a default string compare (like rule sort-vars to be consistent)
65 | * of the first used property name.
66 | */
67 | if (currentIndex === previousIndex) {
68 | reportOnAlphabeticalSort(node, previousNode);
69 | }
70 | if (currentIndex < previousIndex) {
71 | reportOnExpectedSyntax(node, currentIndex, previousIndex);
72 | }
73 | }
74 |
75 | previousNode = node;
76 | };
77 |
78 | const handlePropertySort = node => {
79 | if (isStaticRequire(node)) return;
80 | if (!node.declarations[0].id.properties) return;
81 | const properties = node.declarations[0].id.properties;
82 | const mergeText = (sourceText, property, index) => {
83 | let textAfterProperty = '';
84 | if (index !== properties.length - 1) {
85 | textAfterProperty = sourceCode
86 | .getText()
87 | .slice(
88 | properties[index].range[1],
89 | properties[index + 1].range[0]
90 | );
91 | }
92 | return (
93 | sourceText + sourceCode.getText(property) + textAfterProperty
94 | );
95 | };
96 | const firstUnsortedIndex = properties
97 | .map(getSortableName)
98 | .findIndex((name, index, array) => array[index - 1] > name);
99 |
100 | const fix = ({ replaceTextRange }) => {
101 | // If there are comments in the property list, don't rearrange the properties.
102 | if (hasComments(properties)) return null;
103 | const range = [
104 | properties[0].range[0],
105 | properties[properties.length - 1].range[1]
106 | ];
107 | const text = [...properties].sort(sortByName).reduce(mergeText, '');
108 | return replaceTextRange(range, text);
109 | };
110 |
111 | if (firstUnsortedIndex === -1) return;
112 | const { value } = properties[firstUnsortedIndex];
113 | const propertyName = isAssignmentPattern(value)
114 | ? value.left.name
115 | : value.name;
116 |
117 | context.report({
118 | node: properties[firstUnsortedIndex],
119 | message:
120 | "Property '{{propertyName}}' of the require declaration should be sorted alphabetically.",
121 | data: { propertyName },
122 | fix
123 | });
124 | };
125 |
126 | const isTopLevel = ({ parent }) => parent.type === 'Program';
127 | const isStaticRequire = node => {
128 | if (node.type !== 'CallExpression') return false;
129 | return (
130 | node.callee?.type === 'Identifier' &&
131 | node.callee?.name === 'require' &&
132 | node.arguments?.length === 1
133 | );
134 | };
135 | const isRequire = node =>
136 | node.declarations[0]?.init?.callee?.name === 'require';
137 | const isAssignmentPattern = node => node?.type === 'AssignmentPattern';
138 | const hasObjectPattern = node =>
139 | node.declarations[0]?.id?.type === 'ObjectPattern';
140 | const hasMultipleProperties = node =>
141 | node.declarations[0]?.id?.properties.length > 1;
142 |
143 | const hasComments = properties =>
144 | properties.some(property => {
145 | const commentsBefore = sourceCode.getCommentsBefore(property);
146 | const commentsAfter = sourceCode.getCommentsAfter(property);
147 | return commentsBefore.length || commentsAfter.length;
148 | });
149 |
150 | const getSortableName = ({ value }) => {
151 | const name = isAssignmentPattern(value)
152 | ? value.left.name
153 | : value.name;
154 | if (name) return ignoreCase ? name.toLowerCase() : name;
155 | return null;
156 | };
157 |
158 | const sortByName = (propertyA, propertyB) => {
159 | const aName = getSortableName(propertyA);
160 | const bName = getSortableName(propertyB);
161 | return aName > bName ? 1 : -1;
162 | };
163 |
164 | const getPropertySyntax = node => {
165 | if (isStaticRequire(node)) return 'none';
166 | if (!hasObjectPattern(node) || !hasMultipleProperties(node)) {
167 | return 'single';
168 | }
169 | return 'multiple';
170 | };
171 |
172 | const getPropertySyntaxIndex = node =>
173 | propertySyntaxSortOrder.indexOf(getPropertySyntax(node));
174 |
175 | const getDeclarationName = node => {
176 | if (isStaticRequire(node)) return node.arguments[0].value;
177 | if (!hasObjectPattern(node)) return node.declarations[0].id.name;
178 | const value = node.declarations[0].id.properties[0].value;
179 | return isAssignmentPattern(value) ? value.left.name : value.name;
180 | };
181 |
182 | const reportOnAlphabeticalSort = (node, previousNode) => {
183 | let firstName = getDeclarationName(node);
184 | let previousName = getDeclarationName(previousNode);
185 | if (ignoreCase) {
186 | previousName = previousName && previousName.toLowerCase();
187 | firstName = firstName && firstName.toLowerCase();
188 | }
189 | if (previousName && firstName && firstName < previousName) {
190 | context.report({
191 | node,
192 | message: 'Requires should be sorted alphabetically.'
193 | });
194 | }
195 | };
196 |
197 | const reportOnExpectedSyntax = (node, currentIndex, previousIndex) => {
198 | context.report({
199 | node,
200 | message:
201 | "Expected '{{syntaxA}}' syntax before '{{syntaxB}}' syntax.",
202 | data: {
203 | syntaxA: propertySyntaxSortOrder[currentIndex],
204 | syntaxB: propertySyntaxSortOrder[previousIndex]
205 | }
206 | });
207 | };
208 |
209 | return {
210 | ExpressionStatement(node) {
211 | if (!isTopLevel(node)) return;
212 | if (!isStaticRequire(node.expression)) return;
213 | nodes.push(node.expression);
214 | },
215 | VariableDeclaration(node) {
216 | if (!isTopLevel(node)) return;
217 | if (!isRequire(node)) return;
218 | nodes.push(node);
219 | },
220 | 'Program:exit'() {
221 | if (!ignoreDeclarationSort) nodes.forEach(handleDeclarationSort);
222 | if (!ignorePropertySort) nodes.forEach(handlePropertySort);
223 | }
224 | };
225 | }
226 | }
227 | }
228 | };
229 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "publishConfig": {
3 | "access": "public"
4 | },
5 | "name": "eslint-plugin-require-sort",
6 | "version": "1.3.0",
7 | "description": "ESlint plugin for sorting requires (CommonJS modules) alphabetically",
8 | "main": "dist/index.js",
9 | "scripts": {
10 | "test": "jest",
11 | "lint": "eslint .",
12 | "prebuild": "rimraf dist",
13 | "prepare": "npm run build",
14 | "build": "babel index.js -d dist",
15 | "release": "npx np"
16 | },
17 | "files": [
18 | "dist"
19 | ],
20 | "repository": {
21 | "type": "git",
22 | "url": "https://github.com/zcuric/eslint-plugin-require-sort"
23 | },
24 | "author": "Zdravko Ćurić ",
25 | "license": "MIT",
26 | "bugs": {
27 | "url": "https://github.com/zcuric/eslint-plugin-require-sort/issues"
28 | },
29 | "homepage": "https://github.com/zcuric/eslint-plugin-require-sort",
30 | "keywords": [
31 | "commonjs",
32 | "eslint",
33 | "eslint-plugin",
34 | "eslint-plugin-sort-imports",
35 | "eslint-plugin-require-sort",
36 | "node",
37 | "require"
38 | ],
39 | "devDependencies": {
40 | "@babel/cli": "^7.10.5",
41 | "@babel/core": "^7.19.0",
42 | "@babel/preset-env": "^7.19.0",
43 | "@extensionengine/eslint-config": "^3.0.1",
44 | "babel-eslint": "^10.1.0",
45 | "babel-jest": "^29.0.3",
46 | "eslint": "^8.23.1",
47 | "eslint-config-semistandard": "^17.0.0",
48 | "eslint-config-standard": "^17.0.0",
49 | "eslint-plugin-eslint-plugin": "^5.0.6",
50 | "eslint-plugin-import": "^2.26.0",
51 | "eslint-plugin-node": "^11.1.0",
52 | "eslint-plugin-promise": "^6.0.1",
53 | "eslint-plugin-standard": "^5.0.0",
54 | "eslint-plugin-vue": "^9.4.0",
55 | "jest": "^29.0.3",
56 | "prettier": "^2.7.1",
57 | "rimraf": "^3.0.2"
58 | },
59 | "jest": {
60 | "testPathIgnorePatterns": [
61 | "dist",
62 | "/node_modules/"
63 | ]
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/test/require-sort.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const rule = require('../index.js').rules['require-sort'];
4 | const { RuleTester } = require('eslint');
5 |
6 | const ruleTester = new RuleTester({
7 | parserOptions: { ecmaVersion: 2020, sourceType: 'script' }
8 | });
9 |
10 | const expectedError = {
11 | message: 'Requires should be sorted alphabetically.',
12 | type: 'VariableDeclaration'
13 | };
14 | const ignoreCaseArgs = [{ ignoreCase: true }];
15 |
16 | const test = ({ only = false, ...settings }) => {
17 | return {
18 | settings,
19 | only
20 | };
21 | };
22 |
23 | Object.defineProperty(test, 'only', {
24 | value: settings => test({ ...settings, only: true })
25 | });
26 |
27 | const getTests = (testCases = [], allSuites = true) => {
28 | if (!allSuites) return [];
29 | if (testCases.some(({ only }) => only === true)) {
30 | return testCases.reduce((acc, test) => {
31 | if (test.only === true) acc.push(test.settings);
32 | return acc;
33 | }, []);
34 | }
35 | return testCases.map(({ settings }) => ({ ...settings }));
36 | };
37 |
38 | ruleTester.run('require-sort', rule, {
39 | valid: getTests([
40 | // Sorts only top level
41 | test({
42 | code: `
43 | const a = require('bar');
44 | const b = require('foo');
45 | if (bar) {
46 | const c = require('baz');
47 | }
48 | `
49 | }),
50 | test({
51 | code: `
52 | require('baz');
53 | const a = require('bar');
54 | const { b: c = {} } = require('foo');
55 | const d = 'foobar';
56 | `
57 | }),
58 | test({
59 | code: `
60 | const a = require('bar');
61 | const b = require('foo');
62 |
63 | if (bar) {
64 | const c = require('baz');
65 | }
66 | `
67 | }),
68 | // ignoreCase
69 | test({
70 | code: `
71 | const A = require('bar');
72 | const a = require('foo');
73 | `
74 | }),
75 | test({
76 | code: `
77 | const { a } = require('foo');
78 | const A = require('bar');
79 | `,
80 | options: ignoreCaseArgs
81 | }),
82 | // propertySyntaxSortOrder
83 | test({
84 | code: `
85 | require('foo');
86 | const { b, c } = require('baz');
87 | const A = require('bar');
88 | `
89 | }),
90 | test({
91 | code: `
92 | require('foo');
93 | const A = require('bar');
94 | const { b, c } = require('baz');
95 | require('foo', 'bar');
96 | if (true) {
97 | require('baz');
98 | }
99 | `,
100 | options: [
101 | {
102 | propertySyntaxSortOrder: ['none', 'single', 'multiple']
103 | }
104 | ]
105 | }),
106 | test({
107 | code: `
108 | const A = require('bar');
109 | const { b, c } = require('baz');
110 | require('foo');
111 | `,
112 | options: [
113 | {
114 | propertySyntaxSortOrder: ['single', 'multiple', 'none']
115 | }
116 | ]
117 | }),
118 | test({
119 | code: `
120 | const { b, c } = require('baz');
121 | require('foo');
122 | const A = require('bar');
123 | `,
124 | options: [
125 | {
126 | propertySyntaxSortOrder: ['multiple', 'none', 'single']
127 | }
128 | ]
129 | }),
130 | // ignoreDeclarationSort
131 | test({
132 | code: `
133 | const A = require('bar');
134 | const a = require('foo');
135 | `
136 | }),
137 | test({
138 | code: `
139 | const z = require('bar');
140 | const a = require('foo');
141 | `,
142 | options: [
143 | {
144 | ignoreDeclarationSort: true
145 | }
146 | ]
147 | }),
148 | // ignorePropertySort
149 | test({
150 | code: `
151 | const { a, b, c } = require('bar');
152 | `
153 | }),
154 | test({
155 | code: `
156 | const { a, b: { d: e }, c } = require('bar');
157 | `
158 | }),
159 | test({
160 | code: `
161 | const { b, c, a } = require('bar');
162 | `,
163 | options: [
164 | {
165 | ignorePropertySort: true
166 | }
167 | ]
168 | })
169 | ]),
170 | invalid: getTests([
171 | // ignoreCase
172 | test({
173 | code: `
174 | require('baz');
175 | const a = require('foo');
176 | const A = require('bar');
177 | `,
178 | output: null,
179 | errors: [expectedError]
180 | }),
181 | test({
182 | code: `
183 | require('foo');
184 | require('bar');
185 | const a = require('foo');
186 | const A = require('bar');
187 | `,
188 | output: null,
189 | errors: [{ ...expectedError, type: 'CallExpression' }, expectedError]
190 | }),
191 | // propertySyntaxSortOrder
192 | test({
193 | code: `
194 | require('foo');
195 | const A = require('bar');
196 | const { Ba } = require('bar');
197 | const { b, c } = require('baz');
198 | `,
199 | options: [
200 | {
201 | propertySyntaxSortOrder: ['none', 'multiple', 'single'],
202 | ignoreCase: true
203 | }
204 | ],
205 | errors: [
206 | {
207 | message: "Expected 'multiple' syntax before 'single' syntax.",
208 | type: 'VariableDeclaration'
209 | }
210 | ]
211 | }),
212 | test({
213 | code: `
214 | const A = require('bar');
215 | require('foo');
216 | const { b, c } = require('baz');
217 | `,
218 | options: [
219 | {
220 | propertySyntaxSortOrder: ['none', 'multiple', 'single']
221 | }
222 | ],
223 | errors: [
224 | {
225 | message: "Expected 'none' syntax before 'single' syntax.",
226 | type: 'CallExpression'
227 | }
228 | ]
229 | }),
230 | // property order
231 | test({
232 | code: `
233 | const { b, a, d, c } = require('foo');
234 | `,
235 | output: `
236 | const { a, b, c, d } = require('foo');
237 | `,
238 | errors: [
239 | {
240 | message:
241 | "Property 'a' of the require declaration should be sorted alphabetically.",
242 | type: 'Property'
243 | }
244 | ]
245 | }),
246 | // property order with assignment
247 | test({
248 | code: `
249 | const { b, a = {}, d, c } = require('foo');
250 | `,
251 | output: `
252 | const { a = {}, b, c, d } = require('foo');
253 | `,
254 | errors: [
255 | {
256 | message:
257 | "Property 'a' of the require declaration should be sorted alphabetically.",
258 | type: 'Property'
259 | }
260 | ]
261 | }),
262 | // property order with aliases
263 | test({
264 | code: `
265 | const { b: z, c: g, d: e } = require('foo');
266 | `,
267 | output: `
268 | const { d: e, c: g, b: z } = require('foo');
269 | `,
270 | errors: [
271 | {
272 | message:
273 | "Property 'g' of the require declaration should be sorted alphabetically.",
274 | type: 'Property'
275 | }
276 | ]
277 | }),
278 | test({
279 | code: `
280 | const {zzzzz, /* comment */ aaaaa} = require('foo');
281 | `,
282 | output: null, // not fixed due to comment
283 | errors: [
284 | {
285 | message:
286 | "Property 'aaaaa' of the require declaration should be sorted alphabetically.",
287 | type: 'Property'
288 | }
289 | ]
290 | })
291 | ])
292 | });
293 |
--------------------------------------------------------------------------------