prettier-plugin-sort-imports icon indicating copy to clipboard operation
prettier-plugin-sort-imports copied to clipboard

Extra linebreak above side-effect imports in some cases.

Open MaKTaiL opened this issue 10 months ago • 17 comments

Your Environment

  • Prettier version: 3.5.3
  • node version: 22.14.0
  • package manager: pnpm@10
  • IDE: VSCode

Describe the bug

I'm trying to separate only type imports from the rest but the script is adding a blank line between all different imports instead.

To Reproduce

This is the import order I'm trying to use:

importOrder: ["<TYPES>", "", "<THIRD_PARTY_MODULES>", "^@/", "^[./]"]

Expected output:

import type { Metadata } from "next";

import { Geist, Geist_Mono } from "next/font/google";
import "@/app/globals.css";
import Test from "./components/test";

Actual output:

import type { Metadata } from "next";

import { Geist, Geist_Mono } from "next/font/google";

import "@/app/globals.css";

import Test from "./components/test";

Configuration File (cat .prettierrc, prettier.config.js, .prettier.js)

/**
 * @see https://prettier.io/docs/configuration
 * @type {import("prettier").Config}
 * @type {import("prettier-plugin-tailwindcss").PluginOptions}
 * @type {import("@ianvs/prettier-plugin-sort-imports").PrettierConfig}
 */
const config = {
  plugins: [
    "@ianvs/prettier-plugin-sort-imports",
    "prettier-plugin-tailwindcss",
  ],
  importOrder: ["<TYPES>", "", "<THIRD_PARTY_MODULES>", "^@/", "^[./]"],
  endOfLine: "auto",
};

export default config;

Contribute to @ianvs/prettier-plugin-sort-imports

  • [ ] I'm willing to fix this bug 🥇

MaKTaiL avatar Apr 22 '25 12:04 MaKTaiL

Something else must be causing the extra lines. I created a new project with your config settings, and I get the expected output above. To troubleshoot, try running prettier on the CLI to see if it is something in your editor, and if that still doesn't work, then maybe try starting with a blank project and slowly add your project config in until you find out what's causing it. I'd be interested to hear what it is.

IanVS avatar Apr 22 '25 12:04 IanVS

Something else must be causing the extra lines. I created a new project with your config settings, and I get the expected output above. To troubleshoot, try running prettier on the CLI to see if it is something in your editor, and if that still doesn't work, then maybe try starting with a blank project and slowly add your project config in until you find out what's causing it. I'd be interested to hear what it is.

I did that. I started a completely new blank project and prettier/eslint was the first thing I configured. I'm currently using create-next-app@latest. Using prettier from cli produces the same output.

MaKTaiL avatar Apr 22 '25 12:04 MaKTaiL

I uploaded it to a repository if you want to give it a try:

https://github.com/MaKTaiL/test-prettier-imports

MaKTaiL avatar Apr 22 '25 12:04 MaKTaiL

I'm taking a look at your test repo. The only file with more than one import is layout.tsx, but it does not have the imports as shown in this issue. If I update the file to import those files, then save in VSCode, I get this expected result:

import type { Metadata } from "next";

import { Geist, Geist_Mono } from "next/font/google";
import { send } from "@/app/functions";
import Test from "./components/test";

The reason you're seeing extra spaces is that you have a .css side effect import in the middle, so your imports are being split apart into two groups, everything above the side-effect import, and everything below, and each group is formatted according to your rules. It's easier to see with a few more imports:

import type { Metadata } from "next";

import { Geist, Geist_Mono } from "next/font/google";
import Foo from "./components/foo";

import "@/app/globalss.css";

import type { HttpsAgent } from "next";

import { bar } from "@/app/globals";
import Test from "./components/test";

I'd recommend moving your .css import to the end of your imports, and then you'll get something more like what you expect:

import type { Metadata } from "next";

import { Geist, Geist_Mono } from "next/font/google";
import Test from "./components/test";

import "@/app/globals.css";

The extra space above the side-effect import is a bit unexpected, and only seems to happen when a line separator is used anywhere in the importOrder. I'll take a look at the code and see if I can figure out why exactly that's happening. But I don't think it's intentional.

IanVS avatar Apr 22 '25 13:04 IanVS

Thanks for testing it out. Yeah, I accidently copied the example of a newer version of the layout.tsx that had the "send" function when I created the issue, sorry about that. So it seems you found out that the side-effect import is the culprit. Hopefully you can figure out why this issue is happening. ☺️

MaKTaiL avatar Apr 22 '25 13:04 MaKTaiL

I moved the side-effect import to the top of the file and everything is working as intended now. I was under the assumption that the side-effect import would fall under the "^@/" rule, that's why I had it placed in that spot for testing.

MaKTaiL avatar Apr 22 '25 13:04 MaKTaiL

Right, side-effects are never sorted, because the order they are placed in can matter (for example multiple css imports will determine the cascade order, and rearranging them can change how your app looks).

IanVS avatar Apr 22 '25 13:04 IanVS

Reading the discussion above, the confusion is resolved! (Happy to reopen if I misunderstood)

fbartho avatar May 27 '25 16:05 fbartho

@fbartho I was keeping it open due to:

The extra space above the side-effect import is a bit unexpected, and only seems to happen when a line separator is used anywhere in the importOrder. I'll take a look at the code and see if I can figure out why exactly that's happening. But I don't think it's intentional.

It's pretty minor, but it does seem to be odd behavior.

IanVS avatar May 27 '25 17:05 IanVS

@IanVS I experience the same on v4.5.1. Manually moving the .css or .scss imports to the end of the imports resolves the problem. However, for those working on a large legacy codebase (80,000+ files), this isn't a minor issue. I have to manually reorder the styles at the end of the imports for every component, which can be quite tedious.

Do you think you're able to come up with some sort of fix?

AdaptCharm avatar Aug 06 '25 00:08 AdaptCharm

@AdaptCharm what solution are you looking for? Just avoiding the extra new line above style (side-effect) imports?

IanVS avatar Aug 06 '25 01:08 IanVS

@AdaptCharm what solution are you looking for? Just avoiding the extra new line above style (side-effect) imports?

Essentially: if a style import is in the middle of other imports and there are spaces between groups (.prettierrc.json), it should be able to handle that without breaking the order.

.prettierrc.json:

{
  ...
  "importOrder": [
    "^(react/(.*)$)|^(react$)",
    "<BUILTIN_MODULES>",
    "^(react-(.*)$)",
    "<THIRD_PARTY_MODULES>",
    "",
    "^@/redux(/.*)?$",
    "",
    "^@/config/(.*)$",
    "^@/constant/(.*)$",
    "^@/lib/(.*)$",
    "^@/utils/(.*)$",
    "^@/hooks/(.*)$",
    "",
    "^@/components/(.*)$",
    "",
    "^[./].*(?<!\\.(css|scss))$",
    "\\.(css|scss)$"
  ]
}

Actual result:

import React, { useEffect, useState } from 'react'
import { connect } from 'react-redux'
import axios from 'axios'

import { deleteAllAlerts } from '@/redux/alert/alertActions'
import { setHolidays } from '@/redux/holiday/holidayActions'

import Calendar from './calendar/Calendar'
import ColorGuide from './ColorGuide'
import ConfirmTable from './confirmation/ConfirmTable'
import ManagerLeave from './manager-leave/ManagerLeave'
import RegistrationTable from './registration/RegistrationTable'
import Statistics from './statistics/Statistics'

import './AttendancePage.scss'

import _get from 'lodash/get'

import { handleDecentralization } from '@/redux/roleMatrix/roleMatrixSelector'

import retryAxiosRequests from '@/utils/retryAxiosRequests'

import Procedure from '@/components/procedure/Procedure'

import CheckinOutLog from './checkinout-log/CheckinOutLog'

Expected result:

import React, { useEffect, useState } from 'react'
import { connect } from 'react-redux'
import axios from 'axios'
import _get from 'lodash/get'

import { deleteAllAlerts } from '@/redux/alert/alertActions'
import { setHolidays } from '@/redux/holiday/holidayActions'
import { handleDecentralization } from '@/redux/roleMatrix/roleMatrixSelector'

import retryAxiosRequests from '@/utils/retryAxiosRequests'

import Procedure from '@/components/procedure/Procedure'

import Calendar from './calendar/Calendar'
import CheckinOutLog from './checkinout-log/CheckinOutLog'
import ColorGuide from './ColorGuide'
import ConfirmTable from './confirmation/ConfirmTable'
import ManagerLeave from './manager-leave/ManagerLeave'
import RegistrationTable from './registration/RegistrationTable'
import Statistics from './statistics/Statistics'

import './AttendancePage.scss'

Edit: I solved it by writing a script that moves all CSS imports to the end of the import list for each file. That said, writing a script really shouldn’t be necessary as the plugin should handle this "out of the box"..

AdaptCharm avatar Aug 06 '25 02:08 AdaptCharm

Hi, the reason this plugin was forked from Trivago's version in the first place was that I wanted a tool that did not sort side-effect imports. In general, they are not safe to re-arrange. Consider two CSS imports in the same file. Moving one above the other will change the resulting css cascade and could change the resulting styling of the app. By definition, side-effect-only imports cause side-effects, and the order of those side-effects can (and often do) matter.

This issue is about an extra newline being added above those imports in some situations, not about re-arranging them.

We have a separate issue about finding a way to opt-in to sorting side-effect imports: https://github.com/IanVS/prettier-plugin-sort-imports/issues/188

IanVS avatar Aug 06 '25 13:08 IanVS

@IanVS i first encountered such behavior and also thought that it is a bug, you clarification sorted things out, But it seems that it is not obvious from the readme.

  • Maybe more info about side effects should be addded to the readme?
  • Maybe even new <SIDE_EFFECTS> group should be introduced, so plugin could combine all side effects into one group and place it in the desired place?

shrpne avatar Dec 19 '25 20:12 shrpne

Grouping side-effect imports would not be safe, but we do now have importOrderSafeSideEffects if you want to treat some of them as safe-to-reorder. I'd be happy to review a PR to clean up / improve the README if you'd like to take a crack at it!

IanVS avatar Dec 19 '25 20:12 IanVS

@IanVS what about <UNSAFE_SIDE_EFFECTS> ? :)

For example, such group can be used in a safe way like this <UNSAFE_SIDE_EFFECTS>[.]css$

shrpne avatar Dec 19 '25 22:12 shrpne

@shrpne sorry, could you open a new issue explaining what you're trying to accomplish, with desired before/after, and we can discuss there?

IanVS avatar Dec 21 '25 21:12 IanVS