Skip to content

Commit

Permalink
Sort exports (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
lydell authored Nov 15, 2020
1 parent dec0366 commit 3d795d5
Show file tree
Hide file tree
Showing 17 changed files with 2,005 additions and 409 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ module.exports = {
env: { es6: true, node: true },
rules: {
"arrow-body-style": warn,
"default-case": error,
"default-case-last": warn,
"dot-notation": warn,
"no-caller": error,
"no-console": warn,
Expand Down
247 changes: 200 additions & 47 deletions README.md

Large diffs are not rendered by default.

21 changes: 12 additions & 9 deletions examples/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@ module.exports = {
},
env: { es6: true },
rules: {
// The actual rule name is "simple-import-sort/sort", but for technical
// reasons it’s just called "sort" within the examples of this repo.
// "simple-import-sort/sort": "error",
sort: "error",
// The actual rule names are "simple-import-sort/imports" and
// "simple-import-sort/exports", but for technical reasons they’re called
// just "imports" and "exports" within the examples of this repo.
// "simple-import-sort/imports": "error",
// "simple-import-sort/exports": "error",
imports: "error",
exports: "error",
},
overrides: [
{
// This file only enables the “sort” rule from this plugin. After
// This file only enables the “imports” rule from this plugin. After
// autofixing, there might be some oddly placed spaces.
files: ["1.spaces.just-sort.js"],
},
Expand Down Expand Up @@ -65,7 +68,7 @@ module.exports = {
{
files: ["groups.custom.js"],
rules: {
sort: [
imports: [
"error",
{
groups: [
Expand Down Expand Up @@ -94,7 +97,7 @@ module.exports = {
{
files: ["groups.no-blank-lines.js"],
rules: {
sort: [
imports: [
"error",
{
// The default grouping, but with no blank lines.
Expand All @@ -106,7 +109,7 @@ module.exports = {
{
files: ["groups.default-reverse.js"],
rules: {
sort: [
imports: [
"error",
{
// The default grouping, but in reverse.
Expand All @@ -118,7 +121,7 @@ module.exports = {
{
files: ["groups.none.js"],
rules: {
sort: [
imports: [
"error",
{
// No grouping, only alphabetical sorting.
Expand Down
10 changes: 5 additions & 5 deletions examples/ignore.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import "a";
separator();

// You can also disable sorting for a whole chunk. The actual rule name is
// "simple-import-sort/sort", but for technical reasons it’s just called "sort"
// within the examples of this repo.
// For copying: eslint-disable-next-line simple-import-sort/sort
// eslint-disable-next-line sort
// "simple-import-sort/imports", but for technical reasons it’s just called
// "imports" within the examples of this repo.
// For copying: eslint-disable-next-line simple-import-sort/imports
// eslint-disable-next-line imports
import d from "d";
import c from "c";

// Note that putting a `eslint-disable-next-line simple-import-sort/sort`
// Note that putting a `eslint-disable-next-line simple-import-sort/imports`
// comment in the middle of a chunk of imports WON’T WORK. It HAS to be at the
// very start!

Expand Down
6 changes: 6 additions & 0 deletions examples/readme-exports-grouping-less-comments.prettier.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export * from "./";
/* This one does not. */ export * from "a"; // Neither does this one.
/* Nor this
one */ export * from "b";
export * from "x";
export * from "y";
9 changes: 9 additions & 0 deletions examples/readme-exports-grouping.prettier.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export * from "x";
export * from "y";

// This comment starts a new group.
/* This one does not. */ export * from "a"; // Neither does this one.
/* Nor this
one */ export * from "b";
/* But this one does. */
export * from "./";
28 changes: 28 additions & 0 deletions examples/readme-order-items.prettier.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
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,
img10,
// Then everything else, alphabetically:
k,
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,
} 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,
};
export type { A, B, A as C };

var k_, L_, anotherName_, n_;
type A = 1;
type B = 1;
50 changes: 32 additions & 18 deletions examples/readme-order.prettier.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,44 @@ import b from "https://example.com/script.js";
// Absolute imports and other imports.
import c from "/";
import d from "/home/user/foo";
import Error from "@/components/error.vue"
import Error from "@/components/error.vue";

// Relative imports.
import e from "../..";
import f from "../../Utils"; // Case insensitive.
import type { B } from "../types";
import typeof C from "../types";
import f from "../Utils"; // Case insensitive.
import g from ".";
import h from "./constants";
import i from "./styles";

// Regardless of group, imported items are sorted like this:
import {
// First, type imports.
type x,
typeof y,
// Then everything else, alphabetically:
k,
L, // Case insensitive.
m as anotherName, // Sorted by the original name “m”, not “anotherName”.
m as tie, // But do use the `as` name in case of a tie.
n,
// Numbers are sorted by their numeric value:
img1,
img2,
img10,
} from "./x";
// Different types of exports:
export { a } from "../..";
export { b } from "/";
export { Error } from "@/components/error.vue";
export * from "an-npm-package";
export { readFile } from "fs";
export * as ns from "https://example.com/script.js";

// This comment groups some more exports:
export { e } from "../..";
export { f } from "../Utils";
export { g } from ".";
export { h } from "./constants";
export { i } from "./styles";

// Other exports – the plugin does not touch these, other than sorting named
// exports inside braces.
export var one = 1;
export let two = 2;
export const three = 3;
export function func() {}
export class Class {}
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;
91 changes: 91 additions & 0 deletions src/exports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"use strict";

const shared = require("./shared");

module.exports = {
meta: {
type: "layout",
fixable: "code",
schema: [],
docs: {
url:
"https://github.com/lydell/eslint-plugin-simple-import-sort#sort-order",
},
messages: {
sort: "Run autofix to sort these exports!",
},
},
create: (context) => ({
Program: (programNode) => {
const sourceCode = context.getSourceCode();
for (const chunk of shared.extractChunks(programNode, (node, lastNode) =>
isPartOfChunk(node, lastNode, sourceCode)
)) {
maybeReportChunkSorting(chunk, context);
}
},
ExportNamedDeclaration: (node) => {
if (node.source == null && node.declaration == null) {
maybeReportExportSpecifierSorting(node, context);
}
},
}),
};

function maybeReportChunkSorting(chunk, context) {
const sourceCode = context.getSourceCode();
const items = shared.getImportExportItems(
chunk,
sourceCode,
() => false, // isSideEffectImport
getSpecifiers
);
const sortedItems = [[shared.sortImportExportItems(items)]];
const sorted = shared.printSortedItems(sortedItems, items, sourceCode);
const { start } = items[0];
const { end } = items[items.length - 1];
shared.maybeReportSorting(context, sorted, start, end);
}

function maybeReportExportSpecifierSorting(node, context) {
const sorted = shared.printWithSortedSpecifiers(
node,
context.getSourceCode(),
getSpecifiers
);
const [start, end] = node.range;
shared.maybeReportSorting(context, sorted, start, end);
}

// `export * from "a"` does not have `.specifiers`.
function getSpecifiers(exportNode) {
return exportNode.specifiers || [];
}

function isPartOfChunk(node, lastNode, sourceCode) {
if (!isExportFrom(node)) {
return "NotPartOfChunk";
}

const hasGroupingComment = sourceCode
.getCommentsBefore(node)
.some(
(comment) =>
(lastNode == null || comment.loc.start.line > lastNode.loc.end.line) &&
comment.loc.end.line < node.loc.start.line
);

return hasGroupingComment ? "PartOfNewChunk" : "PartOfChunk";
}

// Full export-from statement.
// export {a, b} from "A"
// export * from "A"
// export * as A from "A"
function isExportFrom(node) {
return (
(node.type === "ExportNamedDeclaration" ||
node.type === "ExportAllDeclaration") &&
node.source != null
);
}
Loading

0 comments on commit 3d795d5

Please sign in to comment.