Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow disabling plugin by setting importOrder to [] #161

Merged
merged 4 commits into from
Mar 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ This project is based on [@trivago/prettier-plugin-sort-imports](https://github.
- [4. Group type imports separately from values](#4-group-type-imports-separately-from-values)
- [5. Group aliases with local imports](#5-group-aliases-with-local-imports)
- [6. Enforce a blank line after top of file comments](#6-enforce-a-blank-line-after-top-of-file-comments)
- [7. Enforce sort order only in certain folders or files](#7-enforce-sort-order-only-in-certain-folders-or-files)
- [`importOrderTypeScriptVersion`](#importordertypescriptversion)
- [`importOrderParserPlugins`](#importorderparserplugins)
- [Prevent imports from being sorted](#prevent-imports-from-being-sorted)
Expand Down Expand Up @@ -332,6 +333,24 @@ import icon from '@assets/icon';
import App from './App';
```

##### 7. Enforce sort order only in certain folders or files

If you'd like to sort the imports only in a specific set of files or directories, you can disable the plugin by setting `importOrder` to an empty array, and then use Prettier's [Configuration Overrides](https://prettier.io/docs/en/configuration#configuration-overrides) to set the order for files matching a glob pattern.

This can also be beneficial for large projects wishing to gradually adopt a sort order in a less disruptive approach than a single big-bang change.

```json
"importOrder": []
"overrides": [
{
"files": "**/*.test.ts",
"options": {
"importOrder": [ "^vitest", "<THIRD_PARTY_MODULES>", "^[.]" ]
}
}
]
```

#### `importOrderTypeScriptVersion`

**type**: `string`
Expand Down
7 changes: 7 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,10 @@ export const forceANewlineUsingACommentStatement = () => ({

export const injectNewlinesRegex =
/("PRETTIER_PLUGIN_SORT_IMPORTS_NEW_LINE";|\/\/PRETTIER_PLUGIN_SORT_IMPORTS_NEWLINE_COMMENT)/gi;

// This default is set by prettier itself by including it in our config in index.ts
export const DEFAULT_IMPORT_ORDER = [
BUILTIN_MODULES_SPECIAL_WORD,
THIRD_PARTY_MODULES_SPECIAL_WORD, // Everything not matching relative imports
'^[.]', // relative imports
];
11 changes: 2 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { parsers as typescriptParsers } from 'prettier/parser-typescript';

import {
BUILTIN_MODULES_SPECIAL_WORD,
DEFAULT_IMPORT_ORDER,
THIRD_PARTY_MODULES_SPECIAL_WORD,
} from './constants';
import { defaultPreprocessor } from './preprocessors/default';
Expand All @@ -29,15 +30,7 @@ export const options: Record<
type: 'path',
category: 'Global',
array: true,
default: [
{
value: [
BUILTIN_MODULES_SPECIAL_WORD,
THIRD_PARTY_MODULES_SPECIAL_WORD, // Everything not matching relative imports
'^[.]', // relative imports
],
},
],
default: [{ value: DEFAULT_IMPORT_ORDER }],
description:
'Provide an order to sort imports. [node.js built-ins are always first]',
},
Expand Down
5 changes: 5 additions & 0 deletions src/preprocessors/preprocessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ export function preprocessor(code: string, options: PrettierOptions): string {
return code;
}

// short-circuit if importOrder is an empty array (can be used to disable plugin)
if (!remainingOptions.importOrder.length) {
return code;
}

const nodesToOutput = getSortedNodes(
allOriginalImportNodes,
remainingOptions,
Expand Down
4 changes: 3 additions & 1 deletion src/utils/__tests__/get-all-comments-from-nodes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
} from '@babel/types';
import { expect, test } from 'vitest';

import { DEFAULT_IMPORT_ORDER } from '../../constants';
import { getAllCommentsFromNodes } from '../get-all-comments-from-nodes';
import { getImportNodes } from '../get-import-nodes';
import { getSortedNodes } from '../get-sorted-nodes';
Expand All @@ -15,7 +16,8 @@ const getSortedImportNodes = (code: string, options?: ParserOptions) => {
const importNodes: ImportDeclaration[] = getImportNodes(code, options);

return getSortedNodes(importNodes, {
importOrder: testingOnly.normalizeImportOrderOption([]),
importOrder:
testingOnly.normalizeImportOrderOption(DEFAULT_IMPORT_ORDER),
importOrderCombineTypeAndValueImports: true,
});
};
Expand Down
8 changes: 5 additions & 3 deletions src/utils/__tests__/get-code-from-ast.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { format } from 'prettier';
import { expect, test } from 'vitest';

import { DEFAULT_IMPORT_ORDER } from '../../constants';
import { getCodeFromAst } from '../get-code-from-ast';
import { getImportNodes } from '../get-import-nodes';
import { getSortedNodes } from '../get-sorted-nodes';
import { testingOnly } from '../normalize-plugin-options';

const emptyImportOrder = testingOnly.normalizeImportOrderOption([]);
const defaultImportOrder =
testingOnly.normalizeImportOrderOption(DEFAULT_IMPORT_ORDER);

test('sorts imports correctly', async () => {
const code = `import z from 'z';
Expand All @@ -18,7 +20,7 @@ import a from 'a';
`;
const importNodes = getImportNodes(code);
const sortedNodes = getSortedNodes(importNodes, {
importOrder: emptyImportOrder,
importOrder: defaultImportOrder,
importOrderCombineTypeAndValueImports: true,
});
const formatted = getCodeFromAst({
Expand Down Expand Up @@ -50,7 +52,7 @@ import type {See} from 'c';
`;
const importNodes = getImportNodes(code, { plugins: ['typescript'] });
const sortedNodes = getSortedNodes(importNodes, {
importOrder: emptyImportOrder,
importOrder: defaultImportOrder,
importOrderCombineTypeAndValueImports: true,
});
const formatted = getCodeFromAst({
Expand Down
4 changes: 3 additions & 1 deletion src/utils/__tests__/get-sorted-nodes.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ImportDeclaration } from '@babel/types';
import { expect, test } from 'vitest';

import { DEFAULT_IMPORT_ORDER } from '../../constants';
import { getImportNodes } from '../get-import-nodes';
import { getSortedNodes } from '../get-sorted-nodes';
import { getSortedNodesModulesNames } from '../get-sorted-nodes-modules-names';
Expand Down Expand Up @@ -29,7 +30,8 @@ import "se2";
test('it returns all sorted nodes, preserving the order side effect nodes', () => {
const result = getImportNodes(code);
const sorted = getSortedNodes(result, {
importOrder: testingOnly.normalizeImportOrderOption([]),
importOrder:
testingOnly.normalizeImportOrderOption(DEFAULT_IMPORT_ORDER),
importOrderCombineTypeAndValueImports: true,
}) as ImportDeclaration[];
expect(getSortedNodesNamesAndNewlines(sorted)).toEqual([
Expand Down
38 changes: 30 additions & 8 deletions src/utils/__tests__/normalize-plugin-options.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { describe, expect, test } from 'vitest';
import {
BUILTIN_MODULES_REGEX_STR,
BUILTIN_MODULES_SPECIAL_WORD,
DEFAULT_IMPORT_ORDER,
THIRD_PARTY_MODULES_SPECIAL_WORD,
} from '../../constants';
import { NormalizableOptions } from '../../types';
Expand All @@ -12,11 +13,11 @@ import {
} from '../normalize-plugin-options';

describe('normalizeImportOrderOption', () => {
test('it should not inject defaults if [] is passed explicitly', () => {
expect(testingOnly.normalizeImportOrderOption([])).toEqual([]);
});

test('it should inject required modules if not present', () => {
expect(testingOnly.normalizeImportOrderOption([])).toEqual([
BUILTIN_MODULES_REGEX_STR,
THIRD_PARTY_MODULES_SPECIAL_WORD,
]);
expect(testingOnly.normalizeImportOrderOption(['^[.]'])).toEqual([
BUILTIN_MODULES_REGEX_STR,
THIRD_PARTY_MODULES_SPECIAL_WORD,
Expand Down Expand Up @@ -93,7 +94,7 @@ describe('examineAndNormalizePluginOptions', () => {
test('it should set most defaults', () => {
expect(
examineAndNormalizePluginOptions({
importOrder: [],
importOrder: DEFAULT_IMPORT_ORDER,
importOrderParserPlugins: [],
importOrderTypeScriptVersion: '1.0.0',
filepath: __filename,
Expand All @@ -103,6 +104,7 @@ describe('examineAndNormalizePluginOptions', () => {
importOrder: [
BUILTIN_MODULES_REGEX_STR,
THIRD_PARTY_MODULES_SPECIAL_WORD,
'^[.]',
],
importOrderCombineTypeAndValueImports: true,
plugins: [],
Expand Down Expand Up @@ -158,7 +160,7 @@ describe('examineAndNormalizePluginOptions', () => {
test('it should detect typescript-version-dependent-flags', () => {
expect(
examineAndNormalizePluginOptions({
importOrder: [],
importOrder: DEFAULT_IMPORT_ORDER,
importOrderParserPlugins: ['typescript'],
importOrderTypeScriptVersion: '5.0.0',
filepath: __filename,
Expand All @@ -168,6 +170,7 @@ describe('examineAndNormalizePluginOptions', () => {
importOrder: [
BUILTIN_MODULES_REGEX_STR,
THIRD_PARTY_MODULES_SPECIAL_WORD,
'^[.]',
],
importOrderCombineTypeAndValueImports: true,
plugins: ['typescript'],
Expand All @@ -178,7 +181,7 @@ describe('examineAndNormalizePluginOptions', () => {
// full tests for getExperimentalParserPlugins is in its own spec file
expect(
examineAndNormalizePluginOptions({
importOrder: [],
importOrder: DEFAULT_IMPORT_ORDER,
importOrderParserPlugins: ['typescript', 'jsx'],
importOrderTypeScriptVersion: '5.0.0',
filepath: __filename,
Expand All @@ -188,6 +191,7 @@ describe('examineAndNormalizePluginOptions', () => {
importOrder: [
BUILTIN_MODULES_REGEX_STR,
THIRD_PARTY_MODULES_SPECIAL_WORD,
'^[.]',
],
importOrderCombineTypeAndValueImports: true,
plugins: ['typescript'],
Expand All @@ -197,7 +201,7 @@ describe('examineAndNormalizePluginOptions', () => {
test('it should not have a problem with a missing filepath', () => {
expect(
examineAndNormalizePluginOptions({
importOrder: [],
importOrder: DEFAULT_IMPORT_ORDER,
importOrderParserPlugins: [],
importOrderTypeScriptVersion: '1.0.0',
filepath: undefined,
Expand All @@ -207,10 +211,28 @@ describe('examineAndNormalizePluginOptions', () => {
importOrder: [
BUILTIN_MODULES_REGEX_STR,
THIRD_PARTY_MODULES_SPECIAL_WORD,
'^[.]',
],
importOrderCombineTypeAndValueImports: true,
plugins: [],
provideGapAfterTopOfFileComments: false,
});
});

test('it should be disabled if importOrder is empty array', () => {
expect(
examineAndNormalizePluginOptions({
importOrder: [],
importOrderParserPlugins: [],
importOrderTypeScriptVersion: '1.0.0',
filepath: __filename,
} as NormalizableOptions),
).toEqual({
hasAnyCustomGroupSeparatorsInImportOrder: false,
importOrder: [],
importOrderCombineTypeAndValueImports: true,
plugins: [],
provideGapAfterTopOfFileComments: false,
});
});
});
4 changes: 3 additions & 1 deletion src/utils/__tests__/remove-nodes-from-original-code.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { parse as babelParser } from '@babel/parser';
import { format } from 'prettier';
import { expect, test } from 'vitest';

import { DEFAULT_IMPORT_ORDER } from '../../constants';
import { getAllCommentsFromNodes } from '../get-all-comments-from-nodes';
import { getImportNodes } from '../get-import-nodes';
import { getSortedNodes } from '../get-sorted-nodes';
Expand All @@ -24,7 +25,8 @@ test('it should remove nodes from the original code', async () => {
const ast = babelParser(code, { sourceType: 'module' });
const importNodes = getImportNodes(code);
const sortedNodes = getSortedNodes(importNodes, {
importOrder: testingOnly.normalizeImportOrderOption([]),
importOrder:
testingOnly.normalizeImportOrderOption(DEFAULT_IMPORT_ORDER),
importOrderCombineTypeAndValueImports: true,
});
const allCommentsFromImports = getAllCommentsFromNodes(sortedNodes);
Expand Down
9 changes: 7 additions & 2 deletions src/utils/normalize-plugin-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@ import {
import { ExtendedOptions, NormalizableOptions } from '../types';
import { getExperimentalParserPlugins } from './get-experimental-parser-plugins';

// If importOrder is not set in the config, it will be pre-populated with the default before it hits this
// function. This means we should never get something like `undefined`, and if we see a config of `[]`,
// someone set that explicitly in their config.
function normalizeImportOrderOption(
importOrder: NormalizableOptions['importOrder'],
) {
if (importOrder == null) {
importOrder = [];
// Allow disabling by setting an empty array, short-circuit
if (Array.isArray(importOrder) && !importOrder.length) {
return importOrder;
}

importOrder = [...importOrder]; // Clone the array so we can splice it

// If we have a separator in the first slot, we need to inject our required words after it.
Expand Down
20 changes: 20 additions & 0 deletions tests/Disabled/__snapshots__/ppsi.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`mess.ts - typescript-verify > mess.ts 1`] = `
import z from 'z';
import { isEmpty } from "lodash-es";
import threeLevelRelativePath from "../../../threeLevelRelativePath";
import sameLevelRelativePath from "./sameLevelRelativePath";
import thirdParty from "third-party";
import oneLevelRelativePath from "../oneLevelRelativePath";
import path from "path";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
import z from "z";
import { isEmpty } from "lodash-es";
import threeLevelRelativePath from "../../../threeLevelRelativePath";
import sameLevelRelativePath from "./sameLevelRelativePath";
import thirdParty from "third-party";
import oneLevelRelativePath from "../oneLevelRelativePath";
import path from "path";

`;
7 changes: 7 additions & 0 deletions tests/Disabled/mess.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import z from 'z';
import { isEmpty } from "lodash-es";
import threeLevelRelativePath from "../../../threeLevelRelativePath";
import sameLevelRelativePath from "./sameLevelRelativePath";
import thirdParty from "third-party";
import oneLevelRelativePath from "../oneLevelRelativePath";
import path from "path";
5 changes: 5 additions & 0 deletions tests/Disabled/ppsi.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {run_spec} from '../../test-setup/run_spec';

run_spec(__dirname, ["typescript"], {
importOrder: [],
});
12 changes: 7 additions & 5 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ export interface PluginConfig {
* A collection of Regular expressions in string format.
*
* ```json
* "importOrder": ["^@core/(.*)$", "^@server/(.*)$", "^@ui/(.*)$", "^[./]"],
* "importOrder": ["^@core/(.*)$", "^@server/(.*)$", "^@ui/(.*)$", "^[.]"],
* ```
*
* _Default:_ `[]`
* _Default:_ `["<BUILTIN_MODULES>"", "<THIRD_PARTY_MODULES>", "^[.]"]`
*
* By default, this plugin will not move any imports.
* To separate third party from relative imports, use `["^[./]"]`.
* This will become the default in the next major version.
* By default, this plugin will sort node.js built-in modules to the top, followed by non-relative
* imports (usually third-party modules), and finally relative imports.
*
* `<THIRD_PARTY_MODULES>` is a special value that will match any imports not matched by any other regex patterns.
* We'll call them "third party imports" for simplicity, since that's what they usually are.
*
* The plugin moves the third party imports to the top which are not part of the `importOrder` list.
* To move the third party imports at desired place,
Expand Down
Loading