Skip to content

Commit

Permalink
Introduce importOrderBuiltinModulesToTop for Node Builtins (#10)
Browse files Browse the repository at this point in the history
This is a cherry-pick of a PR from @nickhudkins to the upstream project: trivago/prettier-plugin-sort-imports#150, adjusted slightly to work within the rule of this project to not sort beyond side-effect import groups.  

@blutorange what do you think of this?  I'm reluctant to add another option, but if we changed this without an option it would be a breaking change.  I don't have a strong opinion one way or the other, but personally I do like sorting builtins to the top, so it would be nice to have some way to do it.

Co-authored-by: Nick Hudkins <[email protected]>
  • Loading branch information
IanVS and Nick Hudkins authored May 12, 2022
1 parent 36c916a commit 54b3cf5
Show file tree
Hide file tree
Showing 11 changed files with 143 additions and 3 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,16 @@ with options as a JSON string of the plugin array:
importOrderParserPlugins: []
```


#### `importOrderBuiltinModulesToTop`

**type**: `boolean`

**default value:** `false`

A boolean value to enable sorting of builtins to the top of all import groups.


### How does import sort work ?

The plugin extracts the imports which are defined in `importOrder`. These imports are considered as _local imports_.
Expand Down
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ParserPlugin } from '@babel/parser';
import { expressionStatement, stringLiteral } from '@babel/types';
import { builtinModules } from 'module';

export const flow: ParserPlugin = 'flow';
export const typescript: ParserPlugin = 'typescript';
Expand All @@ -15,6 +16,7 @@ export const chunkTypeOther = 'other';
* where the not matched imports should be placed
*/
export const THIRD_PARTY_MODULES_SPECIAL_WORD = '<THIRD_PARTY_MODULES>';
export const BUILTIN_MODULES = builtinModules.join('|');

const PRETTIER_PLUGIN_SORT_IMPORTS_NEW_LINE =
'PRETTIER_PLUGIN_SORT_IMPORTS_NEW_LINE';
Expand Down
2 changes: 2 additions & 0 deletions src/preprocessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export function preprocessor(code: string, options: PrettierOptions): string {
importOrderSeparation,
importOrderGroupNamespaceSpecifiers,
importOrderSortSpecifiers,
importOrderBuiltinModulesToTop,
} = options;

const importNodes: ImportDeclaration[] = [];
Expand Down Expand Up @@ -50,6 +51,7 @@ export function preprocessor(code: string, options: PrettierOptions): string {
importOrderSeparation,
importOrderGroupNamespaceSpecifiers,
importOrderSortSpecifiers,
importOrderBuiltinModulesToTop,
});

return getCodeFromAst(allImports, code, directives, interpreter);
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { RequiredOptions } from 'prettier';
export interface PrettierOptions extends RequiredOptions {
importOrder: string[];
importOrderCaseInsensitive: boolean;
importOrderBuiltinModulesToTop: boolean;
// should be of type ParserPlugin from '@babel/parser' but prettier does not support nested arrays in options
importOrderParserPlugins: string[];
importOrderSeparation: boolean;
Expand All @@ -24,6 +25,7 @@ export type GetSortedNodes = (
options: Pick<
PrettierOptions,
| 'importOrder'
| 'importOrderBuiltinModulesToTop'
| 'importOrderCaseInsensitive'
| 'importOrderSeparation'
| 'importOrderGroupNamespaceSpecifiers'
Expand Down
1 change: 1 addition & 0 deletions src/utils/__tests__/get-all-comments-from-nodes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const getSortedImportNodes = (code: string, options?: ParserOptions) => {
importOrderSeparation: false,
importOrderGroupNamespaceSpecifiers: false,
importOrderSortSpecifiers: false,
importOrderBuiltinModulesToTop: false,
});
};

Expand Down
1 change: 1 addition & 0 deletions src/utils/__tests__/get-code-from-ast.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import a from 'a';
importOrderSeparation: false,
importOrderGroupNamespaceSpecifiers: false,
importOrderSortSpecifiers: false,
importOrderBuiltinModulesToTop: false,
});
const formatted = getCodeFromAst(sortedNodes, code, [], undefined);
expect(format(formatted, { parser: 'babel' })).toEqual(
Expand Down
2 changes: 0 additions & 2 deletions src/utils/__tests__/get-import-nodes-matched-group.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { THIRD_PARTY_MODULES_SPECIAL_WORD } from '../../constants';
import { ImportGroups } from '../../types';
import { getImportNodes } from '../get-import-nodes';
import { getImportNodesMatchedGroup } from '../get-import-nodes-matched-group';

Expand Down
110 changes: 110 additions & 0 deletions src/utils/__tests__/get-sorted-nodes-by-import-order.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import { tC, tA, tB } from 't';
import k, { kE, kB } from 'k';
import * as a from 'a';
import * as x from 'x';
import path from 'path';
import url from 'node:url';
import * as fs from "node:fs/promises"
import BY from 'BY';
import Ba from 'Ba';
import XY from 'XY';
Expand All @@ -28,6 +31,7 @@ test('it returns all sorted nodes', () => {
importOrderSeparation: false,
importOrderGroupNamespaceSpecifiers: false,
importOrderSortSpecifiers: false,
importOrderBuiltinModulesToTop: false,
}) as ImportDeclaration[];

expect(getSortedNodesNames(sorted)).toEqual([
Expand All @@ -39,6 +43,9 @@ test('it returns all sorted nodes', () => {
'c',
'g',
'k',
'node:fs/promises',
'node:url',
'path',
't',
'x',
'z',
Expand All @@ -58,6 +65,9 @@ test('it returns all sorted nodes', () => {
['c', 'cD'],
['g'],
['k', 'kE', 'kB'],
['fs'],
['url'],
['path'],
['tC', 'tA', 'tB'],
['x'],
['z'],
Expand All @@ -72,6 +82,7 @@ test('it returns all sorted nodes case-insensitive', () => {
importOrderSeparation: false,
importOrderGroupNamespaceSpecifiers: false,
importOrderSortSpecifiers: false,
importOrderBuiltinModulesToTop: false,
}) as ImportDeclaration[];

expect(getSortedNodesNames(sorted)).toEqual([
Expand All @@ -81,6 +92,9 @@ test('it returns all sorted nodes case-insensitive', () => {
'c',
'g',
'k',
'node:fs/promises',
'node:url',
'path',
't',
'x',
'Xa',
Expand All @@ -100,6 +114,9 @@ test('it returns all sorted nodes case-insensitive', () => {
['c', 'cD'],
['g'],
['k', 'kE', 'kB'],
['fs'],
['url'],
['path'],
['tC', 'tA', 'tB'],
['x'],
['Xa'],
Expand All @@ -116,13 +133,17 @@ test('it returns all sorted nodes with sort order', () => {
importOrderSeparation: false,
importOrderGroupNamespaceSpecifiers: false,
importOrderSortSpecifiers: false,
importOrderBuiltinModulesToTop: false,
}) as ImportDeclaration[];

expect(getSortedNodesNames(sorted)).toEqual([
'XY',
'Xa',
'c',
'g',
'node:fs/promises',
'node:url',
'path',
'x',
'z',
'a',
Expand All @@ -142,6 +163,9 @@ test('it returns all sorted nodes with sort order', () => {
['Xa'],
['c', 'cD'],
['g'],
['fs'],
['url'],
['path'],
['x'],
['z'],
['a'],
Expand All @@ -160,10 +184,14 @@ test('it returns all sorted nodes with sort order case-insensitive', () => {
importOrderSeparation: false,
importOrderGroupNamespaceSpecifiers: false,
importOrderSortSpecifiers: false,
importOrderBuiltinModulesToTop: false,
}) as ImportDeclaration[];
expect(getSortedNodesNames(sorted)).toEqual([
'c',
'g',
'node:fs/promises',
'node:url',
'path',
'x',
'Xa',
'XY',
Expand All @@ -183,6 +211,9 @@ test('it returns all sorted nodes with sort order case-insensitive', () => {
).toEqual([
['c', 'cD'],
['g'],
['fs'],
['url'],
['path'],
['x'],
['Xa'],
['XY'],
Expand All @@ -203,12 +234,16 @@ test('it returns all sorted import nodes with sorted import specifiers', () => {
importOrderSeparation: false,
importOrderGroupNamespaceSpecifiers: false,
importOrderSortSpecifiers: true,
importOrderBuiltinModulesToTop: false,
}) as ImportDeclaration[];
expect(getSortedNodesNames(sorted)).toEqual([
'XY',
'Xa',
'c',
'g',
'node:fs/promises',
'node:url',
'path',
'x',
'z',
'a',
Expand All @@ -228,6 +263,9 @@ test('it returns all sorted import nodes with sorted import specifiers', () => {
['Xa'],
['c', 'cD'],
['g'],
['fs'],
['url'],
['path'],
['x'],
['z'],
['a'],
Expand All @@ -246,10 +284,14 @@ test('it returns all sorted import nodes with sorted import specifiers with case
importOrderSeparation: false,
importOrderGroupNamespaceSpecifiers: false,
importOrderSortSpecifiers: true,
importOrderBuiltinModulesToTop: false,
}) as ImportDeclaration[];
expect(getSortedNodesNames(sorted)).toEqual([
'c',
'g',
'node:fs/promises',
'node:url',
'path',
'x',
'Xa',
'XY',
Expand All @@ -269,6 +311,9 @@ test('it returns all sorted import nodes with sorted import specifiers with case
).toEqual([
['c', 'cD'],
['g'],
['fs'],
['url'],
['path'],
['x'],
['Xa'],
['XY'],
Expand All @@ -289,13 +334,17 @@ test('it returns all sorted nodes with custom third party modules', () => {
importOrderCaseInsensitive: true,
importOrderGroupNamespaceSpecifiers: false,
importOrderSortSpecifiers: false,
importOrderBuiltinModulesToTop: false,
}) as ImportDeclaration[];
expect(getSortedNodesNames(sorted)).toEqual([
'a',
'Ba',
'BY',
'c',
'g',
'node:fs/promises',
'node:url',
'path',
'x',
'Xa',
'XY',
Expand All @@ -313,10 +362,12 @@ test('it returns all sorted nodes with namespace specifiers at the top', () => {
importOrderSeparation: false,
importOrderGroupNamespaceSpecifiers: true,
importOrderSortSpecifiers: false,
importOrderBuiltinModulesToTop: false,
}) as ImportDeclaration[];

expect(getSortedNodesNames(sorted)).toEqual([
'a',
'node:fs/promises',
'x',
'BY',
'Ba',
Expand All @@ -325,7 +376,66 @@ test('it returns all sorted nodes with namespace specifiers at the top', () => {
'c',
'g',
'k',
'node:url',
'path',
't',
'z',
]);
});

test('it returns all sorted nodes with builtin specifiers at the top, ', () => {
const result = getImportNodes(code);
const sorted = getSortedNodes(result, {
importOrder: [],
importOrderCaseInsensitive: false,
importOrderSeparation: false,
importOrderGroupNamespaceSpecifiers: false,
importOrderSortSpecifiers: false,
importOrderBuiltinModulesToTop: true,
}) as ImportDeclaration[];

expect(getSortedNodesNames(sorted)).toEqual([
'node:fs/promises',
'node:url',
'path',
'BY',
'Ba',
'XY',
'Xa',
'a',
'c',
'g',
'k',
't',
'x',
'z',
]);
});

test('it returns all sorted nodes with custom third party modules and builtins at top', () => {
const result = getImportNodes(code);
const sorted = getSortedNodes(result, {
importOrder: ['^a$', '<THIRD_PARTY_MODULES>', '^t$', '^k$'],
importOrderSeparation: false,
importOrderCaseInsensitive: true,
importOrderGroupNamespaceSpecifiers: false,
importOrderSortSpecifiers: false,
importOrderBuiltinModulesToTop: true,
}) as ImportDeclaration[];
expect(getSortedNodesNames(sorted)).toEqual([
'node:fs/promises',
'node:url',
'path',
'a',
'Ba',
'BY',
'c',
'g',
'x',
'Xa',
'XY',
'z',
't',
'k',
]);
});
4 changes: 4 additions & 0 deletions src/utils/__tests__/get-sorted-nodes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import "se4";
import "se1";
import * as a from 'a';
import * as x from 'x';
import path from 'path';
import BY from 'BY';
import Ba from 'Ba';
import XY from 'XY';
Expand All @@ -32,6 +33,7 @@ test('it returns all sorted nodes, preserving the order side effect nodes', () =
importOrderSeparation: false,
importOrderGroupNamespaceSpecifiers: false,
importOrderSortSpecifiers: false,
importOrderBuiltinModulesToTop: false,
}) as ImportDeclaration[];

expect(getSortedNodesNames(sorted)).toEqual([
Expand All @@ -48,6 +50,7 @@ test('it returns all sorted nodes, preserving the order side effect nodes', () =
'XY',
'Xa',
'a',
'path',
'x',
'se2',
]);
Expand All @@ -71,6 +74,7 @@ test('it returns all sorted nodes, preserving the order side effect nodes', () =
['XY'],
['Xa'],
['a'],
['path'],
['x'],
[],
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ test('it should remove nodes from the original code', () => {
importOrderSeparation: false,
importOrderGroupNamespaceSpecifiers: false,
importOrderSortSpecifiers: false,
importOrderBuiltinModulesToTop: false,
});
const allCommentsFromImports = getAllCommentsFromNodes(sortedNodes);

Expand Down
Loading

0 comments on commit 54b3cf5

Please sign in to comment.