Skip to content

Commit

Permalink
Merge pull request #111 from blutorange/issue-110-preserver-side-effe…
Browse files Browse the repository at this point in the history
…ct-import-order

Do not reorder side effect nodes #110
  • Loading branch information
byara authored Jan 19, 2022
2 parents 41fceff + 812cbab commit 4c5fd99
Show file tree
Hide file tree
Showing 26 changed files with 1,472 additions and 368 deletions.
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,35 @@ importOrderParserPlugins: []
The plugin extracts the imports which are defined in `importOrder`. These imports are considered as _local imports_.
The imports which are not part of the `importOrder` is considered as _third party imports_.

After, the plugin sorts the _local imports_ and _third party imports_ using [natural sort algorithm](https://en.wikipedia.org/wiki/Natural_sort_order).
First, the plugin checks for
[side effect imports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#import_a_module_for_its_side_effects_only),
such as `import 'mock-fs'`. These imports often modify the global scope or apply some patches to the current
environment, which may affect other imports. To preserve potential side effects, these kind of side effect imports are
not sorted. They also behave as a barrier that other imports may not cross during the sort. So for example, let's say
you've got these imports:

```javascript
import E from 'e';
import F from 'f';
import D from 'd';
import 'c';
import B from 'b';
import A from 'a';
```

Then the first three imports are sorted and the last two imports are sorted, but all imports above `c` stay above `c`
and all imports below `c` stay below `c`, resulting in:

```javascript
import D from 'd';
import E from 'e';
import F from 'f';
import 'c';
import A from 'a';
import B from 'b';
```

Next, the plugin sorts the _local imports_ and _third party imports_ using [natural sort algorithm](https://en.wikipedia.org/wiki/Natural_sort_order).

In the end, the plugin returns final imports with _third party imports_ on top and _local imports_ at the end.

Expand Down
4 changes: 2 additions & 2 deletions docs/DEBUG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

### How to run example in the repository ?
```shell
yarn run example examples/example.ts --config .prettierrc
yarn run example examples/example.ts --config examples/.prettierrc
```

### How to debug the plugin using node `debugger` ?
You can set a `debugger` anywhere in the code and then use following command:
```shell
yarn run compile && node --inspect-brk ./node_modules/.bin/prettier --config .prettierrc --plugin lib/src/index.js examples/example.ts
yarn run compile && node --inspect-brk ./node_modules/.bin/prettier --config examples/.prettierrc --plugin lib/src/index.js examples/example.ts
```

### How to debug the unit test using `debugger` ?
Expand Down
2 changes: 1 addition & 1 deletion examples/example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import React, {
} from 'react';
import { logger } from '@core/logger';
import { reduce, debounce } from 'lodash';
import { Message } from '../Mesage';
import { Message } from '../Message';
import { createServer } from '@server/node';
import { Alert } from '@ui/Alert';
import { repeat, filter, add } from './utils';
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@
"devDependencies": {
"@types/chai": "4.2.15",
"@types/jest": "26.0.20",
"@types/node": "14.14.34",
"@types/lodash": "4.14.168",
"@types/node": "14.14.34",
"jest": "26.6.3",
"prettier": "2.3.1",
"ts-jest": "26.5.3",
Expand Down
3 changes: 3 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export const jsx: ParserPlugin = 'jsx';

export const newLineCharacters = '\n\n';

export const chunkSideEffectNode = 'side-effect-node';
export const chunkSideOtherNode = 'other-node';

/*
* Used to mark the position between RegExps,
* where the not matched imports should be placed
Expand Down
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ export interface PrettierOptions extends RequiredOptions {
importOrderSortSpecifiers: boolean;
}

export interface ImportChunk {
nodes: ImportDeclaration[];
type: string;
}

export type ImportGroups = Record<string, ImportDeclaration[]>;
export type ImportOrLine = ImportDeclaration | ExpressionStatement;

Expand Down
107 changes: 107 additions & 0 deletions src/utils/__tests__/adjust-comments-on-sorted-nodes.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { ImportDeclaration } from '@babel/types';

import { adjustCommentsOnSortedNodes } from '../adjust-comments-on-sorted-nodes';
import { getImportNodes } from '../get-import-nodes';

function leadingComments(node: ImportDeclaration): string[] {
return node.leadingComments?.map((c) => c.value) ?? [];
}

function trailingComments(node: ImportDeclaration): string[] {
return node.trailingComments?.map((c) => c.value) ?? [];
}

test('it preserves the single leading comment for each import declaration', () => {
const importNodes = getImportNodes(`
import {x} from "c";
// comment b
import {y} from "b";
// comment a
import {z} from "a";
`);
expect(importNodes).toHaveLength(3);
const finalNodes = [importNodes[2], importNodes[1], importNodes[0]];
adjustCommentsOnSortedNodes(importNodes, finalNodes);
expect(finalNodes).toHaveLength(3);
expect(leadingComments(finalNodes[0])).toEqual([' comment a']);
expect(trailingComments(finalNodes[0])).toEqual([]);
expect(leadingComments(finalNodes[1])).toEqual([' comment b']);
expect(trailingComments(finalNodes[1])).toEqual([]);
expect(leadingComments(finalNodes[2])).toEqual([]);
expect(trailingComments(finalNodes[2])).toEqual([]);
});

test('it preserves multiple leading comments for each import declaration', () => {
const importNodes = getImportNodes(`
import {x} from "c";
// comment b1
// comment b2
// comment b3
import {y} from "b";
// comment a1
// comment a2
// comment a3
import {z} from "a";
`);
expect(importNodes).toHaveLength(3);
const finalNodes = [importNodes[2], importNodes[1], importNodes[0]];
adjustCommentsOnSortedNodes(importNodes, finalNodes);
expect(finalNodes).toHaveLength(3);
expect(leadingComments(finalNodes[0])).toEqual([
' comment a1',
' comment a2',
' comment a3',
]);
expect(trailingComments(finalNodes[0])).toEqual([]);
expect(leadingComments(finalNodes[1])).toEqual([
' comment b1',
' comment b2',
' comment b3',
]);
expect(trailingComments(finalNodes[1])).toEqual([]);
expect(leadingComments(finalNodes[2])).toEqual([]);
expect(trailingComments(finalNodes[2])).toEqual([]);
});

test('it does not move comments at before all import declarations', () => {
const importNodes = getImportNodes(`
// comment c1
// comment c2
import {x} from "c";
import {y} from "b";
import {z} from "a";
`);
expect(importNodes).toHaveLength(3);
const finalNodes = [importNodes[2], importNodes[1], importNodes[0]];
adjustCommentsOnSortedNodes(importNodes, finalNodes);
expect(finalNodes).toHaveLength(3);
expect(leadingComments(finalNodes[0])).toEqual([
' comment c1',
' comment c2',
]);
expect(trailingComments(finalNodes[0])).toEqual([]);
expect(leadingComments(finalNodes[1])).toEqual([]);
expect(trailingComments(finalNodes[1])).toEqual([]);
expect(leadingComments(finalNodes[2])).toEqual([]);
expect(trailingComments(finalNodes[2])).toEqual([]);
});

test('it does not affect comments after all import declarations', () => {
const importNodes = getImportNodes(`
import {x} from "c";
import {y} from "b";
import {z} from "a";
// comment final 1
// comment final 2
`);
expect(importNodes).toHaveLength(3);
const finalNodes = [importNodes[2], importNodes[1], importNodes[0]];
adjustCommentsOnSortedNodes(importNodes, finalNodes);
expect(finalNodes).toHaveLength(3);
expect(leadingComments(finalNodes[0])).toEqual([]);
expect(trailingComments(finalNodes[0])).toEqual([]);
expect(leadingComments(finalNodes[1])).toEqual([]);
expect(trailingComments(finalNodes[1])).toEqual([]);
expect(leadingComments(finalNodes[2])).toEqual([]);
expect(trailingComments(finalNodes[2])).toEqual([]);
});
Loading

0 comments on commit 4c5fd99

Please sign in to comment.