pure-glyf 0.1.0 → 1.0.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 CHANGED
@@ -1,5 +1,7 @@
1
1
  # pure-glyf
2
2
 
3
+ [![npm version](https://badge.fury.io/js/pure-glyf.svg)](https://badge.fury.io/js/pure-glyf)
4
+
3
5
  **Vite plugin and runtime to compile SVG icons into tree-shakeable CSS masks for modern web applications.**
4
6
 
5
7
  `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.
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=new Set;exports.sheet=`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=new Map,a=new Set;let t=null;const r=`
2
2
  .pure-glyf-icon {
3
3
  display: inline-block;
4
4
  width: 1em;
@@ -10,4 +10,4 @@
10
10
  -webkit-mask-repeat: no-repeat;
11
11
  -webkit-mask-position: center;
12
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;
13
+ }`;exports.sheet=r;function s(){typeof document>"u"||t||(t=document.createElement("style"),t.textContent=exports.sheet,document.head.appendChild(t))}function l(e){a.add(e)}function u(e){const n=e.match(/^\.([\w-]+)/),o=n?n[1]:e;c.has(o)||(c.set(o,e),exports.sheet+=e,t&&(t.textContent=exports.sheet),a.forEach(i=>i(e)))}function m(e){const n=new Set;for(const[o,i]of c.entries())e.includes(o)&&n.add(i);return r+Array.from(n).join("")}exports.extractCriticalCSS=m;exports.injectCSS=u;exports.mount=s;exports.onInject=l;
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export { injectCSS, sheet, mount, onInject } from './inject';
1
+ export { injectCSS, sheet, mount, onInject, extractCriticalCSS } from './inject';
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
- const o = /* @__PURE__ */ new Set();
2
- let n = `
1
+ const a = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Set();
2
+ let t = null;
3
+ const s = `
3
4
  .pure-glyf-icon {
4
5
  display: inline-block;
5
6
  width: 1em;
@@ -11,20 +12,28 @@ let n = `
11
12
  -webkit-mask-repeat: no-repeat;
12
13
  -webkit-mask-position: center;
13
14
  -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));
15
+ }`;
16
+ let c = s;
17
+ function l() {
18
+ typeof document > "u" || t || (t = document.createElement("style"), t.textContent = c, document.head.appendChild(t));
17
19
  }
18
- const i = /* @__PURE__ */ new Set();
19
- function c(e) {
20
- i.add(e);
20
+ function u(e) {
21
+ r.add(e);
22
+ }
23
+ function m(e) {
24
+ const n = e.match(/^\.([\w-]+)/), o = n ? n[1] : e;
25
+ a.has(o) || (a.set(o, e), c += e, t && (t.textContent = c), r.forEach((i) => i(e)));
21
26
  }
22
27
  function d(e) {
23
- o.has(e) || (o.add(e), n += e, t && (t.textContent = n), i.forEach((r) => r(e)));
28
+ const n = /* @__PURE__ */ new Set();
29
+ for (const [o, i] of a.entries())
30
+ e.includes(o) && n.add(i);
31
+ return s + Array.from(n).join("");
24
32
  }
25
33
  export {
26
- d as injectCSS,
27
- a as mount,
28
- c as onInject,
29
- n as sheet
34
+ d as extractCriticalCSS,
35
+ m as injectCSS,
36
+ l as mount,
37
+ u as onInject,
38
+ c as sheet
30
39
  };
package/dist/inject.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  */
8
8
  /**
9
9
  * Accumulated CSS string for Server-Side Rendering (SSR).
10
- * Reset it if needed between requests in a server environment.
10
+ * Contains all injected styles.
11
11
  */
12
12
  export declare let sheet: string;
13
13
  /**
@@ -18,3 +18,10 @@ export declare let sheet: string;
18
18
  export declare function mount(): void;
19
19
  export declare function onInject(callback: (css: string) => void): void;
20
20
  export declare function injectCSS(css: string): void;
21
+ /**
22
+ * Scans the provided HTML for usage of injected icons and returns
23
+ * a critical CSS string containing only the necessary styles.
24
+ *
25
+ * @param html The full HTML string to scan
26
+ */
27
+ export declare function extractCriticalCSS(html: string): string;
package/dist/plugin.cjs CHANGED
@@ -1,6 +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}";
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("node:fs"),p=require("node:path");function C(o){return o.replace(/[-_./\\]+(.)/g,(c,s)=>s.toUpperCase()).replace(/^./,c=>c.toUpperCase())}function I(o){return`data:image/svg+xml,${o.replace(/"/g,"'").replace(/%/g,"%25").replace(/#/g,"%23").replace(/{/g,"%7B").replace(/}/g,"%7D").replace(/</g,"%3C").replace(/>/g,"%3E").replace(/\s+/g," ")}`}function v(o,c){let s=[];return d.existsSync(o)&&d.readdirSync(o).forEach(i=>{const a=p.join(o,i),l=d.statSync(a);l&&l.isDirectory()?s=s.concat(v(a,c)):i.endsWith(".svg")&&s.push(p.relative(c,a))}),s}function P(o,c=!1){const s=[];for(const[t,e]of Object.entries(o)){const n=p.resolve(process.cwd(),e);if(!d.existsSync(n)){console.warn(`[pure-glyf] Warning: Icon directory not found: ${n}`);continue}v(n,n).forEach(g=>{const y=p.join(n,g),S=d.readFileSync(y,"utf-8"),m=g.replace(/\.svg$/,""),$=C(m),j=t+$,x=`glyf-${t.toLowerCase()}-${m.replace(/[^a-zA-Z0-9-]/g,"-")}`,h=I(S),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;c?(u.unshift("import 'pure-glyf/icons.css';"),i=s.map(t=>t.css).join(""),a=s.map(t=>{const e=t.css.match(/^\s*\.(\S+)\{/),n=e?e[1]:"error-class";return`export const ${t.name} = "pure-glyf-icon ${n}";`})):a=s.map(t=>{const e=t.css.match(/^\s*\.(\S+)\{/),n=e?e[1]:"error-class";return`export const ${t.name} = /*#__PURE__*/ (() => {
2
+ injectCSS(\`${t.css}\`);
3
+ return "pure-glyf-icon ${n}";
4
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;
5
+ `),r=["declare module 'pure-glyf/icons' {"," export function mount(): void;"," export const sheet: string;"," export function onInject(callback: (css: string) => void): void;"];s.forEach(t=>{r.push(` export const ${t.name}: string;`)}),r.push("}");const f=r.join(`
6
+ `);return{code:l,css:i,dts:f}}function b(o){const c="pure-glyf/icons",s="\0"+c,u="pure-glyf/icons.css",i="\0"+u,a=o.dts||"pure-glyf.d.ts";let l=null,r=null,f=!1;function t(){try{if(l=P(o.icons,f),d.writeFileSync(p.resolve(process.cwd(),a),l.dts),r){const e=r.moduleGraph.getModuleById(s);e&&(r.moduleGraph.invalidateModule(e),r.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){r=e,Object.values(o.icons).forEach(n=>{r?.watcher.add(p.resolve(n))}),r?.watcher.on("change",n=>{n.endsWith(".svg")&&t()}),r?.watcher.on("add",n=>{n.endsWith(".svg")&&t()}),r?.watcher.on("unlink",n=>{n.endsWith(".svg")&&t()})},buildStart(){t()},resolveId(e){if(e===c)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.js CHANGED
@@ -1,29 +1,29 @@
1
1
  import p from "node:fs";
2
2
  import d from "node:path";
3
- function C(c) {
4
- return c.replace(/[-_./\\]+(.)/g, (r, n) => n.toUpperCase()).replace(/^./, (r) => r.toUpperCase());
3
+ function C(o) {
4
+ return o.replace(/[-_./\\]+(.)/g, (c, s) => s.toUpperCase()).replace(/^./, (c) => c.toUpperCase());
5
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, " ")}`;
6
+ function I(o) {
7
+ return `data:image/svg+xml,${o.replace(/"/g, "'").replace(/%/g, "%25").replace(/#/g, "%23").replace(/{/g, "%7B").replace(/}/g, "%7D").replace(/</g, "%3C").replace(/>/g, "%3E").replace(/\s+/g, " ")}`;
8
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;
9
+ function v(o, c) {
10
+ let s = [];
11
+ return p.existsSync(o) && p.readdirSync(o).forEach((i) => {
12
+ const a = d.join(o, i), l = p.statSync(a);
13
+ l && l.isDirectory() ? s = s.concat(v(a, c)) : i.endsWith(".svg") && s.push(d.relative(c, a));
14
+ }), s;
15
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}`);
16
+ function P(o, c = !1) {
17
+ const s = [];
18
+ for (const [t, e] of Object.entries(o)) {
19
+ const n = d.resolve(process.cwd(), e);
20
+ if (!p.existsSync(n)) {
21
+ console.warn(`[pure-glyf] Warning: Icon directory not found: ${n}`);
22
22
  continue;
23
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 });
24
+ v(n, n).forEach((g) => {
25
+ const y = d.join(n, g), S = p.readFileSync(y, "utf-8"), m = g.replace(/\.svg$/, ""), $ = C(m), x = t + $, j = `glyf-${t.toLowerCase()}-${m.replace(/[^a-zA-Z0-9-]/g, "-")}`, h = I(S), w = `.${j}{mask-image:url("${h}");-webkit-mask-image:url("${h}");}`;
26
+ s.push({ name: x, css: w });
27
27
  });
28
28
  }
29
29
  const u = [
@@ -32,38 +32,38 @@ function P(c, r = !1) {
32
32
  ""
33
33
  ];
34
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}";
35
+ c ? (u.unshift("import 'pure-glyf/icons.css';"), i = s.map((t) => t.css).join(""), a = s.map((t) => {
36
+ const e = t.css.match(/^\s*\.(\S+)\{/), n = e ? e[1] : "error-class";
37
+ return `export const ${t.name} = "pure-glyf-icon ${n}";`;
38
+ })) : a = s.map((t) => {
39
+ const e = t.css.match(/^\s*\.(\S+)\{/), n = e ? e[1] : "error-class";
40
+ return `export const ${t.name} = /*#__PURE__*/ (() => {
41
+ injectCSS(\`${t.css}\`);
42
+ return "pure-glyf-icon ${n}";
43
43
  })();`;
44
44
  });
45
45
  const l = u.concat(a).join(`
46
- `), t = [
46
+ `), r = [
47
47
  "declare module 'pure-glyf/icons' {",
48
48
  " export function mount(): void;",
49
49
  " export const sheet: string;",
50
50
  " export function onInject(callback: (css: string) => void): void;"
51
51
  ];
52
- n.forEach((s) => {
53
- t.push(` export const ${s.name}: string;`);
54
- }), t.push("}");
55
- const f = t.join(`
52
+ s.forEach((t) => {
53
+ r.push(` export const ${t.name}: string;`);
54
+ }), r.push("}");
55
+ const f = r.join(`
56
56
  `);
57
57
  return { code: l, css: i, dts: f };
58
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() {
59
+ function _(o) {
60
+ const c = "pure-glyf/icons", s = "\0" + c, u = "pure-glyf/icons.css", i = "\0" + u, a = o.dts || "pure-glyf.d.ts";
61
+ let l = null, r = null, f = !1;
62
+ function t() {
63
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({
64
+ if (l = P(o.icons, f), p.writeFileSync(d.resolve(process.cwd(), a), l.dts), r) {
65
+ const e = r.moduleGraph.getModuleById(s);
66
+ e && (r.moduleGraph.invalidateModule(e), r.ws.send({
67
67
  type: "full-reload",
68
68
  path: "*"
69
69
  }));
@@ -79,27 +79,27 @@ function _(c) {
79
79
  f = e.command === "serve";
80
80
  },
81
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();
82
+ r = e, Object.values(o.icons).forEach((n) => {
83
+ r?.watcher.add(d.resolve(n));
84
+ }), r?.watcher.on("change", (n) => {
85
+ n.endsWith(".svg") && t();
86
+ }), r?.watcher.on("add", (n) => {
87
+ n.endsWith(".svg") && t();
88
+ }), r?.watcher.on("unlink", (n) => {
89
+ n.endsWith(".svg") && t();
90
90
  });
91
91
  },
92
92
  buildStart() {
93
- s();
93
+ t();
94
94
  },
95
95
  resolveId(e) {
96
- if (e === r)
97
- return n;
96
+ if (e === c)
97
+ return s;
98
98
  if (e === u)
99
99
  return i;
100
100
  },
101
101
  load(e) {
102
- if (e === n)
102
+ if (e === s)
103
103
  return l.code;
104
104
  if (e === i)
105
105
  return l.css;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pure-glyf",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "Vite plugin and runtime for compiling SVGs into tree-shakeable CSS masks",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -38,7 +38,9 @@
38
38
  "types": "./dist/inject.d.ts",
39
39
  "import": "./dist/inject.js",
40
40
  "require": "./dist/inject.cjs"
41
- }
41
+ },
42
+ "./src": "./src/index.ts",
43
+ "./src/*": "./src/*"
42
44
  },
43
45
  "files": [
44
46
  "dist"
@@ -50,10 +52,10 @@
50
52
  "preview": "vite preview"
51
53
  },
52
54
  "devDependencies": {
53
- "@types/node": "^25.0.9",
55
+ "@types/node": "25.0.10",
54
56
  "tsx": "^4.21.0",
55
57
  "typescript": "^5.7.0",
56
- "vite": "^6.0.0",
58
+ "vite": "7.3.1",
57
59
  "vite-plugin-dts": "^4.0.0"
58
60
  },
59
61
  "license": "MIT",