pure-glyf 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +265 -0
- package/dist/generator.d.ts +10 -0
- package/dist/icons.d.ts +2 -0
- package/dist/index.cjs +13 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +30 -0
- package/dist/inject.d.ts +20 -0
- package/dist/plugin.cjs +6 -0
- package/dist/plugin.d.ts +14 -0
- package/dist/plugin.js +111 -0
- package/package.json +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# pure-glyf
|
|
2
|
+
|
|
3
|
+
**Vite plugin and runtime to compile SVG icons into tree-shakeable CSS masks for modern web applications.**
|
|
4
|
+
|
|
5
|
+
`pure-glyf` takes a different approach to icon management. Instead of inlining SVGs (bloating your DOM) or using sprites (complexity), it converts your SVGs into CSS classes that inject their styles on demand. The result? Zero runtime overhead for unused icons, perfect tree-shaking, and a developer experience that feels like magic.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- 🌳 **True Tree-Shaking**: Only the icons you actually import end up in your final bundle.
|
|
12
|
+
- 🚀 **Zero Runtime Overhead**: Icons are just strings (CSS class names). No heavy JS objects or runtime parsers.
|
|
13
|
+
- ⚡ **On-Demand Injection**: CSS for an icon is only injected into the page when you import it.
|
|
14
|
+
- 🧩 **Vite Integration**: A dedicated Vite plugin automates the entire process.
|
|
15
|
+
- 🎨 **Themable**: Icons automatically inherit `currentColor`.
|
|
16
|
+
- 🖥️ **SSR Ready**: Built-in support for server-side rendering (critical CSS extraction).
|
|
17
|
+
- 🏷️ **Type-Safe**: Generated TypeScript definitions for your specific icon set.
|
|
18
|
+
|
|
19
|
+
## Why pure-glyf?
|
|
20
|
+
|
|
21
|
+
| Method | JS Bundle Size | DOM Size | Tree Shaking | Developer Experience |
|
|
22
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
23
|
+
| **Inline SVGs** | ❌ Heavy | ❌ Bloated | ✅ Good | 😐 Verbose JSX |
|
|
24
|
+
| **SVG Sprites** | ✅ Light | ✅ Light | ❌ Hard | 😫 Manual Management |
|
|
25
|
+
| **Icon Fonts** | ✅ Light | ✅ Light | ❌ None | 😐 FOUT / Accessibility |
|
|
26
|
+
| **pure-glyf** | ✅ **Minimal** | ✅ **Minimal** | ✅ **Perfect** | 😍 **Auto-generated** |
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install pure-glyf
|
|
32
|
+
# or
|
|
33
|
+
pnpm add pure-glyf
|
|
34
|
+
# or
|
|
35
|
+
yarn add pure-glyf
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
### 1. Structure Your Icons
|
|
41
|
+
|
|
42
|
+
Place your SVG files in a directory. You can have multiple directories (e.g., for different icon sets).
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
src/
|
|
46
|
+
assets/
|
|
47
|
+
icons/
|
|
48
|
+
home.svg
|
|
49
|
+
user.svg
|
|
50
|
+
settings.svg
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
#### Using Icons from npm
|
|
54
|
+
You can also source icons directly from installed packages (e.g., `@tabler/icons`, `@mdi/svg`, `heroicons`). Just install them as dev dependencies!
|
|
55
|
+
|
|
56
|
+
### 2. Configure Vite
|
|
57
|
+
|
|
58
|
+
Add `pureGlyfPlugin` to your `vite.config.ts`.
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
// vite.config.ts
|
|
62
|
+
import { defineConfig } from 'vite';
|
|
63
|
+
import { pureGlyfPlugin } from 'pure-glyf/plugin';
|
|
64
|
+
|
|
65
|
+
export default defineConfig({
|
|
66
|
+
plugins: [
|
|
67
|
+
pureGlyfPlugin({
|
|
68
|
+
icons: {
|
|
69
|
+
// 1. Local icons
|
|
70
|
+
// Usage: 'custom' + 'Home' -> customHome
|
|
71
|
+
custom: './src/assets/icons',
|
|
72
|
+
|
|
73
|
+
// 2. Common Icon Libraries (examples)
|
|
74
|
+
|
|
75
|
+
// Tabler Icons (Install: pnpm add -D @tabler/icons) - outline
|
|
76
|
+
// Usage: tablerHome, tablerUser
|
|
77
|
+
tabler: './node_modules/@tabler/icons/icons/outline',
|
|
78
|
+
|
|
79
|
+
// -- or --
|
|
80
|
+
|
|
81
|
+
// Tabler Icons (Install: pnpm add -D @tabler/icons) - all icons
|
|
82
|
+
// Usage: tablerOutlineHome, tablerFilledUser
|
|
83
|
+
tabler: './node_modules/@tabler/icons/icons',
|
|
84
|
+
|
|
85
|
+
// Material Design Icons (Install: pnpm add -D @mdi/svg)
|
|
86
|
+
// Usage: mdiAccount, mdiHome
|
|
87
|
+
mdi: './node_modules/@mdi/svg/svg',
|
|
88
|
+
|
|
89
|
+
// Heroicons (Install: pnpm add -D heroicons)
|
|
90
|
+
// Usage: heroHome, heroUser
|
|
91
|
+
hero: './node_modules/heroicons/24/outline',
|
|
92
|
+
|
|
93
|
+
// Lucide (Install: pnpm add -D lucide-static)
|
|
94
|
+
// Usage: lucHome, lucUser
|
|
95
|
+
luc: './node_modules/lucide-static/icons',
|
|
96
|
+
},
|
|
97
|
+
dts: 'src/pure-glyf-icons.d.ts'
|
|
98
|
+
})
|
|
99
|
+
]
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 3. Configure Rollup (Alternative)
|
|
104
|
+
|
|
105
|
+
If you are using Rollup without Vite, use the plugin directly.
|
|
106
|
+
|
|
107
|
+
> **Note**: You must use `@rollup/plugin-node-resolve` so Rollup can resolve the `pure-glyf` runtime.
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
// rollup.config.js
|
|
111
|
+
import resolve from '@rollup/plugin-node-resolve';
|
|
112
|
+
import { pureGlyfPlugin } from 'pure-glyf/plugin';
|
|
113
|
+
|
|
114
|
+
export default {
|
|
115
|
+
// ...
|
|
116
|
+
plugins: [
|
|
117
|
+
resolve(), // Required to resolve 'pure-glyf' runtime
|
|
118
|
+
pureGlyfPlugin({
|
|
119
|
+
icons: {
|
|
120
|
+
// ... same configuration as Vite
|
|
121
|
+
myIcon: './src/assets/icons'
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
]
|
|
125
|
+
};
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 3. Mount Styles
|
|
129
|
+
|
|
130
|
+
In your application's entry point (e.g., `main.tsx` or `index.ts`), call `mount()` to set up the style injection system.
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// You can import 'mount' from the main package OR the virtual module
|
|
134
|
+
import { mount } from 'pure-glyf';
|
|
135
|
+
// import { mount } from 'pure-glyf/icons'; // Also works!
|
|
136
|
+
|
|
137
|
+
mount(); // Injects the base styles and prepares the style tag
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 4. Use Icons
|
|
141
|
+
|
|
142
|
+
Import icons from the virtual module `pure-glyf/icons`. The export name is `[Prefix][PascalCaseFilename]`.
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
import { customHome, customUser } from 'pure-glyf/icons';
|
|
146
|
+
|
|
147
|
+
function App() {
|
|
148
|
+
// 'customHome' is just a string: "pure-glyf-icon glyf-custom-home"
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<nav>
|
|
152
|
+
{/* Use it as a class name on any element (usually <span> or <i>) */}
|
|
153
|
+
<span className={customHome} />
|
|
154
|
+
|
|
155
|
+
<button>
|
|
156
|
+
<i className={customUser} /> Profile
|
|
157
|
+
</button>
|
|
158
|
+
</nav>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Configuration
|
|
164
|
+
|
|
165
|
+
The `pureGlyfPlugin` accepts the following options:
|
|
166
|
+
|
|
167
|
+
### `icons` (Required)
|
|
168
|
+
A record mapping prefixes to directory paths.
|
|
169
|
+
- **Key**: The prefix for the generated variable names (e.g., `tabler` -> `tablerHome`).
|
|
170
|
+
- **Value**: The relative path to the directory containing `.svg` files.
|
|
171
|
+
|
|
172
|
+
### `dts` (Optional)
|
|
173
|
+
Path to generate the TypeScript declaration file.
|
|
174
|
+
- **Default**: `pure-glyf.d.ts` in the project root.
|
|
175
|
+
- **Recommendation**: Set this to a path included in your `tsconfig.json` (e.g., `src/pure-glyf-icons.d.ts`).
|
|
176
|
+
|
|
177
|
+
## styling
|
|
178
|
+
|
|
179
|
+
Icons are rendered as CSS masks. They inherit the text color (`currentColor`) by default.
|
|
180
|
+
|
|
181
|
+
### Default Styles
|
|
182
|
+
Every icon comes with the `.pure-glyf-icon` class:
|
|
183
|
+
|
|
184
|
+
```css
|
|
185
|
+
.pure-glyf-icon {
|
|
186
|
+
display: inline-block;
|
|
187
|
+
width: 1em;
|
|
188
|
+
height: 1em;
|
|
189
|
+
background-color: currentColor; /* Matches text color */
|
|
190
|
+
mask-size: contain;
|
|
191
|
+
mask-repeat: no-repeat;
|
|
192
|
+
mask-position: center;
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Custom Sizing & Coloring
|
|
197
|
+
Since they are just elements with a background color, you style them with standard CSS:
|
|
198
|
+
|
|
199
|
+
```css
|
|
200
|
+
.my-huge-icon {
|
|
201
|
+
font-size: 3rem; /* 3x normal text size */
|
|
202
|
+
color: #ff0000; /* Red icon */
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Server-Side Rendering (SSR)
|
|
207
|
+
|
|
208
|
+
`pure-glyf` supports SSR by allowing you to extract the CSS required for the rendered page.
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import { sheet } from 'pure-glyf';
|
|
212
|
+
import { renderToString } from 'react-dom/server';
|
|
213
|
+
|
|
214
|
+
// 1. Render your app
|
|
215
|
+
const html = renderToString(<App />);
|
|
216
|
+
|
|
217
|
+
// 2. Extract the accumulated CSS
|
|
218
|
+
// 'sheet' contains the base styles + styles for all icons used during render
|
|
219
|
+
const css = sheet;
|
|
220
|
+
|
|
221
|
+
// 3. Inject into your HTML template
|
|
222
|
+
const fullHtml = `
|
|
223
|
+
<!DOCTYPE html>
|
|
224
|
+
<html>
|
|
225
|
+
<head>
|
|
226
|
+
<style>${css}</style>
|
|
227
|
+
</head>
|
|
228
|
+
<body>
|
|
229
|
+
<div id="root">${html}</div>
|
|
230
|
+
</body>
|
|
231
|
+
</ul>
|
|
232
|
+
`;
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Architecture & Performance
|
|
236
|
+
|
|
237
|
+
`pure-glyf` uses a hybrid strategy to deliver the best developer experience without compiling performance costs.
|
|
238
|
+
|
|
239
|
+
### Development Mode: Parallel Parsing
|
|
240
|
+
In development, decoding thousands of Data URIs in JavaScript can be slow (blocking the main thread).
|
|
241
|
+
- **Mechanism**: The plugin moves all icon CSS into a separate virtual CSS module (`pure-glyf/icons.css`).
|
|
242
|
+
- **Benefit**: This offloads parsing to the browser's native CSS engine, which runs in parallel. The main JavaScript module remains tiny and instant to load.
|
|
243
|
+
- **Result**: Near-instant HMR and page reloads, even with huge icon sets like Material Design or Tabler.
|
|
244
|
+
|
|
245
|
+
### Production Mode: Perfect Tree-Shaking
|
|
246
|
+
In production, we prioritize bundle size.
|
|
247
|
+
- **Mechanism**: Icons are compiled into side-effect-free IIFEs (Immediately Invoked Function Expressions) annotated with `/*#__PURE__*/`.
|
|
248
|
+
- **Logic**:
|
|
249
|
+
```javascript
|
|
250
|
+
export const myIcon = /*#__PURE__*/ (() => {
|
|
251
|
+
injectCSS("..."); // Only runs if 'myIcon' is actually used
|
|
252
|
+
return "pure-glyf-icon glyf-my-icon";
|
|
253
|
+
})();
|
|
254
|
+
```
|
|
255
|
+
- **Benefit**: If you don't import `myIcon`, your bundler (Vite/Rollup/Esbuild) completely removes the code *and* the associated CSS string.
|
|
256
|
+
|
|
257
|
+
### The Virtual Module `pure-glyf/icons`
|
|
258
|
+
The plugin orchestrates everything by creating a virtual module `pure-glyf/icons`. This module re-exports the core runtime functions for convenience:
|
|
259
|
+
- `mount()`: Injects the styles.
|
|
260
|
+
- `sheet`: Access the accumulated CSS string.
|
|
261
|
+
- `onInject()`: Subscribe to new style injections.
|
|
262
|
+
|
|
263
|
+
## License
|
|
264
|
+
|
|
265
|
+
MIT
|
package/dist/icons.d.ts
ADDED
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=new Set;exports.sheet=`
|
|
2
|
+
.pure-glyf-icon {
|
|
3
|
+
display: inline-block;
|
|
4
|
+
width: 1em;
|
|
5
|
+
height: 1em;
|
|
6
|
+
background-color: currentColor;
|
|
7
|
+
mask-repeat: no-repeat;
|
|
8
|
+
mask-position: center;
|
|
9
|
+
mask-size: contain;
|
|
10
|
+
-webkit-mask-repeat: no-repeat;
|
|
11
|
+
-webkit-mask-position: center;
|
|
12
|
+
-webkit-mask-size: contain;
|
|
13
|
+
}`;let t=null;function r(){typeof document>"u"||t||(t=document.createElement("style"),t.textContent=exports.sheet,document.head.appendChild(t))}const o=new Set;function c(e){o.add(e)}function a(e){n.has(e)||(n.add(e),exports.sheet+=e,t&&(t.textContent=exports.sheet),o.forEach(i=>i(e)))}exports.injectCSS=a;exports.mount=r;exports.onInject=c;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { injectCSS, sheet, mount, onInject } from './inject';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const o = /* @__PURE__ */ new Set();
|
|
2
|
+
let n = `
|
|
3
|
+
.pure-glyf-icon {
|
|
4
|
+
display: inline-block;
|
|
5
|
+
width: 1em;
|
|
6
|
+
height: 1em;
|
|
7
|
+
background-color: currentColor;
|
|
8
|
+
mask-repeat: no-repeat;
|
|
9
|
+
mask-position: center;
|
|
10
|
+
mask-size: contain;
|
|
11
|
+
-webkit-mask-repeat: no-repeat;
|
|
12
|
+
-webkit-mask-position: center;
|
|
13
|
+
-webkit-mask-size: contain;
|
|
14
|
+
}`, t = null;
|
|
15
|
+
function a() {
|
|
16
|
+
typeof document > "u" || t || (t = document.createElement("style"), t.textContent = n, document.head.appendChild(t));
|
|
17
|
+
}
|
|
18
|
+
const i = /* @__PURE__ */ new Set();
|
|
19
|
+
function c(e) {
|
|
20
|
+
i.add(e);
|
|
21
|
+
}
|
|
22
|
+
function d(e) {
|
|
23
|
+
o.has(e) || (o.add(e), n += e, t && (t.textContent = n), i.forEach((r) => r(e)));
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
d as injectCSS,
|
|
27
|
+
a as mount,
|
|
28
|
+
c as onInject,
|
|
29
|
+
n as sheet
|
|
30
|
+
};
|
package/dist/inject.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS injection utility for pure-glyf icons.
|
|
3
|
+
*
|
|
4
|
+
* Uses a deduplication set to ensure each CSS rule is only injected once.
|
|
5
|
+
* Exports `sheet` for SSR usage.
|
|
6
|
+
* styles are NOT injected automatically. You must call `mount()` to inject them into the DOM.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Accumulated CSS string for Server-Side Rendering (SSR).
|
|
10
|
+
* Reset it if needed between requests in a server environment.
|
|
11
|
+
*/
|
|
12
|
+
export declare let sheet: string;
|
|
13
|
+
/**
|
|
14
|
+
* Mounts the styles to the DOM.
|
|
15
|
+
* If the style tag already exists, it does nothing.
|
|
16
|
+
* If not, it creates it and populates it with the current accumulated CSS.
|
|
17
|
+
*/
|
|
18
|
+
export declare function mount(): void;
|
|
19
|
+
export declare function onInject(callback: (css: string) => void): void;
|
|
20
|
+
export declare function injectCSS(css: string): void;
|
package/dist/plugin.cjs
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("node:fs"),p=require("node:path");function C(c){return c.replace(/[-_./\\]+(.)/g,(r,s)=>s.toUpperCase()).replace(/^./,r=>r.toUpperCase())}function I(c){return`data:image/svg+xml,${c.replace(/"/g,"'").replace(/%/g,"%25").replace(/#/g,"%23").replace(/{/g,"%7B").replace(/}/g,"%7D").replace(/</g,"%3C").replace(/>/g,"%3E").replace(/\s+/g," ")}`}function y(c,r){let s=[];return d.existsSync(c)&&d.readdirSync(c).forEach(i=>{const a=p.join(c,i),l=d.statSync(a);l&&l.isDirectory()?s=s.concat(y(a,r)):i.endsWith(".svg")&&s.push(p.relative(r,a))}),s}function P(c,r=!1){const s=[];for(const[n,e]of Object.entries(c)){const o=p.resolve(process.cwd(),e);if(!d.existsSync(o)){console.warn(`[pure-glyf] Warning: Icon directory not found: ${o}`);continue}y(o,o).forEach(g=>{const S=p.join(o,g),v=d.readFileSync(S,"utf-8"),m=g.replace(/\.svg$/,""),$=C(m),j=n+$,x=`glyf-${n.toLowerCase()}-${m.replace(/[^a-zA-Z0-9-]/g,"-")}`,h=I(v),w=`.${x}{mask-image:url("${h}");-webkit-mask-image:url("${h}");}`;s.push({name:j,css:w})})}const u=["import { injectCSS, mount, sheet, onInject } from 'pure-glyf';","export { mount, sheet, onInject };",""];let i="",a;r?(u.unshift("import 'pure-glyf/icons.css';"),i=s.map(n=>n.css).join(""),a=s.map(n=>{const e=n.css.match(/^\s*\.(\S+)\{/),o=e?e[1]:"error-class";return`export const ${n.name} = "pure-glyf-icon ${o}";`})):a=s.map(n=>{const e=n.css.match(/^\s*\.(\S+)\{/),o=e?e[1]:"error-class";return`export const ${n.name} = /*#__PURE__*/ (() => {
|
|
2
|
+
injectCSS(\`${n.css}\`);
|
|
3
|
+
return "pure-glyf-icon ${o}";
|
|
4
|
+
})();`});const l=u.concat(a).join(`
|
|
5
|
+
`),t=["declare module 'pure-glyf/icons' {"," export function mount(): void;"," export const sheet: string;"," export function onInject(callback: (css: string) => void): void;"];s.forEach(n=>{t.push(` export const ${n.name}: string;`)}),t.push("}");const f=t.join(`
|
|
6
|
+
`);return{code:l,css:i,dts:f}}function b(c){const r="pure-glyf/icons",s="\0"+r,u="pure-glyf/icons.css",i="\0"+u,a=c.dts||"pure-glyf.d.ts";let l=null,t=null,f=!1;function n(){try{if(l=P(c.icons,f),d.writeFileSync(p.resolve(process.cwd(),a),l.dts),t){const e=t.moduleGraph.getModuleById(s);e&&(t.moduleGraph.invalidateModule(e),t.ws.send({type:"full-reload",path:"*"}))}}catch(e){console.error("[pure-glyf] Generation failed:",e)}}return{name:"vite-plugin-pure-glyf",enforce:"pre",configResolved(e){f=e.command==="serve"},configureServer(e){t=e,Object.values(c.icons).forEach(o=>{t==null||t.watcher.add(p.resolve(o))}),t==null||t.watcher.on("change",o=>{o.endsWith(".svg")&&n()}),t==null||t.watcher.on("add",o=>{o.endsWith(".svg")&&n()}),t==null||t.watcher.on("unlink",o=>{o.endsWith(".svg")&&n()})},buildStart(){n()},resolveId(e){if(e===r)return s;if(e===u)return i},load(e){if(e===s)return l.code;if(e===i)return l.css}}}exports.pureGlyfPlugin=b;
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
export interface PureGlyfConfig {
|
|
3
|
+
/**
|
|
4
|
+
* Map of Prefix -> Directory Path
|
|
5
|
+
* Example: { 'Tabler': './icons/tabler', 'MyIcon': './src/assets' }
|
|
6
|
+
*/
|
|
7
|
+
icons: Record<string, string>;
|
|
8
|
+
/**
|
|
9
|
+
* Path to write the d.ts file to.
|
|
10
|
+
* Defaults to 'pure-glyf.d.ts' in the project root.
|
|
11
|
+
*/
|
|
12
|
+
dts?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function pureGlyfPlugin(config: PureGlyfConfig): Plugin;
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import p from "node:fs";
|
|
2
|
+
import d from "node:path";
|
|
3
|
+
function C(c) {
|
|
4
|
+
return c.replace(/[-_./\\]+(.)/g, (r, n) => n.toUpperCase()).replace(/^./, (r) => r.toUpperCase());
|
|
5
|
+
}
|
|
6
|
+
function I(c) {
|
|
7
|
+
return `data:image/svg+xml,${c.replace(/"/g, "'").replace(/%/g, "%25").replace(/#/g, "%23").replace(/{/g, "%7B").replace(/}/g, "%7D").replace(/</g, "%3C").replace(/>/g, "%3E").replace(/\s+/g, " ")}`;
|
|
8
|
+
}
|
|
9
|
+
function y(c, r) {
|
|
10
|
+
let n = [];
|
|
11
|
+
return p.existsSync(c) && p.readdirSync(c).forEach((i) => {
|
|
12
|
+
const a = d.join(c, i), l = p.statSync(a);
|
|
13
|
+
l && l.isDirectory() ? n = n.concat(y(a, r)) : i.endsWith(".svg") && n.push(d.relative(r, a));
|
|
14
|
+
}), n;
|
|
15
|
+
}
|
|
16
|
+
function P(c, r = !1) {
|
|
17
|
+
const n = [];
|
|
18
|
+
for (const [s, e] of Object.entries(c)) {
|
|
19
|
+
const o = d.resolve(process.cwd(), e);
|
|
20
|
+
if (!p.existsSync(o)) {
|
|
21
|
+
console.warn(`[pure-glyf] Warning: Icon directory not found: ${o}`);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
y(o, o).forEach((g) => {
|
|
25
|
+
const S = d.join(o, g), v = p.readFileSync(S, "utf-8"), m = g.replace(/\.svg$/, ""), $ = C(m), x = s + $, j = `glyf-${s.toLowerCase()}-${m.replace(/[^a-zA-Z0-9-]/g, "-")}`, h = I(v), w = `.${j}{mask-image:url("${h}");-webkit-mask-image:url("${h}");}`;
|
|
26
|
+
n.push({ name: x, css: w });
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
const u = [
|
|
30
|
+
"import { injectCSS, mount, sheet, onInject } from 'pure-glyf';",
|
|
31
|
+
"export { mount, sheet, onInject };",
|
|
32
|
+
""
|
|
33
|
+
];
|
|
34
|
+
let i = "", a;
|
|
35
|
+
r ? (u.unshift("import 'pure-glyf/icons.css';"), i = n.map((s) => s.css).join(""), a = n.map((s) => {
|
|
36
|
+
const e = s.css.match(/^\s*\.(\S+)\{/), o = e ? e[1] : "error-class";
|
|
37
|
+
return `export const ${s.name} = "pure-glyf-icon ${o}";`;
|
|
38
|
+
})) : a = n.map((s) => {
|
|
39
|
+
const e = s.css.match(/^\s*\.(\S+)\{/), o = e ? e[1] : "error-class";
|
|
40
|
+
return `export const ${s.name} = /*#__PURE__*/ (() => {
|
|
41
|
+
injectCSS(\`${s.css}\`);
|
|
42
|
+
return "pure-glyf-icon ${o}";
|
|
43
|
+
})();`;
|
|
44
|
+
});
|
|
45
|
+
const l = u.concat(a).join(`
|
|
46
|
+
`), t = [
|
|
47
|
+
"declare module 'pure-glyf/icons' {",
|
|
48
|
+
" export function mount(): void;",
|
|
49
|
+
" export const sheet: string;",
|
|
50
|
+
" export function onInject(callback: (css: string) => void): void;"
|
|
51
|
+
];
|
|
52
|
+
n.forEach((s) => {
|
|
53
|
+
t.push(` export const ${s.name}: string;`);
|
|
54
|
+
}), t.push("}");
|
|
55
|
+
const f = t.join(`
|
|
56
|
+
`);
|
|
57
|
+
return { code: l, css: i, dts: f };
|
|
58
|
+
}
|
|
59
|
+
function _(c) {
|
|
60
|
+
const r = "pure-glyf/icons", n = "\0" + r, u = "pure-glyf/icons.css", i = "\0" + u, a = c.dts || "pure-glyf.d.ts";
|
|
61
|
+
let l = null, t = null, f = !1;
|
|
62
|
+
function s() {
|
|
63
|
+
try {
|
|
64
|
+
if (l = P(c.icons, f), p.writeFileSync(d.resolve(process.cwd(), a), l.dts), t) {
|
|
65
|
+
const e = t.moduleGraph.getModuleById(n);
|
|
66
|
+
e && (t.moduleGraph.invalidateModule(e), t.ws.send({
|
|
67
|
+
type: "full-reload",
|
|
68
|
+
path: "*"
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
} catch (e) {
|
|
72
|
+
console.error("[pure-glyf] Generation failed:", e);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
name: "vite-plugin-pure-glyf",
|
|
77
|
+
enforce: "pre",
|
|
78
|
+
configResolved(e) {
|
|
79
|
+
f = e.command === "serve";
|
|
80
|
+
},
|
|
81
|
+
configureServer(e) {
|
|
82
|
+
t = e, Object.values(c.icons).forEach((o) => {
|
|
83
|
+
t == null || t.watcher.add(d.resolve(o));
|
|
84
|
+
}), t == null || t.watcher.on("change", (o) => {
|
|
85
|
+
o.endsWith(".svg") && s();
|
|
86
|
+
}), t == null || t.watcher.on("add", (o) => {
|
|
87
|
+
o.endsWith(".svg") && s();
|
|
88
|
+
}), t == null || t.watcher.on("unlink", (o) => {
|
|
89
|
+
o.endsWith(".svg") && s();
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
buildStart() {
|
|
93
|
+
s();
|
|
94
|
+
},
|
|
95
|
+
resolveId(e) {
|
|
96
|
+
if (e === r)
|
|
97
|
+
return n;
|
|
98
|
+
if (e === u)
|
|
99
|
+
return i;
|
|
100
|
+
},
|
|
101
|
+
load(e) {
|
|
102
|
+
if (e === n)
|
|
103
|
+
return l.code;
|
|
104
|
+
if (e === i)
|
|
105
|
+
return l.css;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
_ as pureGlyfPlugin
|
|
111
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pure-glyf",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Vite plugin and runtime for compiling SVGs into tree-shakeable CSS masks",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"icons",
|
|
8
|
+
"svg",
|
|
9
|
+
"css",
|
|
10
|
+
"tree-shaking",
|
|
11
|
+
"vite-plugin"
|
|
12
|
+
],
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/eddow/pure-glyf.git"
|
|
16
|
+
},
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/eddow/pure-glyf/issues"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/eddow/pure-glyf#readme",
|
|
21
|
+
"author": "eddow <eddow@ownk.com>",
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"main": "./dist/index.cjs",
|
|
24
|
+
"module": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js",
|
|
30
|
+
"require": "./dist/index.cjs"
|
|
31
|
+
},
|
|
32
|
+
"./plugin": {
|
|
33
|
+
"types": "./dist/plugin.d.ts",
|
|
34
|
+
"import": "./dist/plugin.js",
|
|
35
|
+
"require": "./dist/plugin.cjs"
|
|
36
|
+
},
|
|
37
|
+
"./inject": {
|
|
38
|
+
"types": "./dist/inject.d.ts",
|
|
39
|
+
"import": "./dist/inject.js",
|
|
40
|
+
"require": "./dist/inject.cjs"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist"
|
|
45
|
+
],
|
|
46
|
+
"scripts": {
|
|
47
|
+
"dev": "vite",
|
|
48
|
+
"build": "vite build && tsc --emitDeclarationOnly",
|
|
49
|
+
"build:watch": "vite build --watch",
|
|
50
|
+
"preview": "vite preview"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^25.0.9",
|
|
54
|
+
"tsx": "^4.21.0",
|
|
55
|
+
"typescript": "^5.7.0",
|
|
56
|
+
"vite": "^6.0.0",
|
|
57
|
+
"vite-plugin-dts": "^4.0.0"
|
|
58
|
+
},
|
|
59
|
+
"license": "MIT",
|
|
60
|
+
"packageManager": "pnpm@10.7.1+sha512.2d92c86b7928dc8284f53494fb4201f983da65f0fb4f0d40baafa5cf628fa31dae3e5968f12466f17df7e97310e30f343a648baea1b9b350685dafafffdf5808"
|
|
61
|
+
}
|