Skip to content

Commit

Permalink
Change how type import/export specifiers are sorted (#125)
Browse files Browse the repository at this point in the history
  • Loading branch information
lydell authored Jan 27, 2023
1 parent a58f3ed commit 3ef8f5a
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 52 deletions.
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ This is for those who use `eslint --fix` (autofix) a lot and want to completely
## Example

<!-- prettier-ignore -->
```js
```ts
import React from "react";
import Button from "../Button";

Expand All @@ -41,7 +41,7 @@ import { truncate, formatNumber } from "../../utils";
⬇️

<!-- prettier-ignore -->
```js
```ts
import classnames from "classnames";
import PropTypes from "prop-types";
import React from "react";
Expand Down Expand Up @@ -222,7 +222,7 @@ If both `import type` _and_ regular imports are used for the same source, the ty
### Example

<!-- prettier-ignore -->
```js
```ts
// Side effect imports. (These are not sorted internally.)
import "./setup";
import "some-polyfill";
Expand All @@ -245,7 +245,6 @@ import Error from "@/components/error.vue";
// Relative imports.
import e from "../..";
import type { B } from "../types";
import typeof C from "../types";
import f from "../Utils"; // Case insensitive.
import g from ".";
import h from "./constants";
Expand Down Expand Up @@ -281,11 +280,8 @@ export default whatever;

Regardless of group, imported items are sorted like this:

```js
```ts
import {
// First, type imports. (`export { type x, typeof y }` is a syntax error).
type x,
typeof y,
// Numbers are sorted by their numeric value:
img1,
img2,
Expand All @@ -295,19 +291,23 @@ import {
L, // Case insensitive.
m as anotherName, // Sorted by the “external interface” name “m”, not “anotherName”.
m as tie, // But do use the file-local name in case of a tie.
n,
// Types are sorted as if the `type` keyword wasn’t there.
type x,
y,
} from "./x";
```

Exported items are sorted even for exports _without_ `from` (even though the whole export statement itself isn’t sorted in relation to other exports):

```js
```ts
export {
k,
L, // Case insensitive.
anotherName as m, // Sorted by the “external interface” name “m”, not “anotherName”.
// tie as m, // For exports there can’t be ties – all exports must be unique.
n,
// Types are sorted as if the `type` keyword wasn’t there.
type x,
y,
};
export type { A, B, A as C };
```
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import {
// First, type imports. (`export { type x, typeof y }` is a syntax error).
type x,
typeof y,
// Numbers are sorted by their numeric value:
img1,
img2,
Expand All @@ -11,15 +8,19 @@ import {
L, // Case insensitive.
m as anotherName, // Sorted by the “external interface” name “m”, not “anotherName”.
m as tie, // But do use the file-local name in case of a tie.
n,
// Types are sorted as if the `type` keyword wasn’t there.
type x,
y,
} from "./x";

export {
k,
L, // Case insensitive.
anotherName as m, // Sorted by the “external interface” name “m”, not “anotherName”.
// tie as m, // For exports there can’t be ties – all exports must be unique.
n,
// Types are sorted as if the `type` keyword wasn’t there.
type x,
y,
};
export type { A, B, A as C };

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import Error from "@/components/error.vue";
// Relative imports.
import e from "../..";
import type { B } from "../types";
import typeof C from "../types";
import f from "../Utils"; // Case insensitive.
import g from ".";
import h from "./constants";
Expand Down Expand Up @@ -52,7 +51,3 @@ export type Type = string;
export { named, other as renamed };
export type { T, U as V };
export default whatever;

var named, other;
type T = 1;
type U = 1;
12 changes: 6 additions & 6 deletions src/shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -730,12 +730,7 @@ function sortImportExportItems(items) {
function sortSpecifierItems(items) {
return items.slice().sort(
(itemA, itemB) =>
// Put type imports/exports before regular ones.
compare(
getImportExportKind(itemA.node),
getImportExportKind(itemB.node)
) ||
// Then compare by imported or exported name (external interface name).
// Compare by imported or exported name (external interface name).
// import { a as b } from "a"
// ^
// export { b as a }
Expand All @@ -750,6 +745,11 @@ function sortSpecifierItems(items) {
// export { b as a }
// ^
compare(itemA.node.local.name, itemB.node.local.name) ||
// Then put type specifiers before regular ones.
compare(
getImportExportKind(itemA.node),
getImportExportKind(itemB.node)
) ||
// Keep the original order if the names are the same. It’s not worth
// trying to compare anything else, `import {a, a} from "mod"` is a syntax
// error anyway (but @babel/eslint-parser kind of supports it).
Expand Down
22 changes: 9 additions & 13 deletions test/__snapshots__/examples.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ import {/* comment at start */ f, /* f */g/* g */ } from "wherever3";
`;

exports[`examples readme-example.prettier.js 1`] = `
exports[`examples readme-example.prettier.ts 1`] = `
import classnames from "classnames";
import PropTypes from "prop-types";
import React from "react";
Expand Down Expand Up @@ -468,7 +468,7 @@ export * from "y";
`;

exports[`examples readme-order.prettier.js 1`] = `
exports[`examples readme-order.prettier.ts 1`] = `
// Side effect imports. (These are not sorted internally.)
import "./setup";
import "some-polyfill";
Expand All @@ -491,7 +491,6 @@ import Error from "@/components/error.vue";
// Relative imports.
import e from "../..";
import type { B } from "../types";
import typeof C from "../types";
import f from "../Utils"; // Case insensitive.
import g from ".";
import h from "./constants";
Expand Down Expand Up @@ -524,17 +523,10 @@ export { named, other as renamed };
export type { T, U as V };
export default whatever;
var named, other;
type T = 1;
type U = 1;
`;

exports[`examples readme-order-items.prettier.js 1`] = `
exports[`examples readme-order-items.prettier.ts 1`] = `
import {
// First, type imports. (\`export { type x, typeof y }\` is a syntax error).
type x,
typeof y,
// Numbers are sorted by their numeric value:
img1,
img2,
Expand All @@ -544,15 +536,19 @@ import {
L, // Case insensitive.
m as anotherName, // Sorted by the “external interface” name “m”, not “anotherName”.
m as tie, // But do use the file-local name in case of a tie.
n,
// Types are sorted as if the \`type\` keyword wasn’t there.
type x,
y,
} from "./x";
export {
k,
L, // Case insensitive.
anotherName as m, // Sorted by the “external interface” name “m”, not “anotherName”.
// tie as m, // For exports there can’t be ties – all exports must be unique.
n,
// Types are sorted as if the \`type\` keyword wasn’t there.
type x,
y,
};
export type { A, B, A as C };
Expand Down
2 changes: 1 addition & 1 deletion test/examples.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe("examples", () => {
const code = name.includes("prettier")
? prettier.format(
item.output || fs.readFileSync(item.filePath, "utf8"),
{ parser: "babel" }
{ parser: "babel-ts" }
)
: item.output;
expect(code).toMatchSnapshot();
Expand Down
23 changes: 18 additions & 5 deletions test/exports.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1072,8 +1072,8 @@ const typescriptTests = {
`export type { T, U as V }; type T = 1; type U = 1;`,

// type specifiers.
`export { type b, type c, a } from "a"`,
`export { type b, type c, a }`,
`export { a, type b, c, type d } from "a"`,
`export { a, type b, c, type d }`,

// Sorted alphabetically.
input`
Expand All @@ -1097,7 +1097,7 @@ const typescriptTests = {
expect(actual).toMatchInlineSnapshot(`
|export type {Z} from "Z";
|export type Y = 5;
|export {type type as type, a, z} from "../type";
|export {a, type type as type, z} from "../type";
|export type {B} from "./B";
|export type {C} from "/B";
|export type {E} from "@/B";
Expand All @@ -1107,6 +1107,19 @@ const typescriptTests = {
errors: 1,
},

// Type import with same name as regular import comes first.
{
code: input`
|export {MyClass, type MyClass} from "../type";
`,
output: (actual) => {
expect(actual).toMatchInlineSnapshot(
`export {type MyClass,MyClass} from "../type";`
);
},
errors: 1,
},

// Exports inside module declarations.
{
code: input`
Expand All @@ -1131,7 +1144,7 @@ const typescriptTests = {
|declare module 'my-module' {
| export { type CopyOptions } from 'fs';
| export type { ParsedPath,PlatformPath } from 'path';interface Something {}
| export {type type as type, a, z} from "../type";
| export {a, type type as type, z} from "../type";
| // comment
|/*
| */→export {} from "b"; // b
Expand All @@ -1140,7 +1153,7 @@ const typescriptTests = {
|}
`);
},
errors: 4,
errors: 3,
},
],
};
Expand Down
12 changes: 6 additions & 6 deletions test/imports.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1591,13 +1591,13 @@ const flowTests = {
`import typeof { } from "a"`,

// type specifiers.
`import { type b, type c, a } from "a"`,
`import { a, type b, c, type d } from "a"`,

// typeof specifiers.
`import { typeof b, typeof c, a } from "a"`,
`import { a, typeof b, c, typeof d } from "a"`,

// Mixed specifiers.
`import { type c, typeof b, a } from "a"`,
`import { type a, typeof b, c } from "a"`,

// Sorted alphabetically.
input`
Expand Down Expand Up @@ -1637,7 +1637,7 @@ const flowTests = {
|
|import type B from "./B";
|import typeof D from "./D";
|import {type Y, typeof T, pluralize,truncate} from "./utils"
|import {pluralize,typeof T, truncate, type Y} from "./utils"
`);
},
errors: 1,
Expand Down Expand Up @@ -1848,7 +1848,7 @@ const typescriptTests = {
`import json from "./foo.json" assert { type: "json" };`,

// type specifiers.
`import { type b, type c, a } from "a"`,
`import { a, type b, c, type d } from "a"`,

// Sorted alphabetically.
input`
Expand Down Expand Up @@ -1890,7 +1890,7 @@ const typescriptTests = {
|import type {Button,target, type as tipe} from "../Button";
|import type X from "../Button";
|import Button from "../Button";
|import {type type as type, a, z} from "../type";
|import {a, type type as type, z} from "../type";
|import styles from "./styles.css";
|
|function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
Expand Down

0 comments on commit 3ef8f5a

Please sign in to comment.