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

Feature: Hook to Normalize Import Names

Open fbartho opened this issue 7 months ago • 2 comments

Is your feature request related to a problem?

VSCode and Copilot sometimes fight to insert a dependency when I’m auto-completing a type. And they inject either multiple references, or a relative reference to a different package in my repo instead of a package-scoped import.

import { a } from “@repo/tools”;
Import { b } from “../../../../packages/tools/b”; // this should just import from `@repo/tools`

Describe the solution you'd like

When using prettier.config.[c|m]js we could offer a hook to normalize an import expression’s path. The user would be able to write arbitrary code to transform the import expression before it gets formatted.

Describe alternatives you've considered

I could monitor this with an external developer tool, but I’d have to monitor all source files in my repo, and this seems wasteful, and annoying to replicate the regex to extract the dependencies and patch their paths.

fbartho avatar May 28 '25 18:05 fbartho

That's an interesting thought. How would you envision this working within the project? What might the api of such a hook look like?

IanVS avatar May 28 '25 19:05 IanVS

I was imagining, user provides a function.

The signature would be something like the following (using typescript for convenience though this would be JSDoc/JS in practice).

interface ImportRewriteHookParams {
  /** The file being parsed/sorted */
  relativePath: string;
  /** The current source. Eg. ../libs/package/bar */
  importSource: string;
} & Record<string, unknown>;
interface ImportRewriteHookResultRewriteNeeded {
  kind: “rewrite”;
  importSource: string;
}
type ImportRewriteHookResultChangeNothing = undefined;
type ImportRewriteHookResult = ImportRewriteHookResultRewriteNeeded | ImportRewriteHookResultChangeNothing;

type ImportRewriteHook = (params: ImportRewriteHookParams) => ImportRewriteHookResult;

This is pretty forwards-compatible, which makes it more verbose than maybe needed.

A simpler type that would be less forward compatible would look like:

interface ImportRewriteHookParams {
  /** The file being parsed/sorted */
  relativePath: string;
  /** The current source. Eg. ../libs/package/bar */
  importSource: string;
}

type ImportRewriteHook = (params: ImportRewriteHookParams) => string | undefined;

In practice in my prettier.config.js:

export default {
  …
  plugins: [ require(“@ianvs/prettier-plugin-sort-imports”).customizeImports(myHook) ]
  …
}
function myHook({importSource}) {
  if importSource.matches(/…/) {
    return {kind: “rewrite”, importSource: importSource.replace(…) };
  }
}

How does that sound?

fbartho avatar May 28 '25 19:05 fbartho