From d75e4c2f22c070922e281ce2c468022e563d9461 Mon Sep 17 00:00:00 2001 From: Nick Hudkins Date: Thu, 28 Apr 2022 12:19:59 -0400 Subject: [PATCH] Introduces `importOrderBuiltinModulesToTop` for Node Builtins #71 --- README.md | 10 ++++ src/constants.ts | 2 + src/preprocessor.ts | 2 + src/types.ts | 2 + .../get-all-comments-from-nodes.spec.ts | 1 + src/utils/__tests__/get-code-from-ast.spec.ts | 1 + .../get-import-nodes-matched-group.spec.ts | 2 - src/utils/__tests__/get-sorted-nodes.spec.ts | 50 +++++++++++++++++++ .../remove-nodes-from-original-code.spec.ts | 1 + src/utils/get-sorted-nodes.ts | 11 +++- 10 files changed, 79 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dd8f3ae..4a2ffde 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,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_. diff --git a/src/constants.ts b/src/constants.ts index e04f402..556a9d7 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -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'; @@ -12,6 +13,7 @@ export const newLineCharacters = '\n\n'; * where the not matched imports should be placed */ export const THIRD_PARTY_MODULES_SPECIAL_WORD = ''; +export const BUILTIN_MODULES = builtinModules.join('|'); const PRETTIER_PLUGIN_SORT_IMPORTS_NEW_LINE = 'PRETTIER_PLUGIN_SORT_IMPORTS_NEW_LINE'; diff --git a/src/preprocessor.ts b/src/preprocessor.ts index d0c06a3..cf2fa90 100644 --- a/src/preprocessor.ts +++ b/src/preprocessor.ts @@ -15,6 +15,7 @@ export function preprocessor(code: string, options: PrettierOptions) { importOrderSeparation, importOrderGroupNamespaceSpecifiers, importOrderSortSpecifiers, + importOrderBuiltinModulesToTop, } = options; const importNodes: ImportDeclaration[] = []; @@ -46,6 +47,7 @@ export function preprocessor(code: string, options: PrettierOptions) { importOrderSeparation, importOrderGroupNamespaceSpecifiers, importOrderSortSpecifiers, + importOrderBuiltinModulesToTop, }); return getCodeFromAst(allImports, code, interpreter); diff --git a/src/types.ts b/src/types.ts index 590dce5..fccd161 100644 --- a/src/types.ts +++ b/src/types.ts @@ -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; @@ -19,6 +20,7 @@ export type GetSortedNodes = ( options: Pick< PrettierOptions, | 'importOrder' + | 'importOrderBuiltinModulesToTop' | 'importOrderCaseInsensitive' | 'importOrderSeparation' | 'importOrderGroupNamespaceSpecifiers' diff --git a/src/utils/__tests__/get-all-comments-from-nodes.spec.ts b/src/utils/__tests__/get-all-comments-from-nodes.spec.ts index ff8f156..1d543aa 100644 --- a/src/utils/__tests__/get-all-comments-from-nodes.spec.ts +++ b/src/utils/__tests__/get-all-comments-from-nodes.spec.ts @@ -14,6 +14,7 @@ const getSortedImportNodes = (code: string, options?: ParserOptions) => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderBuiltinModulesToTop: false, }); }; diff --git a/src/utils/__tests__/get-code-from-ast.spec.ts b/src/utils/__tests__/get-code-from-ast.spec.ts index 23009ce..9c43721 100644 --- a/src/utils/__tests__/get-code-from-ast.spec.ts +++ b/src/utils/__tests__/get-code-from-ast.spec.ts @@ -21,6 +21,7 @@ import a from 'a'; importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderBuiltinModulesToTop: false, }); const formatted = getCodeFromAst(sortedNodes, code, null); expect(format(formatted, { parser: 'babel' })).toEqual( diff --git a/src/utils/__tests__/get-import-nodes-matched-group.spec.ts b/src/utils/__tests__/get-import-nodes-matched-group.spec.ts index 2912cb0..b7dd837 100644 --- a/src/utils/__tests__/get-import-nodes-matched-group.spec.ts +++ b/src/utils/__tests__/get-import-nodes-matched-group.spec.ts @@ -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'; diff --git a/src/utils/__tests__/get-sorted-nodes.spec.ts b/src/utils/__tests__/get-sorted-nodes.spec.ts index d488c92..71e3b73 100644 --- a/src/utils/__tests__/get-sorted-nodes.spec.ts +++ b/src/utils/__tests__/get-sorted-nodes.spec.ts @@ -14,6 +14,7 @@ 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 BY from 'BY'; import Ba from 'Ba'; import XY from 'XY'; @@ -28,6 +29,7 @@ test('it returns all sorted nodes', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderBuiltinModulesToTop: false, }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ @@ -39,6 +41,7 @@ test('it returns all sorted nodes', () => { 'c', 'g', 'k', + 'path', 't', 'x', 'z', @@ -58,6 +61,7 @@ test('it returns all sorted nodes', () => { ['c', 'cD'], ['g'], ['k', 'kE', 'kB'], + ['path'], ['tC', 'tA', 'tB'], ['x'], ['z'], @@ -72,6 +76,7 @@ test('it returns all sorted nodes case-insensitive', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderBuiltinModulesToTop: false, }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ @@ -81,6 +86,7 @@ test('it returns all sorted nodes case-insensitive', () => { 'c', 'g', 'k', + 'path', 't', 'x', 'Xa', @@ -100,6 +106,7 @@ test('it returns all sorted nodes case-insensitive', () => { ['c', 'cD'], ['g'], ['k', 'kE', 'kB'], + ['path'], ['tC', 'tA', 'tB'], ['x'], ['Xa'], @@ -116,6 +123,7 @@ test('it returns all sorted nodes with sort order', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderBuiltinModulesToTop: false, }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ @@ -123,6 +131,7 @@ test('it returns all sorted nodes with sort order', () => { 'Xa', 'c', 'g', + 'path', 'x', 'z', 'a', @@ -142,6 +151,7 @@ test('it returns all sorted nodes with sort order', () => { ['Xa'], ['c', 'cD'], ['g'], + ['path'], ['x'], ['z'], ['a'], @@ -160,10 +170,12 @@ 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', + 'path', 'x', 'Xa', 'XY', @@ -183,6 +195,7 @@ test('it returns all sorted nodes with sort order case-insensitive', () => { ).toEqual([ ['c', 'cD'], ['g'], + ['path'], ['x'], ['Xa'], ['XY'], @@ -203,12 +216,14 @@ 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', + 'path', 'x', 'z', 'a', @@ -228,6 +243,7 @@ test('it returns all sorted import nodes with sorted import specifiers', () => { ['Xa'], ['c', 'cD'], ['g'], + ['path'], ['x'], ['z'], ['a'], @@ -246,10 +262,12 @@ 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', + 'path', 'x', 'Xa', 'XY', @@ -269,6 +287,7 @@ test('it returns all sorted import nodes with sorted import specifiers with case ).toEqual([ ['c', 'cD'], ['g'], + ['path'], ['x'], ['Xa'], ['XY'], @@ -289,6 +308,7 @@ 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', @@ -296,6 +316,7 @@ test('it returns all sorted nodes with custom third party modules', () => { 'BY', 'c', 'g', + 'path', 'x', 'Xa', 'XY', @@ -313,6 +334,7 @@ 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([ @@ -325,7 +347,35 @@ test('it returns all sorted nodes with namespace specifiers at the top', () => { 'c', 'g', 'k', + '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([ + 'path', + 'BY', + 'Ba', + 'XY', + 'Xa', + 'a', + 'c', + 'g', + 'k', + 't', + 'x', + 'z', + ]); +}); diff --git a/src/utils/__tests__/remove-nodes-from-original-code.spec.ts b/src/utils/__tests__/remove-nodes-from-original-code.spec.ts index f214cf5..463291e 100644 --- a/src/utils/__tests__/remove-nodes-from-original-code.spec.ts +++ b/src/utils/__tests__/remove-nodes-from-original-code.spec.ts @@ -25,6 +25,7 @@ test('it should remove nodes from the original code', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderBuiltinModulesToTop: false, }); const allCommentsFromImports = getAllCommentsFromNodes(sortedNodes); diff --git a/src/utils/get-sorted-nodes.ts b/src/utils/get-sorted-nodes.ts index d2c8127..ba06190 100644 --- a/src/utils/get-sorted-nodes.ts +++ b/src/utils/get-sorted-nodes.ts @@ -1,7 +1,11 @@ import { addComments, removeComments } from '@babel/types'; import { clone, isEqual } from 'lodash'; -import { THIRD_PARTY_MODULES_SPECIAL_WORD, newLineNode } from '../constants'; +import { + BUILTIN_MODULES, + THIRD_PARTY_MODULES_SPECIAL_WORD, + newLineNode, +} from '../constants'; import { naturalSort } from '../natural-sort'; import { GetSortedNodes, ImportGroups, ImportOrLine } from '../types'; import { getImportNodesMatchedGroup } from './get-import-nodes-matched-group'; @@ -22,6 +26,7 @@ export const getSortedNodes: GetSortedNodes = (nodes, options) => { importOrderSeparation, importOrderSortSpecifiers, importOrderGroupNamespaceSpecifiers, + importOrderBuiltinModulesToTop, } = options; const originalNodes = nodes.map(clone); @@ -31,6 +36,10 @@ export const getSortedNodes: GetSortedNodes = (nodes, options) => { importOrder = [THIRD_PARTY_MODULES_SPECIAL_WORD, ...importOrder]; } + if (importOrderBuiltinModulesToTop) { + importOrder = [BUILTIN_MODULES, ...importOrder]; + } + const importOrderGroups = importOrder.reduce( (groups, regexp) => ({ ...groups,