diff --git a/.prettierrc b/.prettierrc index e2aea53..62fd808 100644 --- a/.prettierrc +++ b/.prettierrc @@ -5,6 +5,13 @@ "singleQuote": true, "jsxBracketSameLine": true, "semi": true, - "importOrder": ["^@server/(.*)$", "^@core/(.*)$", "^@ui/(.*)$", "^[./]"], + "importOrder": [ + "", + "^@server/(.*)$", + "^@core/(.*)$", + "^@ui/(.*)$", + "", + "^[./]" + ], "importOrderSeparation": true } diff --git a/src/index.ts b/src/index.ts index b452933..f08fec8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,7 +23,7 @@ const options = { array: true, default: [{ value: [] }], description: 'Provide a list of plugins for special syntax', - } + }, }; module.exports = { diff --git a/src/utils/get-sorted-nodes.ts b/src/utils/get-sorted-nodes.ts index cd0f40f..3c2b76d 100644 --- a/src/utils/get-sorted-nodes.ts +++ b/src/utils/get-sorted-nodes.ts @@ -27,51 +27,41 @@ export const getSortedNodes = ( importOrderSeparation: boolean, ) => { const originalNodes = nodes.map(clone); + const newLine = importOrderSeparation && nodes.length > 1 ? newLineNode : null; - const sortedNodesByImportOrder = order.reduce( - ( - res: (ImportDeclaration | ExpressionStatement)[], - val, - ): (ImportDeclaration | ExpressionStatement)[] => { - const x = originalNodes.filter( - (node) => node.source.value.match(new RegExp(val)) !== null, - ); + const sortedNodesByImportOrder = order.reduce((res, val) => { + if (val === '') return [...res, newLineNode]; - // remove "found" imports from the list of nodes - pull(originalNodes, ...x); + const sorted = originalNodes + .filter((node) => node.source.value.match(new RegExp(val)) !== null) + .sort((a, b) => naturalSort(a.source.value, b.source.value)); - if (x.length > 0) { - x.sort((a, b) => naturalSort(a.source.value, b.source.value)); + // remove "found" imports from the list of nodes + pull(originalNodes, ...sorted); - if (res.length > 0) { - return compact([...res, newLine, ...x]); - } - return x; - } - return res; - }, - [], - ); + return compact([...res, newLine, ...sorted]); + }, [] as (ImportDeclaration | ExpressionStatement)[]); const sortedNodesNotInImportOrder = originalNodes.filter( - (node) => !isSimilarTextExistInArray(order, node.source.value), + (node) => !isSimilarTextExistInArray(compact(order), node.source.value), ); sortedNodesNotInImportOrder.sort((a, b) => naturalSort(a.source.value, b.source.value), ); - const shouldAddNewLineInBetween = - sortedNodesNotInImportOrder.length > 0 && importOrderSeparation; - const allSortedNodes = compact([ ...sortedNodesNotInImportOrder, - shouldAddNewLineInBetween ? newLineNode : null, ...sortedNodesByImportOrder, newLineNode, // insert a newline after all sorted imports ]); + // remove trailing newlines + while (allSortedNodes[0] === newLineNode) { + allSortedNodes.shift(); + } + // maintain a copy of the nodes to extract comments from const sortedNodesClone = allSortedNodes.map(clone); diff --git a/tests/ImportsSeparatedByEmptyRegex/__snapshots__/ppsi.spec.js.snap b/tests/ImportsSeparatedByEmptyRegex/__snapshots__/ppsi.spec.js.snap new file mode 100644 index 0000000..d6d545b --- /dev/null +++ b/tests/ImportsSeparatedByEmptyRegex/__snapshots__/ppsi.spec.js.snap @@ -0,0 +1,354 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`example.tsx - typescript-verify: example.tsx 1`] = ` +// @ts-nocheck +import threeLevelRelativePath from '../../../threeLevelRelativePath'; +import sameLevelRelativePath from './sameLevelRelativePath'; +import thirdParty from 'third-party'; +import React, { FC } from 'react'; +export { random } from './random'; +import oneLevelRelativePath from '../oneLevelRelativePath'; +import otherthing from '@core/otherthing'; +import twoLevelRelativePath from '../../twoLevelRelativePath'; +import component from '@ui/hello'; +interface HelloWorldProps { + name: string; +} + +const HelloWorld: FC = ({ name }) => { + return
Hello, {name}
; +}; + +export default HelloWorld; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @ts-nocheck +import React, { FC } from "react"; +import thirdParty from "third-party"; + +import otherthing from "@core/otherthing"; +import component from "@ui/hello"; + +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import sameLevelRelativePath from "./sameLevelRelativePath"; + +export { random } from "./random"; + +interface HelloWorldProps { + name: string; +} + +const HelloWorld: FC = ({ name }) => { + return
Hello, {name}
; +}; + +export default HelloWorld; + +`; + +exports[`import-export-in-between.ts - typescript-verify: import-export-in-between.ts 1`] = ` +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +import sameLevelRelativePath from "./sameLevelRelativePath"; +import thirdParty from "third-party"; +export { random } from './random'; +import c from 'c'; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import otherthing from "@core/otherthing"; +import a from 'a'; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +export default { + title: 'hello', +}; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; +import x from 'x'; + +function add(a:number,b:number) { + return a + b; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import a from "a"; +import c from "c"; +import thirdParty from "third-party"; +import x from "x"; + +import otherthing from "@core/otherthing"; +import something from "@server/something"; +import component from "@ui/hello"; + +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import sameLevelRelativePath from "./sameLevelRelativePath"; + +export { random } from "./random"; + +export default { + title: "hello", +}; + +function add(a: number, b: number) { + return a + b; +} + +`; + +exports[`import-export-only.ts - typescript-verify: import-export-only.ts 1`] = ` +import React from 'react'; +export const a = 1; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import React from "react"; + +export const a = 1; + +`; + +exports[`imports-with-comments.ts - typescript-verify: imports-with-comments.ts 1`] = ` +// I am top level comment in this file. +// I am second line of top level comment in this file. +import './commands'; + +// Comment +// Comment + +function add(a:number,b:number) { + return a + b; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// I am top level comment in this file. +// I am second line of top level comment in this file. +import "./commands"; + +// Comment +// Comment + +function add(a: number, b: number) { + return a + b; +} + +`; + +exports[`imports-with-comments-and-third-party.ts - typescript-verify: imports-with-comments-and-third-party.ts 1`] = ` +// I am top level comment in this file. +// I am second line of top level comment in this file. +import './commands'; +import React from 'react'; +// Comment +// Comment + +function add(a:number,b:number) { + return a + b; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// I am top level comment in this file. +// I am second line of top level comment in this file. +import React from "react"; + +import "./commands"; + +// Comment +// Comment + +function add(a: number, b: number) { + return a + b; +} + +`; + +exports[`imports-with-comments-on-top.ts - typescript-verify: imports-with-comments-on-top.ts 1`] = ` +// I am top level comment in this file. +// I am second line of top level comment in this file. +import z from 'z'; +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +import sameLevelRelativePath from "./sameLevelRelativePath"; +import thirdParty from "third-party"; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import otherthing from "@core/otherthing"; +import abc from "@core/abc"; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; +import xyz from "@ui/xyz"; +import qwerty from "@server/qwerty"; + +function add(a:number,b:number) { + return a + b; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// I am top level comment in this file. +// I am second line of top level comment in this file. +import thirdParty from "third-party"; +import z from "z"; + +import abc from "@core/abc"; +import otherthing from "@core/otherthing"; +import qwerty from "@server/qwerty"; +import something from "@server/something"; +import component from "@ui/hello"; +import xyz from "@ui/xyz"; + +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import sameLevelRelativePath from "./sameLevelRelativePath"; + +function add(a: number, b: number) { + return a + b; +} + +`; + +exports[`imports-with-file-level-comments.ts - typescript-verify: imports-with-file-level-comments.ts 1`] = ` +//@ts-ignore +// I am file top level comments +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +// I am stick to sameLevelRelativePath +import sameLevelRelativePath from "./sameLevelRelativePath"; +// I am stick to third party comment +import thirdParty from "third-party"; +// leading comment +import { + random // inner comment +} from './random'; +// leading comment +export { + random // inner comment +} from './random'; +import c from 'c'; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import otherthing from "@core/otherthing"; +import a from 'a'; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +export default { + title: 'hello', +}; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; +import x from 'x'; + +// I am function comment + +function add(a:number,b:number) { + return a + b; // I am inside function +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +//@ts-ignore +// I am file top level comments +import a from "a"; +import c from "c"; +// I am stick to third party comment +import thirdParty from "third-party"; +import x from "x"; + +import otherthing from "@core/otherthing"; +import something from "@server/something"; +import component from "@ui/hello"; + +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import oneLevelRelativePath from "../oneLevelRelativePath"; +// leading comment +import { + random, // inner comment +} from "./random"; +// I am stick to sameLevelRelativePath +import sameLevelRelativePath from "./sameLevelRelativePath"; + +// leading comment +export { + random, // inner comment +} from "./random"; + +export default { + title: "hello", +}; + +// I am function comment + +function add(a: number, b: number) { + return a + b; // I am inside function +} + +`; + +exports[`imports-without-third-party.ts - typescript-verify: imports-without-third-party.ts 1`] = ` +// I am top level comment +import otherthing from "@core/otherthing"; +import abc from "@core/abc"; +// I am comment +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// I am top level comment +import abc from "@core/abc"; +import otherthing from "@core/otherthing"; +import something from "@server/something"; +import component from "@ui/hello"; + +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +// I am comment +import twoLevelRelativePath from "../../twoLevelRelativePath"; + +`; + +exports[`no-import-export.ts - typescript-verify: no-import-export.ts 1`] = ` +function add(a:number,b:number) { + return a + b; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function add(a: number, b: number) { + return a + b; +} + +`; + +exports[`one-import.ts - typescript-verify: one-import.ts 1`] = ` +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands'; + +// Alternatively you can use CommonJS syntax: +// require('./commands') +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** +// Import commands.js using ES2015 syntax: +import "./commands"; + +// Alternatively you can use CommonJS syntax: +// require('./commands') + +`; diff --git a/tests/ImportsSeparatedByEmptyRegex/example.tsx b/tests/ImportsSeparatedByEmptyRegex/example.tsx new file mode 100644 index 0000000..80814fb --- /dev/null +++ b/tests/ImportsSeparatedByEmptyRegex/example.tsx @@ -0,0 +1,19 @@ +// @ts-nocheck +import threeLevelRelativePath from '../../../threeLevelRelativePath'; +import sameLevelRelativePath from './sameLevelRelativePath'; +import thirdParty from 'third-party'; +import React, { FC } from 'react'; +export { random } from './random'; +import oneLevelRelativePath from '../oneLevelRelativePath'; +import otherthing from '@core/otherthing'; +import twoLevelRelativePath from '../../twoLevelRelativePath'; +import component from '@ui/hello'; +interface HelloWorldProps { + name: string; +} + +const HelloWorld: FC = ({ name }) => { + return
Hello, {name}
; +}; + +export default HelloWorld; diff --git a/tests/ImportsSeparatedByEmptyRegex/import-export-in-between.ts b/tests/ImportsSeparatedByEmptyRegex/import-export-in-between.ts new file mode 100644 index 0000000..16bc1d7 --- /dev/null +++ b/tests/ImportsSeparatedByEmptyRegex/import-export-in-between.ts @@ -0,0 +1,20 @@ +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +import sameLevelRelativePath from "./sameLevelRelativePath"; +import thirdParty from "third-party"; +export { random } from './random'; +import c from 'c'; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import otherthing from "@core/otherthing"; +import a from 'a'; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +export default { + title: 'hello', +}; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; +import x from 'x'; + +function add(a:number,b:number) { + return a + b; +} diff --git a/tests/ImportsSeparatedByEmptyRegex/import-export-only.ts b/tests/ImportsSeparatedByEmptyRegex/import-export-only.ts new file mode 100644 index 0000000..44f7eff --- /dev/null +++ b/tests/ImportsSeparatedByEmptyRegex/import-export-only.ts @@ -0,0 +1,2 @@ +import React from 'react'; +export const a = 1; diff --git a/tests/ImportsSeparatedByEmptyRegex/imports-with-comments-and-third-party.ts b/tests/ImportsSeparatedByEmptyRegex/imports-with-comments-and-third-party.ts new file mode 100644 index 0000000..a9fb311 --- /dev/null +++ b/tests/ImportsSeparatedByEmptyRegex/imports-with-comments-and-third-party.ts @@ -0,0 +1,10 @@ +// I am top level comment in this file. +// I am second line of top level comment in this file. +import './commands'; +import React from 'react'; +// Comment +// Comment + +function add(a:number,b:number) { + return a + b; +} diff --git a/tests/ImportsSeparatedByEmptyRegex/imports-with-comments-on-top.ts b/tests/ImportsSeparatedByEmptyRegex/imports-with-comments-on-top.ts new file mode 100644 index 0000000..6b08ed0 --- /dev/null +++ b/tests/ImportsSeparatedByEmptyRegex/imports-with-comments-on-top.ts @@ -0,0 +1,19 @@ +// I am top level comment in this file. +// I am second line of top level comment in this file. +import z from 'z'; +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +import sameLevelRelativePath from "./sameLevelRelativePath"; +import thirdParty from "third-party"; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import otherthing from "@core/otherthing"; +import abc from "@core/abc"; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; +import xyz from "@ui/xyz"; +import qwerty from "@server/qwerty"; + +function add(a:number,b:number) { + return a + b; +} diff --git a/tests/ImportsSeparatedByEmptyRegex/imports-with-comments.ts b/tests/ImportsSeparatedByEmptyRegex/imports-with-comments.ts new file mode 100644 index 0000000..67b8e46 --- /dev/null +++ b/tests/ImportsSeparatedByEmptyRegex/imports-with-comments.ts @@ -0,0 +1,10 @@ +// I am top level comment in this file. +// I am second line of top level comment in this file. +import './commands'; + +// Comment +// Comment + +function add(a:number,b:number) { + return a + b; +} diff --git a/tests/ImportsSeparatedByEmptyRegex/imports-with-file-level-comments.ts b/tests/ImportsSeparatedByEmptyRegex/imports-with-file-level-comments.ts new file mode 100644 index 0000000..8ffcfb5 --- /dev/null +++ b/tests/ImportsSeparatedByEmptyRegex/imports-with-file-level-comments.ts @@ -0,0 +1,33 @@ +//@ts-ignore +// I am file top level comments +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +// I am stick to sameLevelRelativePath +import sameLevelRelativePath from "./sameLevelRelativePath"; +// I am stick to third party comment +import thirdParty from "third-party"; +// leading comment +import { + random // inner comment +} from './random'; +// leading comment +export { + random // inner comment +} from './random'; +import c from 'c'; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import otherthing from "@core/otherthing"; +import a from 'a'; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +export default { + title: 'hello', +}; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; +import x from 'x'; + +// I am function comment + +function add(a:number,b:number) { + return a + b; // I am inside function +} diff --git a/tests/ImportsSeparatedByEmptyRegex/imports-without-third-party.ts b/tests/ImportsSeparatedByEmptyRegex/imports-without-third-party.ts new file mode 100644 index 0000000..ef1797a --- /dev/null +++ b/tests/ImportsSeparatedByEmptyRegex/imports-without-third-party.ts @@ -0,0 +1,8 @@ +// I am top level comment +import otherthing from "@core/otherthing"; +import abc from "@core/abc"; +// I am comment +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; diff --git a/tests/ImportsSeparatedByEmptyRegex/no-import-export.ts b/tests/ImportsSeparatedByEmptyRegex/no-import-export.ts new file mode 100644 index 0000000..e8f71db --- /dev/null +++ b/tests/ImportsSeparatedByEmptyRegex/no-import-export.ts @@ -0,0 +1,3 @@ +function add(a:number,b:number) { + return a + b; +} diff --git a/tests/ImportsSeparatedByEmptyRegex/one-import.ts b/tests/ImportsSeparatedByEmptyRegex/one-import.ts new file mode 100644 index 0000000..35d75ba --- /dev/null +++ b/tests/ImportsSeparatedByEmptyRegex/one-import.ts @@ -0,0 +1,19 @@ +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands'; + +// Alternatively you can use CommonJS syntax: +// require('./commands') diff --git a/tests/ImportsSeparatedByEmptyRegex/ppsi.spec.js b/tests/ImportsSeparatedByEmptyRegex/ppsi.spec.js new file mode 100644 index 0000000..44d746d --- /dev/null +++ b/tests/ImportsSeparatedByEmptyRegex/ppsi.spec.js @@ -0,0 +1,11 @@ +run_spec(__dirname, ['typescript'], { + importOrder: [ + '', + '^@core/(.*)$', + '^@server/(.*)', + '^@ui/(.*)$', + '', + '^[./]', + ], + importOrderSeparation: false, +});