@tinloof/typed-svg-sprite 0.0.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # @tinloof/typed-svg-sprite
2
+
3
+ ## 0.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Initial release of @tinloof/typed-svg-sprite
8
+ - Generate optimized SVG sprites with full TypeScript support
9
+ - CLI tool for generating sprites from SVG files
10
+ - Next.js integration with `withSpriteLoader`
11
+ - Auto-generated TypeScript definitions
12
+ - Auto-generated React Icon component
13
+ - Watch mode for development
package/README.md ADDED
@@ -0,0 +1,190 @@
1
+ # @tinloof/typed-svg-sprite
2
+
3
+ > Generate optimized SVG sprites with full TypeScript support
4
+
5
+ Automatically generates SVG sprite files with type-safe TypeScript definitions and a ready-to-use React component.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @tinloof/typed-svg-sprite
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ### CLI
16
+
17
+ ```bash
18
+ # Generate sprite
19
+ typed-svg-sprite --input public/icons --output public/sprite.svg
20
+
21
+ # Watch mode
22
+ typed-svg-sprite -i public/icons -o public/sprite.svg --watch
23
+ ```
24
+
25
+ ### Next.js
26
+
27
+ ```typescript
28
+ // next.config.ts
29
+ import { withSpriteLoader } from "@tinloof/typed-svg-sprite/next";
30
+
31
+ export default withSpriteLoader({});
32
+ ```
33
+
34
+ Place SVGs in `public/icons/` and use:
35
+
36
+ ```typescript
37
+ import { HOME, SETTINGS } from "@/generated/icons";
38
+ import { Icon } from "@/generated/Icon";
39
+
40
+ function MyComponent() {
41
+ return (
42
+ <>
43
+ <Icon href={HOME} />
44
+ <Icon href={SETTINGS} size={32} />
45
+ </>
46
+ );
47
+ }
48
+ ```
49
+
50
+ ## Usage
51
+
52
+ ### CLI Options
53
+
54
+ ```bash
55
+ typed-svg-sprite --input <dir> --output <file> [options]
56
+
57
+ Options:
58
+ -i, --input <dir> Directory containing SVG files
59
+ -o, --output <file> Output sprite file path
60
+ -w, --watch Watch for changes and regenerate
61
+ -h, --help Show help message
62
+ ```
63
+
64
+ ### Next.js Configuration
65
+
66
+ ```typescript
67
+ export default withSpriteLoader(
68
+ {
69
+ // your existing Next.js config
70
+ },
71
+ {
72
+ inputDir?: string; // default: "public/icons"
73
+ outputFile?: string; // default: "public/sprite.svg"
74
+ url?: string; // default: "/"
75
+ filename?: string; // default: "sprite.svg"
76
+ typesOutputFile?: string; // default: "generated/icons.ts"
77
+ generateIconComponent?: boolean; // default: true
78
+ iconComponentOutputFile?: string; // default: "generated/Icon.tsx"
79
+ }
80
+ );
81
+ ```
82
+
83
+ ## Generated Files
84
+
85
+ ### 1. Sprite (`public/sprite.svg`)
86
+
87
+ ```xml
88
+ <svg xmlns="http://www.w3.org/2000/svg" style="display:none">
89
+ <symbol id="a" viewBox="0 0 24 24"><!-- icon content --></symbol>
90
+ <symbol id="b" viewBox="0 0 24 24"><!-- icon content --></symbol>
91
+ </svg>
92
+ ```
93
+
94
+ ### 2. TypeScript Types (`generated/icons.ts`)
95
+
96
+ ```typescript
97
+ export enum IconId {
98
+ HOME = "a",
99
+ SETTINGS = "b",
100
+ }
101
+
102
+ export type IconHref = `/sprite.svg#${IconId}`;
103
+
104
+ export const HOME: IconHref = "/sprite.svg#a";
105
+ export const SETTINGS: IconHref = "/sprite.svg#b";
106
+
107
+ export function getIconHref(iconId: IconId): IconHref;
108
+ export const allIcons: IconId[];
109
+ ```
110
+
111
+ ### 3. React Component (`generated/Icon.tsx`)
112
+
113
+ ```typescript
114
+ import { IconHref } from "./icons";
115
+
116
+ export interface IconProps extends React.SVGProps<SVGSVGElement> {
117
+ href: IconHref;
118
+ size?: number | string;
119
+ }
120
+
121
+ export function Icon({ href, size = 24, ...props }: IconProps) {
122
+ // ...
123
+ }
124
+ ```
125
+
126
+ ## Examples
127
+
128
+ ### Basic Usage
129
+
130
+ ```typescript
131
+ import { HOME, SEARCH, SETTINGS } from "@/generated/icons";
132
+ import { Icon } from "@/generated/Icon";
133
+
134
+ <Icon href={HOME} />
135
+ <Icon href={SEARCH} size={20} />
136
+ <Icon href={SETTINGS} className="text-blue-500" />
137
+ ```
138
+
139
+ ### Dynamic Icons
140
+
141
+ ```typescript
142
+ import { IconId, getIconHref } from "@/generated/icons";
143
+
144
+ function DynamicIcon({ iconId }: { iconId: IconId }) {
145
+ return <Icon href={getIconHref(iconId)} />;
146
+ }
147
+ ```
148
+
149
+ ### Build Script Integration
150
+
151
+ ```json
152
+ {
153
+ "scripts": {
154
+ "generate:icons": "typed-svg-sprite --input public/icons --output public/sprite.svg",
155
+ "build": "npm run generate:icons && next build"
156
+ }
157
+ }
158
+ ```
159
+
160
+ ### Without React
161
+
162
+ ```bash
163
+ # Generate sprite and types
164
+ typed-svg-sprite --input src/icons --output public/sprite.svg
165
+
166
+ # Use generated types
167
+ import { HOME, SETTINGS } from "./generated/icons";
168
+
169
+ // In your HTML/JS
170
+ <svg><use href={HOME} /></svg>
171
+ ```
172
+
173
+ ## How It Works
174
+
175
+ 1. Scans directory for `.svg` files
176
+ 2. Extracts and optimizes SVG content
177
+ 3. Generates compact base-52 IDs (`a`, `b`, `aa`, etc.)
178
+ 4. Combines into single sprite file
179
+ 5. Generates TypeScript types with file-based names
180
+ 6. Generates React component (optional)
181
+
182
+ **Symbol IDs**: Short IDs (`a`, `b`) in sprite, original names (`HOME`, `SETTINGS`) in TypeScript exports.
183
+
184
+ ## Roadmap
185
+
186
+ - [ ] Integrate SVGO for advanced SVG optimization
187
+
188
+ ## License
189
+
190
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const core_js_1 = require("./core.js");
40
+ function parseArgs() {
41
+ const args = process.argv.slice(2);
42
+ const options = {};
43
+ for (let i = 0; i < args.length; i++) {
44
+ switch (args[i]) {
45
+ case "--input":
46
+ case "-i":
47
+ options.input = args[++i];
48
+ break;
49
+ case "--output":
50
+ case "-o":
51
+ options.output = args[++i];
52
+ break;
53
+ case "--watch":
54
+ case "-w":
55
+ options.watch = true;
56
+ break;
57
+ case "--help":
58
+ case "-h":
59
+ printHelp();
60
+ process.exit(0);
61
+ }
62
+ }
63
+ if (!options.input || !options.output) {
64
+ console.error("Error: --input and --output are required\n");
65
+ printHelp();
66
+ process.exit(1);
67
+ }
68
+ return options;
69
+ }
70
+ function printHelp() {
71
+ console.log(`
72
+ SVG Sprite Generator
73
+
74
+ Usage:
75
+ typed-svg-sprite --input <dir> --output <file> [options]
76
+
77
+ Options:
78
+ -i, --input <dir> Directory containing SVG files
79
+ -o, --output <file> Output sprite file path
80
+ -w, --watch Watch for changes and regenerate
81
+ -h, --help Show this help message
82
+
83
+ Examples:
84
+ typed-svg-sprite --input public/icons --output public/sprite.svg
85
+ typed-svg-sprite -i ./icons -o ./dist/sprite.svg --watch
86
+ `);
87
+ }
88
+ function runSpriteGeneration(inputDir, outputFile) {
89
+ try {
90
+ (0, core_js_1.generateSprite)({
91
+ inputDir,
92
+ outputFile,
93
+ verbose: true,
94
+ minify: true,
95
+ });
96
+ }
97
+ catch (error) {
98
+ console.error("Error generating sprite:", error);
99
+ process.exit(1);
100
+ }
101
+ }
102
+ function main() {
103
+ const options = parseArgs();
104
+ // Initial generation
105
+ runSpriteGeneration(options.input, options.output);
106
+ // Watch mode
107
+ if (options.watch) {
108
+ const absoluteInputDir = path.resolve(process.cwd(), options.input);
109
+ console.log(`\n👀 Watching ${options.input} for changes...`);
110
+ fs.watch(absoluteInputDir, { recursive: true }, (_eventType, filename) => {
111
+ if (filename?.endsWith(".svg")) {
112
+ console.log(`\n📝 Change detected: ${filename}`);
113
+ runSpriteGeneration(options.input, options.output);
114
+ }
115
+ });
116
+ }
117
+ }
118
+ main();
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Options for Icon component generation
3
+ */
4
+ export interface IconComponentGenerationOptions {
5
+ /**
6
+ * Output path for the Icon component file
7
+ * Can be relative or absolute path
8
+ */
9
+ outputFile: string;
10
+ /**
11
+ * Path to the generated types file (for import statement)
12
+ * Relative to the component output file
13
+ */
14
+ typesFileRelativePath: string;
15
+ /**
16
+ * Whether to log output messages
17
+ * @default true
18
+ */
19
+ verbose?: boolean;
20
+ }
21
+ /**
22
+ * Generate React Icon component file
23
+ */
24
+ export declare function generateIconComponent(options: IconComponentGenerationOptions): void;
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.generateIconComponent = generateIconComponent;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ /**
40
+ * Generate React Icon component file
41
+ */
42
+ function generateIconComponent(options) {
43
+ const { outputFile, typesFileRelativePath, verbose = true } = options;
44
+ try {
45
+ const projectRoot = process.cwd();
46
+ const absoluteOutputFile = path.resolve(projectRoot, outputFile);
47
+ // Normalize the import path (ensure it uses / separator and doesn't start with ./ if in same dir)
48
+ const normalizedImportPath = typesFileRelativePath
49
+ .replace(/\\/g, "/")
50
+ .replace(/\.tsx?$/, "");
51
+ const content = `// Auto-generated by typed-svg-sprite
52
+ // Do not edit this file manually
53
+
54
+ import { IconHref } from "${normalizedImportPath}";
55
+
56
+ export interface IconProps extends React.SVGProps<SVGSVGElement> {
57
+ /** The icon href (import from generated/icons.ts) */
58
+ href: IconHref;
59
+ /** Size shorthand for width and height */
60
+ size?: number | string;
61
+ }
62
+
63
+ export function Icon({
64
+ href,
65
+ size = 24,
66
+ width,
67
+ height,
68
+ className = "",
69
+ viewBox = "0 0 24 24",
70
+ fill = "currentColor",
71
+ ...props
72
+ }: IconProps) {
73
+ return (
74
+ <svg
75
+ className={className}
76
+ width={width || size}
77
+ height={height || size}
78
+ viewBox={viewBox}
79
+ fill={fill}
80
+ aria-hidden="true"
81
+ {...props}
82
+ >
83
+ <use href={href} />
84
+ </svg>
85
+ );
86
+ }
87
+ `;
88
+ // Write component file
89
+ const outputDir = path.dirname(absoluteOutputFile);
90
+ fs.mkdirSync(outputDir, { recursive: true });
91
+ fs.writeFileSync(absoluteOutputFile, content, "utf8");
92
+ if (verbose) {
93
+ console.log(`[svg-sprite] ✅ Generated Icon component → ${outputFile}`);
94
+ }
95
+ }
96
+ catch (error) {
97
+ if (verbose) {
98
+ console.error("[svg-sprite] Error generating Icon component:", error);
99
+ }
100
+ throw error;
101
+ }
102
+ }
package/dist/core.d.ts ADDED
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Represents an SVG symbol with its metadata
3
+ */
4
+ export interface SvgSymbol {
5
+ id: string;
6
+ originalId: string;
7
+ viewBox: string;
8
+ content: string;
9
+ attributes: Record<string, string>;
10
+ }
11
+ /**
12
+ * Options for sprite generation
13
+ */
14
+ export interface SpriteGenerationOptions {
15
+ /**
16
+ * Directory containing SVG files to include in the sprite.
17
+ * Can be relative or absolute path.
18
+ */
19
+ inputDir: string;
20
+ /**
21
+ * Output path for the sprite file.
22
+ * Can be relative or absolute path.
23
+ */
24
+ outputFile: string;
25
+ /**
26
+ * Whether to log output messages
27
+ * @default true
28
+ */
29
+ verbose?: boolean;
30
+ /**
31
+ * Whether to minify the output
32
+ * @default true
33
+ */
34
+ minify?: boolean;
35
+ /**
36
+ * Whether to optimize/compress the SVG content
37
+ * @default true
38
+ */
39
+ optimize?: boolean;
40
+ }
41
+ /**
42
+ * Generate a symbol ID from a file path relative to a base directory.
43
+ * Converts the path to kebab-case and sanitizes it.
44
+ *
45
+ * @example
46
+ * generateSymbolIdFromPath("/path/to/icons/social/github.svg", "/path/to/icons")
47
+ * // Returns: "social-github"
48
+ */
49
+ export declare function generateSymbolIdFromPath(filePath: string, baseDir: string): string;
50
+ /**
51
+ * Parse an SVG file and extract its symbol data
52
+ */
53
+ export declare function parseSvgFile(svgContent: string, originalId: string, shortId: string, shouldOptimize?: boolean): SvgSymbol;
54
+ /**
55
+ * Generate the SVG sprite content from symbols
56
+ */
57
+ export declare function generateSpriteSvg(symbols: SvgSymbol[], minify?: boolean): string;
58
+ /**
59
+ * Find and parse all SVG files in a directory
60
+ */
61
+ export declare function findAndParseSvgFiles(inputDir: string, verbose?: boolean, optimize?: boolean): SvgSymbol[];
62
+ /**
63
+ * Generate an SVG sprite file from a directory of SVG files
64
+ */
65
+ export declare function generateSprite(options: SpriteGenerationOptions): SvgSymbol[];
package/dist/core.js ADDED
@@ -0,0 +1,245 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.generateSymbolIdFromPath = generateSymbolIdFromPath;
37
+ exports.parseSvgFile = parseSvgFile;
38
+ exports.generateSpriteSvg = generateSpriteSvg;
39
+ exports.findAndParseSvgFiles = findAndParseSvgFiles;
40
+ exports.generateSprite = generateSprite;
41
+ const fs = __importStar(require("fs"));
42
+ const path = __importStar(require("path"));
43
+ const cheerio = __importStar(require("cheerio"));
44
+ const glob_1 = require("glob");
45
+ /**
46
+ * Generate a symbol ID from a file path relative to a base directory.
47
+ * Converts the path to kebab-case and sanitizes it.
48
+ *
49
+ * @example
50
+ * generateSymbolIdFromPath("/path/to/icons/social/github.svg", "/path/to/icons")
51
+ * // Returns: "social-github"
52
+ */
53
+ function generateSymbolIdFromPath(filePath, baseDir) {
54
+ const relativePath = path.relative(baseDir, filePath);
55
+ const pathWithoutExt = relativePath.replace(/\.svg$/, "");
56
+ const symbolId = pathWithoutExt
57
+ .replace(/[\/\\]/g, "-")
58
+ .replace(/[^a-zA-Z0-9-_]/g, "-")
59
+ .replace(/--+/g, "-")
60
+ .replace(/^-+|-+$/g, "");
61
+ return symbolId;
62
+ }
63
+ /**
64
+ * Generate a short, unique ID for a symbol
65
+ * Uses base-52 encoding (a-z, A-Z) for compact IDs
66
+ */
67
+ function generateShortId(index) {
68
+ const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
69
+ const maxIndex = chars.length;
70
+ let id = "";
71
+ let num = index;
72
+ do {
73
+ id = chars[num % maxIndex] + id;
74
+ num = Math.floor(num / maxIndex);
75
+ } while (num > 0);
76
+ return id;
77
+ }
78
+ /**
79
+ * Optimize SVG content by removing unnecessary whitespace and simplifying values
80
+ */
81
+ function optimizeSvgContent(content) {
82
+ return (content
83
+ // Remove comments
84
+ .replace(/<!--[\s\S]*?-->/g, "")
85
+ // Remove unnecessary whitespace between tags
86
+ .replace(/>\s+</g, "><")
87
+ // Simplify numbers: remove trailing zeros after decimal
88
+ .replace(/(\d+\.\d*?)0+(?=\D)/g, "$1")
89
+ // Remove unnecessary decimal points (1.0 -> 1)
90
+ .replace(/(\d+)\.0+(?=\D)/g, "$1")
91
+ // Compress multiple spaces to single space
92
+ .replace(/\s+/g, " ")
93
+ // Remove spaces around equals signs in attributes
94
+ .replace(/\s*=\s*/g, "=")
95
+ // Remove leading/trailing whitespace
96
+ .trim());
97
+ }
98
+ /**
99
+ * Parse an SVG file and extract its symbol data
100
+ */
101
+ function parseSvgFile(svgContent, originalId, shortId, shouldOptimize = true) {
102
+ // Optimize content if requested
103
+ const processedContent = shouldOptimize
104
+ ? optimizeSvgContent(svgContent)
105
+ : svgContent;
106
+ const $ = cheerio.load(processedContent, { xmlMode: true });
107
+ const $svg = $("svg").first();
108
+ if ($svg.length === 0) {
109
+ throw new Error("Invalid SVG content");
110
+ }
111
+ let viewBox = $svg.attr("viewBox");
112
+ if (!viewBox) {
113
+ const width = $svg.attr("width") || "24";
114
+ const height = $svg.attr("height") || "24";
115
+ viewBox = `0 0 ${width} ${height}`;
116
+ }
117
+ const attributes = {};
118
+ const svgAttribs = $svg.get(0)?.attribs || {};
119
+ const excludeAttribs = new Set([
120
+ "viewBox",
121
+ "width",
122
+ "height",
123
+ "xmlns",
124
+ "xmlns:xlink",
125
+ ]);
126
+ for (const [key, value] of Object.entries(svgAttribs)) {
127
+ if (!excludeAttribs.has(key) && value) {
128
+ attributes[key] = value;
129
+ }
130
+ }
131
+ return {
132
+ id: shortId,
133
+ originalId: originalId,
134
+ viewBox: viewBox,
135
+ content: $svg.html() || "",
136
+ attributes: attributes,
137
+ };
138
+ }
139
+ /**
140
+ * Generate the SVG sprite content from symbols
141
+ */
142
+ function generateSpriteSvg(symbols, minify = true) {
143
+ if (symbols.length === 0) {
144
+ return minify
145
+ ? '<svg xmlns="http://www.w3.org/2000/svg" style="display:none"></svg>'
146
+ : `<svg xmlns="http://www.w3.org/2000/svg" class="svg-sprite" style="display: none;">
147
+ </svg>`;
148
+ }
149
+ const symbolElements = symbols
150
+ .map((symbol) => {
151
+ let attributesStr = "";
152
+ for (const [key, value] of Object.entries(symbol.attributes)) {
153
+ attributesStr += ` ${key}="${value}"`;
154
+ }
155
+ return `<symbol id="${symbol.id}" viewBox="${symbol.viewBox}"${attributesStr}>${symbol.content}</symbol>`;
156
+ })
157
+ .join(minify ? "" : "\n ");
158
+ if (minify) {
159
+ return `<svg xmlns="http://www.w3.org/2000/svg" style="display:none">${symbolElements}</svg>`;
160
+ }
161
+ return `<svg xmlns="http://www.w3.org/2000/svg" class="svg-sprite" style="display: none;">
162
+ ${symbolElements}
163
+ </svg>`;
164
+ }
165
+ /**
166
+ * Find and parse all SVG files in a directory
167
+ */
168
+ function findAndParseSvgFiles(inputDir, verbose = true, optimize = true) {
169
+ const absoluteInputDir = path.resolve(process.cwd(), inputDir);
170
+ // Check if input directory exists
171
+ if (!fs.existsSync(absoluteInputDir)) {
172
+ if (verbose) {
173
+ console.warn(`Input directory not found: ${inputDir}`);
174
+ }
175
+ return [];
176
+ }
177
+ // Find all SVG files
178
+ const svgFiles = (0, glob_1.globSync)("**/*.svg", {
179
+ cwd: absoluteInputDir,
180
+ absolute: true,
181
+ });
182
+ if (svgFiles.length === 0) {
183
+ if (verbose) {
184
+ console.warn(`No SVG files found in ${inputDir}`);
185
+ }
186
+ return [];
187
+ }
188
+ if (verbose) {
189
+ console.log(`Found ${svgFiles.length} SVG files in ${inputDir}`);
190
+ }
191
+ // Parse all SVG files
192
+ const symbols = [];
193
+ for (let i = 0; i < svgFiles.length; i++) {
194
+ const svgFile = svgFiles[i];
195
+ try {
196
+ const content = fs.readFileSync(svgFile, "utf8");
197
+ const originalId = generateSymbolIdFromPath(svgFile, absoluteInputDir);
198
+ const shortId = generateShortId(i);
199
+ const symbol = parseSvgFile(content, originalId, shortId, optimize);
200
+ symbols.push(symbol);
201
+ }
202
+ catch (error) {
203
+ if (verbose) {
204
+ console.error(`Error parsing ${svgFile}:`, error);
205
+ }
206
+ }
207
+ }
208
+ return symbols;
209
+ }
210
+ /**
211
+ * Generate an SVG sprite file from a directory of SVG files
212
+ */
213
+ function generateSprite(options) {
214
+ const { inputDir, outputFile, verbose = true, minify = true, optimize = true, } = options;
215
+ try {
216
+ const projectRoot = process.cwd();
217
+ const absoluteOutputFile = path.resolve(projectRoot, outputFile);
218
+ // Find and parse SVG files
219
+ const symbols = findAndParseSvgFiles(inputDir, verbose, optimize);
220
+ if (symbols.length === 0) {
221
+ return symbols;
222
+ }
223
+ // Generate sprite
224
+ let spriteContent = generateSpriteSvg(symbols, minify);
225
+ // Additional optimization on final sprite
226
+ if (optimize) {
227
+ spriteContent = optimizeSvgContent(spriteContent);
228
+ }
229
+ // Write sprite file
230
+ const outputDir = path.dirname(absoluteOutputFile);
231
+ fs.mkdirSync(outputDir, { recursive: true });
232
+ fs.writeFileSync(absoluteOutputFile, spriteContent, "utf8");
233
+ if (verbose) {
234
+ const sizeKB = (Buffer.byteLength(spriteContent, "utf8") / 1024).toFixed(2);
235
+ console.log(`✅ Generated sprite with ${symbols.length} symbols (${sizeKB} KB) → ${outputFile}`);
236
+ }
237
+ return symbols;
238
+ }
239
+ catch (error) {
240
+ if (verbose) {
241
+ console.error("Error generating sprite:", error);
242
+ }
243
+ throw error;
244
+ }
245
+ }
package/dist/next.d.ts ADDED
@@ -0,0 +1,111 @@
1
+ interface NextConfig {
2
+ [key: string]: any;
3
+ }
4
+ interface SpriteLoaderOptions {
5
+ /**
6
+ * URL path where the sprite file will be served from.
7
+ * @default "/"
8
+ */
9
+ url?: string;
10
+ /**
11
+ * Filename for the sprite file.
12
+ * @default "sprite.svg"
13
+ */
14
+ filename?: string;
15
+ /**
16
+ * Directory containing SVG files to include in the sprite.
17
+ * Relative to project root.
18
+ * @default "public/icons"
19
+ */
20
+ inputDir?: string;
21
+ /**
22
+ * Output path for the sprite file.
23
+ * Relative to project root.
24
+ * @default "public/sprite.svg"
25
+ */
26
+ outputFile?: string;
27
+ /**
28
+ * Output path for the TypeScript types file.
29
+ * Relative to project root.
30
+ * @default "generated/icons.ts"
31
+ */
32
+ typesOutputFile?: string;
33
+ /**
34
+ * Whether to generate the Icon React component.
35
+ * @default true
36
+ */
37
+ generateIconComponent?: boolean;
38
+ /**
39
+ * Output path for the Icon component file.
40
+ * Relative to project root.
41
+ * @default "generated/Icon.tsx"
42
+ */
43
+ iconComponentOutputFile?: string;
44
+ }
45
+ /**
46
+ * Generates SVG sprite and TypeScript types for Next.js projects
47
+ *
48
+ * This function generates:
49
+ * 1. An SVG sprite file containing all icons
50
+ * 2. A TypeScript file with typed icon IDs and helper functions
51
+ *
52
+ * No loader is needed - just import the typed icon references directly.
53
+ *
54
+ * @param nextConfig - The existing Next.js configuration object
55
+ * @param options - Optional sprite generation configuration
56
+ * @returns Next.js configuration (unchanged, sprite generation happens during config)
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * // next.config.ts
61
+ * import { withSpriteLoader } from '@tinloof/typed-svg-sprite/next';
62
+ *
63
+ * export default withSpriteLoader({
64
+ * // your existing Next.js config
65
+ * });
66
+ *
67
+ * // Then in your components:
68
+ * import { HOME, SETTINGS } from '@/generated/icons';
69
+ * import { Icon } from '@/generated/Icon'; // Icon component is auto-generated
70
+ *
71
+ * function MyComponent() {
72
+ * return (
73
+ * <>
74
+ * <Icon href={HOME} />
75
+ * <Icon href={SETTINGS} />
76
+ * </>
77
+ * );
78
+ * }
79
+ * ```
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * // With custom options
84
+ * export default withSpriteLoader(
85
+ * {
86
+ * // your existing Next.js config
87
+ * },
88
+ * {
89
+ * inputDir: "assets/icons",
90
+ * outputFile: "public/icons-sprite.svg",
91
+ * typesOutputFile: "lib/icons.ts",
92
+ * iconComponentOutputFile: "components/Icon.tsx",
93
+ * url: "/",
94
+ * filename: "icons-sprite.svg"
95
+ * }
96
+ * );
97
+ * ```
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * // Disable Icon component generation
102
+ * export default withSpriteLoader(
103
+ * { /* your config *\/ },
104
+ * {
105
+ * generateIconComponent: false
106
+ * }
107
+ * );
108
+ * ```
109
+ */
110
+ export declare function withSpriteLoader(nextConfig: any, options?: SpriteLoaderOptions): NextConfig;
111
+ export {};
package/dist/next.js ADDED
@@ -0,0 +1,202 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.withSpriteLoader = withSpriteLoader;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const core_js_1 = require("./core.js");
40
+ const types_js_1 = require("./types.js");
41
+ const components_js_1 = require("./components.js");
42
+ // Track if we've already started watching
43
+ let watcherInitialized = false;
44
+ function generateSpriteAndTypes(inputDir, outputFile, spriteUrl, spriteFilename, typesOutputFile, generateIcon, iconComponentOutputFile) {
45
+ // Generate sprite and get symbols
46
+ const symbols = (0, core_js_1.generateSprite)({
47
+ inputDir,
48
+ outputFile,
49
+ verbose: true,
50
+ minify: true,
51
+ optimize: true,
52
+ });
53
+ // Generate TypeScript types file if specified
54
+ if (typesOutputFile && symbols.length > 0) {
55
+ (0, types_js_1.generateTypesFile)({
56
+ symbols,
57
+ spriteUrl,
58
+ spriteFilename,
59
+ outputFile: typesOutputFile,
60
+ verbose: false,
61
+ });
62
+ // Generate Icon component if enabled
63
+ if (generateIcon && iconComponentOutputFile) {
64
+ // Calculate relative path from component to types file
65
+ const componentDir = path.dirname(path.resolve(process.cwd(), iconComponentOutputFile));
66
+ const typesFile = path.resolve(process.cwd(), typesOutputFile);
67
+ const typesDir = path.dirname(typesFile);
68
+ const typesFilename = path.basename(typesFile, path.extname(typesFile));
69
+ // Get relative path from component dir to types dir
70
+ const relativePath = path
71
+ .relative(componentDir, typesDir)
72
+ .replace(/\\/g, "/");
73
+ // Construct the import path
74
+ // If same directory, use ./filename
75
+ // If parent/child relationship, ensure proper path format
76
+ let importPath;
77
+ if (relativePath === "") {
78
+ importPath = `./${typesFilename}`;
79
+ }
80
+ else if (relativePath.startsWith("..")) {
81
+ // Parent directory - ensure proper path
82
+ importPath = `${relativePath}/${typesFilename}`.replace(/\/+/g, "/");
83
+ }
84
+ else {
85
+ // Child directory - ensure it starts with ./
86
+ const normalizedPath = relativePath.startsWith("./")
87
+ ? relativePath
88
+ : `./${relativePath}`;
89
+ importPath = `${normalizedPath}/${typesFilename}`.replace(/\/+/g, "/");
90
+ }
91
+ (0, components_js_1.generateIconComponent)({
92
+ outputFile: iconComponentOutputFile,
93
+ typesFileRelativePath: importPath,
94
+ verbose: false,
95
+ });
96
+ }
97
+ }
98
+ }
99
+ function startWatcher(inputDir, outputFile, spriteUrl, spriteFilename, typesOutputFile, generateIcon, iconComponentOutputFile) {
100
+ if (watcherInitialized) {
101
+ return;
102
+ }
103
+ const projectRoot = process.cwd();
104
+ const absoluteInputDir = path.resolve(projectRoot, inputDir);
105
+ if (!fs.existsSync(absoluteInputDir)) {
106
+ return;
107
+ }
108
+ watcherInitialized = true;
109
+ console.log(`[svg-sprite] 👀 Watching ${inputDir} for changes...`);
110
+ fs.watch(absoluteInputDir, { recursive: true }, (eventType, filename) => {
111
+ if (filename?.endsWith(".svg")) {
112
+ console.log(`[svg-sprite] Change detected: ${filename}`);
113
+ generateSpriteAndTypes(inputDir, outputFile, spriteUrl, spriteFilename, typesOutputFile, generateIcon, iconComponentOutputFile);
114
+ }
115
+ });
116
+ }
117
+ /**
118
+ * Generates SVG sprite and TypeScript types for Next.js projects
119
+ *
120
+ * This function generates:
121
+ * 1. An SVG sprite file containing all icons
122
+ * 2. A TypeScript file with typed icon IDs and helper functions
123
+ *
124
+ * No loader is needed - just import the typed icon references directly.
125
+ *
126
+ * @param nextConfig - The existing Next.js configuration object
127
+ * @param options - Optional sprite generation configuration
128
+ * @returns Next.js configuration (unchanged, sprite generation happens during config)
129
+ *
130
+ * @example
131
+ * ```typescript
132
+ * // next.config.ts
133
+ * import { withSpriteLoader } from '@tinloof/typed-svg-sprite/next';
134
+ *
135
+ * export default withSpriteLoader({
136
+ * // your existing Next.js config
137
+ * });
138
+ *
139
+ * // Then in your components:
140
+ * import { HOME, SETTINGS } from '@/generated/icons';
141
+ * import { Icon } from '@/generated/Icon'; // Icon component is auto-generated
142
+ *
143
+ * function MyComponent() {
144
+ * return (
145
+ * <>
146
+ * <Icon href={HOME} />
147
+ * <Icon href={SETTINGS} />
148
+ * </>
149
+ * );
150
+ * }
151
+ * ```
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * // With custom options
156
+ * export default withSpriteLoader(
157
+ * {
158
+ * // your existing Next.js config
159
+ * },
160
+ * {
161
+ * inputDir: "assets/icons",
162
+ * outputFile: "public/icons-sprite.svg",
163
+ * typesOutputFile: "lib/icons.ts",
164
+ * iconComponentOutputFile: "components/Icon.tsx",
165
+ * url: "/",
166
+ * filename: "icons-sprite.svg"
167
+ * }
168
+ * );
169
+ * ```
170
+ *
171
+ * @example
172
+ * ```typescript
173
+ * // Disable Icon component generation
174
+ * export default withSpriteLoader(
175
+ * { /* your config *\/ },
176
+ * {
177
+ * generateIconComponent: false
178
+ * }
179
+ * );
180
+ * ```
181
+ */
182
+ function withSpriteLoader(nextConfig, options) {
183
+ const inputDir = options?.inputDir ?? "public/icons";
184
+ const outputFile = options?.outputFile ?? "public/sprite.svg";
185
+ const spriteUrl = options?.url ?? "/";
186
+ const spriteFilename = options?.filename ?? "sprite.svg";
187
+ const typesOutputFile = options?.typesOutputFile ?? "generated/icons.ts";
188
+ const generateIcon = options?.generateIconComponent !== false
189
+ ? options?.generateIconComponent ?? true
190
+ : false;
191
+ const iconComponentOutputFile = options?.iconComponentOutputFile ?? "generated/Icon.tsx";
192
+ // Generate sprite and types on startup
193
+ generateSpriteAndTypes(inputDir, outputFile, spriteUrl, spriteFilename, typesOutputFile, generateIcon, generateIcon ? iconComponentOutputFile : undefined);
194
+ // Start watcher in dev mode
195
+ const isDev = process.env.NODE_ENV === "development";
196
+ if (isDev) {
197
+ startWatcher(inputDir, outputFile, spriteUrl, spriteFilename, typesOutputFile, generateIcon, generateIcon ? iconComponentOutputFile : undefined);
198
+ }
199
+ // Return config without loader modifications
200
+ // Users should import from the generated types file instead
201
+ return nextConfig;
202
+ }
@@ -0,0 +1,34 @@
1
+ import type { SvgSymbol } from "./core.js";
2
+ /**
3
+ * Options for TypeScript types generation
4
+ */
5
+ export interface TypesGenerationOptions {
6
+ /**
7
+ * Symbols to generate types for
8
+ */
9
+ symbols: SvgSymbol[];
10
+ /**
11
+ * URL path where the sprite file will be served from
12
+ * @example "/"
13
+ */
14
+ spriteUrl: string;
15
+ /**
16
+ * Filename of the sprite file
17
+ * @example "sprite.svg"
18
+ */
19
+ spriteFilename: string;
20
+ /**
21
+ * Output path for the TypeScript types file
22
+ * Can be relative or absolute path
23
+ */
24
+ outputFile: string;
25
+ /**
26
+ * Whether to log output messages
27
+ * @default true
28
+ */
29
+ verbose?: boolean;
30
+ }
31
+ /**
32
+ * Generate TypeScript types file from SVG symbols
33
+ */
34
+ export declare function generateTypesFile(options: TypesGenerationOptions): void;
package/dist/types.js ADDED
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.generateTypesFile = generateTypesFile;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ /**
40
+ * Generate TypeScript types file from SVG symbols
41
+ */
42
+ function generateTypesFile(options) {
43
+ const { symbols, spriteUrl, spriteFilename, outputFile, verbose = true, } = options;
44
+ try {
45
+ const projectRoot = process.cwd();
46
+ const absoluteOutputFile = path.resolve(projectRoot, outputFile);
47
+ const spriteUrlWithFilename = spriteUrl.endsWith("/")
48
+ ? `${spriteUrl}${spriteFilename}`
49
+ : `${spriteUrl}/${spriteFilename}`;
50
+ // Generate TypeScript content
51
+ const enumEntries = symbols
52
+ .map((s) => {
53
+ // Convert kebab-case to UPPER_SNAKE_CASE for enum key (use originalId)
54
+ const enumKey = s.originalId.replace(/-/g, "_").toUpperCase();
55
+ // But reference the short ID in the value
56
+ return ` ${enumKey} = "${s.id}",`;
57
+ })
58
+ .join("\n");
59
+ const iconExports = symbols
60
+ .map((s) => {
61
+ const constantName = s.originalId.replace(/-/g, "_").toUpperCase();
62
+ return `export const ${constantName}: IconHref = "${spriteUrlWithFilename}#${s.id}";`;
63
+ })
64
+ .join("\n");
65
+ const content = `// Auto-generated by typed-svg-sprite
66
+ // Do not edit this file manually
67
+
68
+ /**
69
+ * Available icon IDs in the sprite
70
+ */
71
+ export enum IconId {
72
+ ${enumEntries}
73
+ }
74
+
75
+ /**
76
+ * Type-safe icon href (sprite URL + fragment identifier)
77
+ */
78
+ export type IconHref = \`${spriteUrlWithFilename}#\${IconId}\`;
79
+
80
+ /**
81
+ * Get the full sprite URL for an icon ID
82
+ */
83
+ export function getIconHref(iconId: IconId): IconHref {
84
+ return \`${spriteUrlWithFilename}#\${iconId}\` as IconHref;
85
+ }
86
+
87
+ /**
88
+ * Pre-built icon references
89
+ */
90
+ ${iconExports}
91
+
92
+ /**
93
+ * All available icons as an array
94
+ */
95
+ export const allIcons: IconId[] = [
96
+ ${symbols
97
+ .map((s) => ` IconId.${s.originalId.replace(/-/g, "_").toUpperCase()},`)
98
+ .join("\n")}
99
+ ];
100
+ `;
101
+ // Write types file
102
+ const outputDir = path.dirname(absoluteOutputFile);
103
+ fs.mkdirSync(outputDir, { recursive: true });
104
+ fs.writeFileSync(absoluteOutputFile, content, "utf8");
105
+ if (verbose) {
106
+ console.log(`[svg-sprite] ✅ Generated types file → ${outputFile}`);
107
+ }
108
+ }
109
+ catch (error) {
110
+ if (verbose) {
111
+ console.error("[svg-sprite] Error generating types file:", error);
112
+ }
113
+ throw error;
114
+ }
115
+ }
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@tinloof/typed-svg-sprite",
3
+ "version": "0.0.1",
4
+ "description": "Generate optimized SVG sprites with full TypeScript support",
5
+ "bin": {
6
+ "typed-svg-sprite": "./dist/cli.js"
7
+ },
8
+ "exports": {
9
+ "./next": {
10
+ "types": "./dist/next.d.ts",
11
+ "default": "./dist/next.js"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "prepare": "npm run build",
17
+ "clean": "rimraf dist",
18
+ "changeset": "changeset",
19
+ "version": "changeset version",
20
+ "release": "npm run build && changeset publish"
21
+ },
22
+ "files": [
23
+ "dist/",
24
+ "README.md",
25
+ "CHANGELOG.md"
26
+ ],
27
+ "keywords": [
28
+ "svg",
29
+ "sprite",
30
+ "icon",
31
+ "typescript",
32
+ "nextjs",
33
+ "cli"
34
+ ],
35
+ "author": "LHDi <elhadi98@gmail.com>",
36
+ "license": "MIT",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/tinloof/typed-svg-sprite.git"
40
+ },
41
+ "bugs": {
42
+ "url": "https://github.com/tinloof/typed-svg-sprite/issues"
43
+ },
44
+ "homepage": "https://github.com/tinloof/typed-svg-sprite#readme",
45
+ "engines": {
46
+ "node": ">=14.0.0"
47
+ },
48
+ "dependencies": {
49
+ "cheerio": "^1.1.2",
50
+ "glob": "^10.3.10"
51
+ },
52
+ "peerDependenciesMeta": {
53
+ "next": {
54
+ "optional": true
55
+ }
56
+ },
57
+ "devDependencies": {
58
+ "@changesets/cli": "^2.29.7",
59
+ "@types/cheerio": "^0.22.35",
60
+ "@types/node": "^20.0.0",
61
+ "rimraf": "^5.0.5",
62
+ "typescript": "^5.0.0"
63
+ }
64
+ }