vite-plugin-icons
Version:
Access thousands of icons as Vue components in Vite
195 lines (186 loc) • 5.36 kB
JavaScript
var __require = (x) => {
if (typeof require !== "undefined")
return require(x);
throw new Error('Dynamic require of "' + x + '" is not supported');
};
// src/loader.ts
import { SVG, Collection } from "@iconify/json-tools";
// src/compiler/vue2.ts
function Vue2Compiler(svg, name, icon) {
const { compile } = __require("vue-template-compiler");
const transpile = __require("vue-template-es2015-compiler");
const { render } = compile(svg);
const toFunction = (code2) => {
return `function () {${code2}}`;
};
let code = transpile(`var __render__ = ${toFunction(render)}
`, {});
code = code.replace(/\s__(render|staticRenderFns)__\s/g, " $1 ");
code += `
/* vite-plugin-components disabled */
export default {
render: render,
name: '${name}-${icon}',
}
`;
return code;
}
// src/compiler/vue3.ts
function Vue3Compiler(svg, name, icon) {
const { compileTemplate } = __require("@vue/compiler-sfc");
let { code } = compileTemplate({
source: svg,
id: `${name}:${icon}`,
filename: `${name}-${icon}.vue`
});
code = code.replace(/^export /g, "");
code += `
export default { name: '${name}-${icon}', render }`;
code += "\n/* vite-plugin-components disabled */";
return code;
}
// src/constants.ts
var URL_PREFIXES = ["/@vite-icons/", "@vite-icons/", "virtual:vite-icons/"];
// src/loader.ts
var iconPathRE = new RegExp(`${URL_PREFIXES.map((v) => `^${v}`).join("|")}`);
function isIconPath(path) {
return iconPathRE.test(path);
}
function normalizeIconPath(path) {
return path.replace(iconPathRE, URL_PREFIXES[0]);
}
function resolveIconsPath(path) {
if (!isIconPath(path))
return null;
path = path.replace(iconPathRE, "");
const query = {};
const queryIndex = path.indexOf("?");
if (queryIndex !== -1) {
const queryRaw = path.slice(queryIndex + 1);
path = path.slice(0, queryIndex);
new URLSearchParams(queryRaw).forEach((value, key) => {
query[value] = key;
});
}
if (path.endsWith(".vue"))
path = path.slice(0, -4);
const [collection, icon] = path.split("/");
return {
collection,
icon,
query
};
}
var _collections = {};
var _idTransforms = [
(str) => str,
(str) => str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(),
(str) => str.replace(/([a-z])(\d+)/g, "$1-$2")
];
function getCollection(name) {
if (!_collections[name]) {
const collection = new Collection();
collection.loadIconifyCollection(name);
_collections[name] = collection;
}
return _collections[name];
}
function getIcon(name, icon) {
const collection = getCollection(name);
if (!collection)
return null;
let data;
for (const trans of _idTransforms) {
data = collection.getIconData(trans(icon));
if (data)
return data;
}
return null;
}
async function generateComponent({ collection: name, icon }, options) {
const data = getIcon(name, icon);
const { scale, defaultStyle, defaultClass } = options;
const svg = new SVG(data);
const svgText = svg.getSVG({
height: `${scale}em`,
width: `${scale}em`,
style: defaultStyle,
class: defaultClass
});
if (!svgText)
return null;
if (options.compiler === "vue2")
return Vue2Compiler(svgText, name, icon);
else
return Vue3Compiler(svgText, name, icon);
}
async function generateComponentFromPath(path, options) {
const resolved = resolveIconsPath(path);
if (!resolved)
return null;
return generateComponent(resolved, options);
}
// src/resolver.ts
import Data from "@iconify/json";
function camelToKebab(key) {
const result = key.replace(/:/g, "-").replace(/([A-Z])/g, " $1").trim();
return result.split(/\s+/g).join("-").toLowerCase();
}
function ViteIconsResolver(options = {}) {
const {
componentPrefix = "i",
enabledCollections = Object.keys(Data.collections())
} = options;
const prefix = componentPrefix ? `${camelToKebab(componentPrefix)}-` : "";
enabledCollections.sort((a, b) => b.length - a.length);
return (name) => {
const kebab = camelToKebab(name);
if (!kebab.startsWith(prefix))
return;
const slice = kebab.slice(prefix.length);
const collection = enabledCollections.find((i) => slice.startsWith(`${i}-`)) || enabledCollections.find((i) => slice.startsWith(i));
if (!collection)
return;
let icon = slice.slice(collection.length);
if (icon[0] === "-")
icon = icon.slice(1);
if (!icon)
return;
if (!getIcon(collection, icon))
return;
return `virtual:vite-icons/${collection}/${icon}`;
};
}
// src/index.ts
function VitePluginIcons(userOptions = {}) {
let options;
return {
name: "vite-plugin-icons",
enforce: "post",
configResolved(config) {
options = Object.assign({
scale: 1.2,
defaultStyle: "",
defaultClass: "",
compiler: config.plugins.find((i) => i.name === "vite-plugin-vue2") ? "vue2" : "vue3"
}, userOptions);
},
resolveId(id) {
if (isIconPath(id)) {
return normalizeIconPath(id).replace(/\.vue$/i, "").replace(/^\//, "");
}
return null;
},
async load(id) {
if (isIconPath(id))
return await generateComponentFromPath(id, options) || null;
return null;
}
};
}
var src_default = VitePluginIcons;
export {
VitePluginIcons as Plugin,
ViteIconsResolver,
src_default as default
};