Extra linebreak above side-effect imports in some cases.
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 🥇
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.
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.
I uploaded it to a repository if you want to give it a try:
https://github.com/MaKTaiL/test-prettier-imports
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.
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. ☺️
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.
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).
Reading the discussion above, the confusion is resolved! (Happy to reopen if I misunderstood)
@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 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 what solution are you looking for? Just avoiding the extra new line above style (side-effect) imports?
@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"..
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 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?
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 what about <UNSAFE_SIDE_EFFECTS> ? :)
For example, such group can be used in a safe way like this <UNSAFE_SIDE_EFFECTS>[.]css$
@shrpne sorry, could you open a new issue explaining what you're trying to accomplish, with desired before/after, and we can discuss there?