lucide-icons / lucide

Beautiful & consistent icon toolkit made by the community. Open-source project and a fork of Feather Icons.
https://lucide.dev
ISC License
11.03k stars 505 forks source link

[react] Drop unsuffixed components or setup suffixed-only components in a dedicated export to prevent auto-import pollution. #1830

Open mgcrea opened 9 months ago

mgcrea commented 9 months ago

Package

Description

I've started to use lucide-react recently and the unsuffixed icons (eg. File instead of FileIcon, Link vs. LinkIcon) do mess quite a bit with my workflow as it tends to pollute auto-complete/auto-import for similarly named objects (eg. Typescript type File, or the common Link component from routers), it's confusing and I'm wasting (arguably little time) on this every time I encounter a conflict.

Ideally I think it would be wise to drop the unsuffixed exports before tagging 1.0 as it leads to duplication/confusion but I guess people might not want such breaking change.

So I'm wondering if we could have an optional export without the unsuffixed components:

Then use something like vscode typescript.preferences.autoImportFileExcludePatterns to ignore the base path with every export from polluting autocomplete.

Use cases

I would lead to a better developer experience when importing icons.

Checklist

karsa-mistmere commented 9 months ago

I'm totally in favour of dropping non-suffixed/prefixed names from our exports starting from v1.0, preferably only keeping either Lucide<IconName> or <IconName>Icon.

ericfennis commented 9 months ago

Hmm, I don't know about dropping it. It's just a matter of taste I think. Also, that means that everyone who updates to v1 should refactor their project and rename all their imports and icon components. Nobody will like that..

But I get your point. When using the library I think I ran into this a few times. So the solution I want to propose is the ability to change this by overriding the type declarations.

In your project, you could create a typing file like lucide-react.d.ts or whatever you want to name it. And include this content:

// lucide-react.d.ts
declare module "lucide-react" {
  export * from "lucide-react/dist/lucide-react.prefixed";
  // This will re-export all exports but only includes the icons with prefixes and other library types
}

In every package, we can create a new entry file that includes different import styles.

I've already tested it, and it works. But need to rewrite some things to generate multiple aliases files.

rwieruch commented 7 months ago

Thanks for writing all this up @mgcrea ! The workaround by @ericfennis would be the MVP and perhaps solve already the issue.

Regarding: "Nobody will like that.." I think this is a "do it once and get over it"-migration. In contrast, the "pollution" when using multiple libraries will stay an ongoing issue. And with the drive towards configless project setups, I'd argue that it would be a huge win to just drop unsuffxed/unprefixed icons. Also from an educational perspective, which I have, it is difficult to explain beginners the workaround.

tillka commented 3 months ago

Thanks Eric for guiding me to this issue page.

I am only every using the suffixed version of the icons, like "LinkIcon", to keep them distinguishable from other naming convention like 'Link' from the next/link package.

As you pointed out, I created the file lucide-react.d.ts in the main dir of my NextJS project. As I intent to use the suffixed version, I included the following content:

declare module 'lucide-react' {
    export * from 'lucide-react/dist/lucide-react.suffixed';
    // This will re-export all exports but only includes the icons with prefixes and other library types
}

Typescript is immediately throwing errors, like:

Module '"lucide-react"' has no exported member 'AlertCircleIcon'.

I tried to check node_modules/lucide-react/dist and it seems there is no lucide-react.suffixed folder in there (I have to say though, that I am not well aversed with handling node-modules).

Can you point me in the right direction? Thanks!

ericfennis commented 3 months ago

@tillka yeah sorry maybe I confused you a bit. This is a proposal for a fix. This is still not implemented yet.

tillka commented 3 months ago

@tillka yeah sorry maybe I confused you a bit. This is a proposal for a fix. This is still not implemented yet.

Oh, thought it was an MVP. Then, you can put me on the wait list for this feature to be implemented. ;)

jt6677 commented 2 months ago

As React libs like shadcn ui becoming more popular, component composition will be the default for many devs. I lost embarrassing amount of time on icons auto import.

jt6677 commented 2 months ago

This auto-import pollution is THE most annoying thing I run into on a daily basis. "typescript.preferences.autoImportFileExcludePatterns": ["lucide-react"], just stop exporting icons as Calendar, Section, Container. What should devs call their components?

jt6677 commented 1 month ago

"typescript.preferences.autoImportFileExcludePatterns": ["lucide-react"], then create Icons.tsx , manually import { Check, ChevronDown } from 'lucide-react' export const ChevronDownIcon = ChevronDown .. Adding a config file for a icon package to thousands of config files frontend project is not ideal. I already lost enough time because of this.

NiklasPor commented 1 week ago

So I also struggle with this, permanently. We solved with a small vite plugin that reexports only the right icons:

function LucideIconPlugin(): Plugin {
  return {
    name: "lucide-icon-plugin",
    buildStart: async () => {
      const declarations = await readFile(
        "node_modules/lucide-react/dist/lucide-react.d.ts",
        "utf-8",
      );

      const iconNames = new Set(declarations.match(/(?<=as )[a-zA-Z0-9]+Icon/g));
      const jsFile = `export * from "lucide-react/dist/esm/lucide-react";\n`;
      const declarationFile =
        'import { Icon } from "lucide-react";\n' +
        [...iconNames]!
          .map((icon) => `declare const ${icon}: import("lucide-react").LucideIcon;\n`)
          .join("");

      writeFileSync("libs/components/icons.gen.d.ts", declarationFile);
      writeFileSync("libs/components/icons.gen.js", jsFile);
    },
  };
}

This will create:

// icons.gen.js
export * from "lucide-react/dist/esm/lucide-react";
// icons.gen.d.ts
declare const AArrowDownIcon: import("lucide-react").LucideIcon;
declare const AArrowUpIcon: import("lucide-react").LucideIcon;
declare const ALargeSmallIcon: import("lucide-react").LucideIcon;
declare const AccessibilityIcon: import("lucide-react").LucideIcon;
declare const ActivityIcon: import("lucide-react").LucideIcon;
...

Now just ignore the lucide-react imports in VSCode:

{
  "typescript.preferences.autoImportFileExcludePatterns": [
    "lucide-react"
  ]
}

And now TS suggestions only show the icons with suffix: image