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

Multiple compiler support? #253

Open
3 tasks done
SegaraRai opened this issue Nov 26, 2022 · 10 comments
Open
3 tasks done

Multiple compiler support? #253

SegaraRai opened this issue Nov 26, 2022 · 10 comments
Labels
enhancement New feature or request

Comments

@SegaraRai
Copy link

Clear and concise description of the problem

Especially in a meta-framework like Astro, where we can mix frameworks, we may want to import icons as Astro components in the .astro file and as Vue components in the .vue file.
Currently we can retrieve raw SVG text using ?raw query, but I think it would be nice to have a more flexible choice of formats.

Suggested solution

If an extension is specified, use the appropriate compiler for that extension.
This would be a breaking change, so we need to make this feature opt-in.

examples:

Icons({
  useExtension: false, // default
})

Icons({
  useExtension: true,
})

Icons({
  useExtension: {
    // custom overrides
    '.vue': 'vue2',
  },
})

Alternative

Select compiler by ?compiler=astro query.
In this solution, we probably do not need to make any changes to the options.

Additional context

No response

Validations

@SegaraRai SegaraRai added the enhancement New feature or request label Nov 26, 2022
@jwing8
Copy link

jwing8 commented Jan 1, 2023

I was able to use icons in both .astro and .vue files by configuring the Icons compiler property (under vite.plugins) to 'vue3'. I was also able set the IconsResolver (under vite.plugins.Components.resolvers) to allow for icon pack aliases and a custom template prefix to use. These settings are working for both Astro and Vue components. As a bonus, the 'unplugin-vue-components' package allows for auto-importing icons within the Vue components (still have to manually import in the Astro files though).

This addressed my use case but I don't think this approach would work should you have multiple UI integrations (e.g. Vue + Svelte), so something like your proposed solution would make sense.

@DeadOce4n
Copy link

I'm using this in a project which has both astro and svelte components, it works correctly without extra configuration by simply calling the Icons function twice inside the plugins array, one for svelte and one for astro, but the one for svelte should be first:

export default defineConfig({
  integrations: [svelte()],
  vite: {
    plugins: [
      Icons({
        compiler: 'svelte',
        autoInstall: true,
      }),
      Icons({
        compiler: 'astro',
        autoInstall: true,
      }),
    ],
  },
});

@Trombach
Copy link

I'm using this in a project which has both astro and svelte components, it works correctly without extra configuration by simply calling the Icons function twice inside the plugins array, one for svelte and one for astro, but the one for svelte should be first:

export default defineConfig({
  integrations: [svelte()],
  vite: {
    plugins: [
      Icons({
        compiler: 'svelte',
        autoInstall: true,
      }),
      Icons({
        compiler: 'astro',
        autoInstall: true,
      }),
    ],
  },
});

@DeadOce4n This does seem to work, thanks! Do you use typescript? I can't figure out how to set up type declarations for this. The code compiles fine, but I get typescript errors in either .astro or .svelte files. If I add

// tsconfig.json
{ 
  "compilerOptions": {
    "types": [
      "unplugin-icons/types/astro",
    ]
  }
}

as per the docs, my astro imports show no errors. But as soon as I try to add the svelte types the astro types break again. I've tried adding "unplugin-icons/types/svelte" to the "types" array in tsconfig.json as well as adding import 'unplugin-icons/types/svelte' or /// <reference types="unplugin-icons/types/svelte" />, as well as other things, but nothing works.

Any ideas how to fix this?

@vdawg-git
Copy link

Subscribing here too for the Typescript solution

@vdawg-git
Copy link

I solved it by using ~icons/* for Astro and virtual:icons/* for Svelte.

To achieve that I put the needed contents of the referenced .d.ts files directly into env.d.ts like this:

/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />
/// <reference types="astro/astro-jsx" />

declare module "virtual:icons/*" {
  import { SvelteComponent } from "svelte"
  import type { SvelteHTMLElements } from "svelte/elements"

  export default class extends SvelteComponent<SvelteHTMLElements["svg"]> {}
}

declare module "~icons/*" {
  const component: (props: astroHTML.JSX.SVGAttributes) => astroHTML.JSX.Element
  export default component
}

@arthurrmp
Copy link

I solved it by using ~icons/* for Astro and virtual:icons/* for Svelte.

Thanks for that @Visual-Dawg! I've been able to use icons for React and Astro using that tip.

If anyone is wondering, first, if you have imported the types on your tsconfig.json, remove it:

{
  "extends": "astro/tsconfigs/strict",
  "compilerOptions": {
    "baseUrl": "/Users/arthur/Documents/Projetos/Khada/khada-astro",
    "paths": {
      "@/*": [
        "./src/*"
      ]
    },
    "jsx": "react-jsx",
    "jsxImportSource": "react",
-    "types": [
-      "unplugin-icons/types/astro",
-   ]
  }
}

Then add this at the end of your env.d.ts file:

//...

declare module "virtual:icons/*" {
  import type { SVGProps } from "react";
 import type React from "react";

  const component: (props: SVGProps<SVGSVGElement>) => React.ReactElement;
  export default component;
}

declare module "~icons/*" {
  const component: (
    props: astroHTML.JSX.SVGAttributes,
  ) => astroHTML.JSX.Element;
  export default component;
}

To use an icon from a .tsx file:

import MdiAlarmOff from "virtual:icons/mdi/alarm-off";

export default function Test() {
  return (
    <MdiAlarmOff />
  )
}

To use an icon from a .astro file:

---
import CarbonSun from "~icons/carbon/sun";
---

<CarbonSun />

@DeadOce4n
Copy link

DeadOce4n commented Jan 31, 2024

@Trombach I did something similar to what others have commented here:

// src/env.d.ts

declare module 'icons:astro/*' {
  const component: (
    props: astroHTML.JSX.SVGAttributes,
  ) => astroHTML.JSX.Element;
  export default component;
}

declare module 'icons:svelte/*' {
  import { SvelteComponent } from 'svelte';
  import type { SvelteHTMLElements } from 'svelte/elements';
  export default class extends SvelteComponent<SvelteHTMLElements['svg']> {}
}
// astro.config.mjs
import { defineConfig } from 'astro/config';
import Icons from 'unplugin-icons/vite';

import svelte from '@astrojs/svelte';

// https://astro.build/config
export default defineConfig({
  integrations: [svelte()],
  vite: {
    resolve: {
      alias: [
        { find: 'icons:svelte', replacement: '~icons' },
        { find: 'icons:astro', replacement: '~icons' },
      ],
    },
    plugins: [
      Icons({
        compiler: 'svelte',
        autoInstall: true,
      }),
      Icons({
        compiler: 'astro',
        autoInstall: true,
      }),
    ],
  },
});

@Trombach
Copy link

Thanks @Visual-Dawg and @DeadOce4n for your solutions! I have gone with the vite resolve solution for now.

@antfu I'm wondering if this could maybe be improved either on the types or at least the documentation side? Since one of Astro's main strengths is that you can use other framework components within it, I think it would be great if the astro types that are shipped with this library allow you to import icons into all supported framework components.

I believe, one solution would be to add /icons:svelte/, /icons:astro/ etc to the allowed URL_PREFIXES array here and then declare all framework specific modules in the astro type file.
There are probably better solutions and I'm open for suggestions. I'm happy to provide a pull request for this if I get some guidance on how this should be handled.

@MrSquaare
Copy link

MrSquaare commented Feb 18, 2024

Did someone manage to use it with Astro and React? Because it doesn't work since unplugin is trying to use an Astro component instead of a React component

Astro components cannot be rendered directly via function call, such as Component() or {items.map(Component)}.

EDIT: Even mixing React and Vue doesn't work, so indeed we really need a way to tells Unplugin which compiler it should use (via import query or path matching)

@rishi-raj-jain
Copy link

Did someone manage to use it with Astro and React? Because it doesn't work since unplugin is trying to use an Astro component instead of a React component

Astro components cannot be rendered directly via function call, such as Component() or {items.map(Component)}.

EDIT: Even mixing React and Vue doesn't work, so indeed we really need a way to tells Unplugin which compiler it should use (via import query or path matching)

Yes! I wrote a quick blog on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

8 participants