vite icon indicating copy to clipboard operation
vite copied to clipboard

Generation of static svg sprite file in dist

Open koskinpark opened this issue 1 year ago • 8 comments

Description

I'm currently trying vite instead of webpack, and seems it's pretty matching our needs, but i noticed one problem which i can not resolve -> generation of svg sprite.

In webpack we have used https://www.npmjs.com/package/svg-sprite-loader plugin with extract mode. If in short -> it takes svg files from some folder, then it generating svg-sprite file with all of those icons and put this file into dist folder. In the end we have a static svg-sprite file in dist which we can use from any template like this:

   <svg>
      <use xlink:href="path-to-dist/svg-sprite.svg#icon-name"></use>
    </svg>

We don't use frameworks. We using Drupal and calling icons from twig templates for example (one of the available options).

Now about the problem - i really can't find at least one plugin or info in vite docs how we can do the same with vite. All existing plugins available on npm are mostly about dynamic js "imports" & dynamic svg-sprite generation & putting svg-sprite into DOM. But no plugin about creation of static file.

Any help please?

Suggested solution

For sure can be some plugin, since smells like "out of vite core's scope". Idk

Alternative

No response

Additional context

No response

Validations

koskinpark avatar Oct 10 '22 08:10 koskinpark

Does these work for you? https://www.npmjs.com/package/vite-plugin-svg-sprite https://github.com/vbenjs/vite-plugin-svg-icons

sapphi-red avatar Oct 17 '22 13:10 sapphi-red

Does these work for you? https://www.npmjs.com/package/vite-plugin-svg-sprite https://github.com/vbenjs/vite-plugin-svg-icons

Unfortunately they do not suit as they require a JS framework. My current project allows only vanilla JS. I suspect this is the same reason OP is searching for a solution.

I found https://www.npmjs.com/package/rollup-plugin-svgsprite-generator and it works perfectly for production builds but causes errors with HMR, namely:

[vite] Internal server error: Failed to resolve import "assets/svg" from "node_modules/vite/dist/client/client.mjs". Does the file exist?

Note that assets/svg is a directory, not a file. I tried formatting the directory path in different ways and was unable to get it to play nicely with HMR.

Did you ever find a solution @koskinpark ?

ctoma avatar Nov 22 '22 23:11 ctoma

Unfortunately they do not suit as they require a JS framework.

I don't think they require a JS framework. Won't vite-plugin-svg-sprite work by this code?

import appIconId from './path/to/icons/app.svg';

document.querySelector('#app-img').innerHTML = `
  <svg>
    <use xlinkHref="#${appIconId}" />
  </svg>
`

But I might got what the OP mean. IIUC they want to extract svg file references automatically and convert it to svg sprite.

sapphi-red avatar Nov 24 '22 15:11 sapphi-red

vite-plugin-svg-icons doesn't generate SVG file, there is an issue about that : Generate a .svg file instead of an embedded SVG tag

cyril-lamotte avatar Jan 18 '23 14:01 cyril-lamotte

Any updates ? 🙄

vshepel avatar Feb 06 '23 12:02 vshepel

https://github.com/JetBrains/svg-sprite-loader/issues/434

`import { Plugin } from 'vite'
import { readFileSync, readdirSync, writeFile } from 'fs'
import { join } from'path';

let idPerfix = ''
const svgTitle = /<svg([^>+].*?)>/
const clearHeightWidth = /(width|height)="([^>+].*?)"/g

const hasViewBox = /(viewBox="[^>+].*?")/g

const clearReturn = /(\r)|(\n)/g

function findSvgFile(dir): string[] {
  const svgRes = []
  const dirents = readdirSync(dir, {
    withFileTypes: true
  })
  for (const dirent of dirents) {
    if (dirent.isDirectory()) {
      svgRes.push(...findSvgFile(dir + dirent.name + '/'))
    } else {
      const svg = readFileSync(dir + dirent.name)
        .toString()
        .replace(clearReturn, '')
        .replace(svgTitle, ($1, $2) => {
          // console.log(++i)
          // console.log(dirent.name)
          let width = 0
          let height = 0
          let content = $2.replace(
            clearHeightWidth,
            (s1, s2, s3) => {
              if (s2 === 'width') {
                width = s3
              } else if (s2 === 'height') {
                height = s3
              }
              return ''
            }
          )
          if (!hasViewBox.test($2)) {
            content += `viewBox="0 0 ${width} ${height}"`
          }
          return `<symbol id="${idPerfix}-${dirent.name.replace(
            '.svg',
            ''
          )}" ${content}>`
        })
        .replace('</svg>', '</symbol>')
      svgRes.push(svg)
    }
  }
  return svgRes
}

export const svgBuilder = (
  path: string,
  perfix = 'icon'
): Plugin => {
  if (path === '') return
  idPerfix = perfix
  const res = findSvgFile(path)
//   console.log(res)
  // const res = []
  writeFile(join(process.cwd(), 'public', '/spritemap.svg'), `
        <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
        ${res.join('')}
        </svg>
    `, function(err) {
        if(err) {
            return console.log(err, {color: 'red'});
        }
        console.log("The spritemap.svg saved!");
    })

  return {
    name: 'svg-transform',
    transformIndexHtml(): string {
      return ''
    }
  }
}`

I add this code

writeFile(join(process.cwd(), 'public', '/spritemap.svg'), `
        <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
        ${res.join('')}
        </svg>
    `, function(err) {
        if(err) {
            return console.log(err, {color: 'red'});
        }
    console.log("The spritemap.svg saved!");
})

yeap, it need refactor but it work

taborskiy avatar Feb 08 '23 13:02 taborskiy

Also https://www.npmjs.com/package/vite-svg-sprite-wrapper

vshepel avatar Feb 12 '23 23:02 vshepel

Hi folks,

As I have done a bit of research for my own projects and most plugins have a similar name and overlapping features, I figured I could compile a somewhat easy-to-read list of available options that are out there:

Library Version Stars Downloads
@spiriit/vite-plugin-svg-spritemap
NPM
Generates a sprite from all the files from a directory/matching pattern. Additionally, has extra features for Vue. GitHub release (with filter) GitHub Repo stars npm
vite-plugin-svg-icons
NPM
Generate a sprite from all the files matching a pattern/in a directory and injects it into the body. Includes svgo with configuration. Additionally, can get icon names as list with virtual:svg-icons-names. GitHub release (with filter) GitHub Repo stars npm
vite-plugin-svg-sprite
NPM
Generates a sprite on the fly from imported svg icons and injects it into the document. Includes svgo. GitHub release (with filter) GitHub Repo stars npm
vite-plugin-svg-spritemap
NPM
Generates a sprite from all the files from a directory/matching pattern. Includes svgo and option to replace colors as currentColor. GitHub release (with filter) GitHub Repo stars npm
vite-svg-sprite-wrapper
NPM
Generates a sprite from all the files from a directory/matching pattern. Additionally, can generate TypeScript types. GitHub release (with filter) GitHub Repo stars npm

For the sake of being somewhat exhaustive, Astro's astro-icon is a vite plugin under the hood.

adrienrn avatar Apr 25 '24 09:04 adrienrn