├── .eslintignore ├── .eslintrc.dogfood.json ├── .eslintrc.js ├── .github └── workflows │ ├── check.yml │ └── test.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── babel.config.json ├── build.js ├── examples ├── .eslintrc.js ├── 1.spaces.just-sort.js ├── 2.spaces.eslint-builtin.js ├── 3.spaces.prettier.js ├── README.md ├── eslint-plugin-import.js ├── groups.custom.js ├── groups.default-reverse.js ├── groups.no-blank-lines.js ├── groups.none.js ├── groups.type-imports-first-in-each-group.ts ├── groups.type-imports-first-sorted.ts ├── groups.type-imports-first.ts ├── groups.type-imports-last-in-each-group.ts ├── groups.type-imports-last-sorted.ts ├── groups.type-imports-last.ts ├── ignore.js ├── markdown.md ├── prettier-comments.js ├── readme-comments-items.js ├── readme-comments.js ├── readme-example.prettier.ts ├── readme-exports-grouping-less-comments.prettier.js ├── readme-exports-grouping.prettier.js ├── readme-order-items.prettier.ts ├── readme-order.prettier.ts ├── typescript.ts └── vue.vue ├── package-lock.json ├── package-real.json ├── package.json ├── src ├── exports.js ├── imports.js ├── index.d.ts ├── index.js └── shared.js ├── test ├── __snapshots__ │ └── examples.test.js.snap ├── examples.test.js ├── exports.test.js ├── helpers.js └── imports.test.js └── vitest.config.mjs /.eslintignore: -------------------------------------------------------------------------------- 1 | !/**/.eslintrc.js 2 | !/*.js 3 | *.snap 4 | build 5 | coverage 6 | examples 7 | node_modules 8 | -------------------------------------------------------------------------------- /.eslintrc.dogfood.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "imports": "error", 4 | "exports": "error" 5 | }, 6 | "parserOptions": { 7 | "sourceType": "module", 8 | "ecmaVersion": "latest" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const error = "error"; 4 | const warn = process.argv.includes("--report-unused-disable-directives") 5 | ? "error" 6 | : "warn"; 7 | 8 | module.exports = { 9 | root: true, 10 | extends: ["eslint:recommended"], 11 | plugins: ["vitest"], 12 | parserOptions: { 13 | ecmaVersion: 2018, 14 | }, 15 | env: { es6: true, node: true }, 16 | rules: { 17 | "arrow-body-style": warn, 18 | "default-case": error, 19 | "default-case-last": warn, 20 | "dot-notation": warn, 21 | "no-caller": error, 22 | "no-console": warn, 23 | "no-eval": error, 24 | "no-labels": error, 25 | "no-octal-escape": error, 26 | "no-param-reassign": error, 27 | "no-promise-executor-return": error, 28 | "no-restricted-syntax": [ 29 | error, 30 | { 31 | selector: "SequenceExpression", 32 | message: 33 | "The comma operator is confusing and a common mistake. Don’t use it!", 34 | }, 35 | ], 36 | "no-self-compare": error, 37 | "no-shadow": error, 38 | "no-template-curly-in-string": error, 39 | "no-unmodified-loop-condition": error, 40 | "no-unneeded-ternary": warn, 41 | "no-useless-backreference": error, 42 | "no-useless-computed-key": warn, 43 | "no-useless-concat": warn, 44 | "no-useless-constructor": warn, 45 | "no-useless-rename": warn, 46 | "no-var": warn, 47 | "object-shorthand": warn, 48 | "one-var": [warn, "never"], 49 | "prefer-arrow-callback": warn, 50 | "prefer-const": warn, 51 | "prefer-destructuring": [warn, { object: true, array: false }], 52 | "prefer-exponentiation-operator": warn, 53 | "prefer-numeric-literals": warn, 54 | "prefer-object-spread": warn, 55 | "prefer-promise-reject-errors": error, 56 | "prefer-regex-literals": warn, 57 | "prefer-rest-params": warn, 58 | "prefer-spread": warn, 59 | "prefer-template": warn, 60 | curly: warn, 61 | eqeqeq: [error, "always", { null: "ignore" }], 62 | strict: error, 63 | yoda: warn, 64 | }, 65 | overrides: [ 66 | { 67 | files: ["test/*.js", "*.mjs"], 68 | parserOptions: { 69 | sourceType: "module", 70 | }, 71 | }, 72 | { 73 | files: ["*.test.js"], 74 | extends: ["plugin:vitest/recommended"], 75 | rules: { 76 | "vitest/no-disabled-tests": warn, 77 | "vitest/no-focused-tests": warn, 78 | }, 79 | }, 80 | ], 81 | }; 82 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | name: Check 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | pull_request: 8 | 9 | jobs: 10 | main: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [20.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - uses: actions/setup-node@v4 21 | with: 22 | node-version: "${{ matrix.node-version }}" 23 | 24 | - name: Cache node_modules 25 | id: cache-node_modules 26 | uses: actions/cache@v4 27 | with: 28 | path: node_modules 29 | key: node_modules-${{ matrix.node-version }}-${{ hashFiles('package.json', 'package-lock.json') }} 30 | 31 | - if: steps.cache-node_modules.outputs.cache-hit != 'true' 32 | run: npm ci --no-audit 33 | 34 | - run: npm run build 35 | 36 | - run: npx eslint . --report-unused-disable-directives 37 | 38 | - run: npx prettier --check . 39 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | pull_request: 8 | 9 | jobs: 10 | main: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [18.x, 20.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - uses: actions/setup-node@v4 21 | with: 22 | node-version: "${{ matrix.node-version }}" 23 | 24 | - name: Cache node_modules 25 | id: cache-node_modules 26 | uses: actions/cache@v4 27 | with: 28 | path: node_modules 29 | key: node_modules-${{ matrix.node-version }}-${{ hashFiles('package.json', 'package-lock.json') }} 30 | 31 | - if: steps.cache-node_modules.outputs.cache-hit != 'true' 32 | run: npm ci --no-audit 33 | 34 | - run: npx vitest 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | coverage 3 | node_modules 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | coverage 3 | examples 4 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "proseWrap": "never" 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### Version 12.1.1 (2024-07-02) 2 | 3 | This release adds a short `meta.docs.description` to each rule. Thanks to fisker Cheung (@fisker)! 4 | 5 | ### Version 12.1.0 (2024-04-13) 6 | 7 | This release adds TypeScript type definitions for the plugin itself. This is useful when you use TypeScript to check your ESLint configuration. It assumes that you install `@types/eslint` yourself. Thanks to @Logicer16! 8 | 9 | ### Version 12.0.0 (2024-02-10) 10 | 11 | This release removes the support for import assignments added in version 11.0.0: 12 | 13 | - Turns out it was broken in some cases. 14 | - The suggested fix went past my complexity tolerance for such an esoteric feature. 15 | - I also learned that they aren’t really imports, and that I don’t understand their semantics well enough to know how sorting them affects your program. 16 | 17 | If you miss the support for import assignments, I suggest you write your own ESLint rule which moves them out of the way from the actual imports, sorting them or not. 18 | 19 | ### Version 11.0.0 (2024-02-08) 20 | 21 | This release adds support for TypeScript import assignments (`import A = B.C` and `import A = require("module")`). Thanks to Szabolcs Kurdi (@szku01) and Svyatoslav Zaytsev (@MillerSvt)! 22 | 23 | It’s only a breaking change if you use TypeScript import assignments, and only in the form that you need to autofix your files. 24 | 25 | In other news, this release adds the `meta` plugin property in preparation for ESLint Flat Config, and avoids the deprecated `context.getSourceCode()` method (while still being backwards compatible). 26 | 27 | ### Version 10.0.0 (2023-01-27) 28 | 29 | This release might move some imported items with `type` around. This is a breaking formatting change (that only affects TypeScript and Flow), but only in the form of that you need to autofix your files. 30 | 31 | In previous versions, `type` specifiers came first: 32 | 33 | ```ts 34 | import { type B, a } from "a"; 35 | export { type B, a } from "a"; 36 | ``` 37 | 38 | Now, all specifiers are sorted alphabetically, regardless of `type`: 39 | 40 | ```ts 41 | import { a, type B } from "a"; 42 | export { a, type B } from "a"; 43 | ``` 44 | 45 | Motivation: 46 | 47 | You might import a class for a type annotation using: 48 | 49 | 50 | ```ts 51 | import { 52 | type MyClass, 53 | coolFunction, 54 | } from "example"; 55 | ``` 56 | 57 | Later, you also start instantiating that class in the same file (`new MyClass()`), so you remove `type`. 58 | 59 | Previously, this resulted in a messy diff due to the class moving: 60 | 61 | ```diff 62 | import { 63 | - type MyClass, 64 | coolFunction, 65 | + MyClass, 66 | } from "example"; 67 | ``` 68 | 69 | Now, the sorting with the `type` keyword would be: 70 | 71 | 72 | ```ts 73 | import { 74 | coolFunction, 75 | type MyClass, 76 | } from "example"; 77 | ``` 78 | 79 | Now there’s no reordering diff, just the `type` keyword being removed: 80 | 81 | ```diff 82 | import { 83 | coolFunction, 84 | - type MyClass, 85 | + MyClass, 86 | } from "example"; 87 | ``` 88 | 89 | This is consistent with [“Why sort on `from`?”][sort-from]. 90 | 91 | Thanks to Jake Bailey (@jakebailey) for reporting and suggesting the fix! 92 | 93 | ### Version 9.0.0 (2023-01-16) 94 | 95 | This version adds support for [eslint-plugin-svelte], and for `declare module` in TypeScript. 96 | 97 | More generally, imports and exports are now supported _anywhere,_ by finding the set of parents of all imports and exports and working with those. Previously, the plugin only sorted imports and exports directly inside a `Program` node. For eslint-plugin-svelte and `declare module` that didn’t cut it. 98 | 99 | This is only a breaking change if you imports or exports in `declare module` in TypeScript, and only in the form of that you need to autofix your files. 100 | 101 | ### Version 8.0.0 (2022-09-03) 102 | 103 | Node.js builtin modules prefixed with `node:` are now in a separate group by default (regex: `^node:`), above the packages group. (Node.js builtins _without_ `node:` are still sorted together with npm packages like before.) 104 | 105 | Before: 106 | 107 | ```js 108 | import fs from "fs"; 109 | import _ from "lodash-es"; 110 | import { rmSync } from "node:fs"; 111 | ``` 112 | 113 | After: 114 | 115 | ```js 116 | import { rmSync } from "node:fs"; 117 | 118 | import fs from "fs"; 119 | import _ from "lodash-es"; 120 | ``` 121 | 122 | This is only a breaking change if you use the `node:` prefix in imports, and only in the form of that you need to autofix your files. 123 | 124 | ### Version 7.0.0 (2020-12-08) 125 | 126 | You can now customize where type imports (`import type { X } from "x"`) go, via the `groups` option. Type imports have `\u0000` at the end. 127 | 128 | This is only a breaking change if you use the `groups` option and your regexes care about what the _last_ character is. If so, you now need to account for the fact that the last character of type imports is `\u0000`. 129 | 130 | ### Version 6.0.1 (2020-11-19) 131 | 132 | - Fixed: `as default` in exports no longer results in invalid code. 133 | 134 | ### Version 6.0.0 (2020-11-15) 135 | 136 | - Renamed: `simple-import-sort/sort` is now called `simple-import-sort/imports`. 137 | - Added: `simple-import-sort/exports` for sorting (some) exports. Big thanks to Remco Haszing (@remcohaszing) for the suggestion and great feedback, and to @JCrepin for the initial implementation! 138 | - Fixed: `../..` imports are now sorted properly based on directory hierarchy. 139 | - Improved: The default regexes for the `groups` option can now be reordered freely without causing imports to unexpectedly end up in other groups than before. 140 | - Removed: Support for Node.js 8. 141 | 142 | ### Version 5.0.3 (2020-04-27) 143 | 144 | - Improved: Reduced package size by 50%. 145 | 146 | ### Version 5.0.2 (2020-03-11) 147 | 148 | - Fixed: The plugin now works with TypeScript 3.8 type imports. Thanks to Liwen Guo (@Livven) and Brandon Chinn (@brandon-leapyear)! 149 | 150 | ### Version 5.0.1 (2020-01-24) 151 | 152 | - Fixed: Side effect imports now correctly keep their original order in Node.js <12. Thanks to Irvin Zhan (@izhan)! 153 | 154 | ### Version 5.0.0 (2019-11-22) 155 | 156 | - Added: The `groups` option for [custom sorting]. 157 | - Changed: Due to the new `groups` option, the default grouping is ever so slightly different. Now, not only _valid_ npm package names are placed in the “packages” group, but also things that _look_ like npm package names, such as `@ui/Section`. And anything starting with `.` is now considered to be a relative import. See [custom sorting] for more information. 158 | - Removed: Built-in support for webpack loader syntax. It didn’t fit well with the new `groups` option, and since I don’t use it myself I decided to remove it. Please open an issue if you have something to say about this! 159 | 160 | ### Version 4.0.0 (2019-06-19) 161 | 162 | - Changed: Sorting is now more human – it is case insensitive (matching the default behavior of TSLint, as well as many IDEs) and numbers are sorted by their numeric values. This might cause some churn but feels a lot nicer. See [#7] for more discussion. 163 | - Improved: `from` paths ending with dots in various ways used to be treated specially. This has now been simplified, which gives a more consistent sorting. Now, `"."` and `".."` are treated as `"./"` and `"../"` – and those are the only special cases for “dotty” paths. For example, you might see `import x from "."` now sorting before `import y from "./y"`. 164 | - Fixed: `".x"` is no longer considered to be a relative import. Only `from` paths equal to `"."` or `".."`, or that start with `"./"` or `"../"` are truly relative. This is a bit of an edge case, but if you do have “weird” imports starting with dots in unusual ways you might notice them jumping up to another group of imports. 165 | - Fixed: `import {} from "a"` is no longer considered a side-effect import. Only imports completely lacking the `{...} from` part are. Remove `{} from` if you relied on this from earlier versions. 166 | - Improved: Trailing spaces after imports are now preserved. Before, if you accidentally added some trailing spaces it would result in a “Run autofix to sort these imports!” error, but the autofix wouldn’t actually sort anything – it would only remove some spaces. That was a bit weird. Now, those spaces are preserved. It is up to other rules or [Prettier] to take care of trailing spaces. 167 | 168 | ### Version 3.1.1 (2019-05-16) 169 | 170 | - Fixed: Semicolon-free code style is now supported. The plugin now leaves a semicolon at the start of a line of code after an import alone. 171 | 172 | ### Version 3.1.0 (2019-03-30) 173 | 174 | - Added: Support for indentation in Vue `