svger-cli 2.0.1 → 2.0.3

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.
Files changed (91) hide show
  1. package/.svgerconfig.example.json +38 -0
  2. package/CHANGELOG.md +64 -0
  3. package/DEVELOPMENT.md +353 -0
  4. package/README.md +24 -5
  5. package/SECURITY.md +69 -0
  6. package/dist/builder.js +16 -16
  7. package/dist/clean.js +2 -2
  8. package/dist/cli.js +38 -38
  9. package/dist/config.js +11 -11
  10. package/dist/core/error-handler.d.ts +63 -0
  11. package/dist/core/error-handler.js +227 -0
  12. package/dist/core/framework-templates.d.ts +17 -0
  13. package/{src/core/framework-templates.ts → dist/core/framework-templates.js} +104 -139
  14. package/dist/core/logger.d.ts +22 -0
  15. package/dist/core/logger.js +85 -0
  16. package/dist/core/performance-engine.d.ts +67 -0
  17. package/dist/core/performance-engine.js +252 -0
  18. package/dist/core/plugin-manager.d.ts +56 -0
  19. package/dist/core/plugin-manager.js +191 -0
  20. package/dist/core/style-compiler.d.ts +88 -0
  21. package/dist/core/style-compiler.js +468 -0
  22. package/dist/core/template-manager.d.ts +64 -0
  23. package/{src/core/template-manager.ts → dist/core/template-manager.js} +172 -255
  24. package/dist/index.d.ts +153 -0
  25. package/{src/index.ts → dist/index.js} +32 -110
  26. package/dist/lock.js +7 -7
  27. package/dist/processors/svg-processor.d.ts +73 -0
  28. package/dist/processors/svg-processor.js +261 -0
  29. package/dist/services/config.d.ts +55 -0
  30. package/dist/services/config.js +211 -0
  31. package/dist/services/file-watcher.d.ts +54 -0
  32. package/dist/services/file-watcher.js +180 -0
  33. package/dist/services/svg-service.d.ts +81 -0
  34. package/dist/services/svg-service.js +395 -0
  35. package/dist/templates/ComponentTemplate.js +25 -25
  36. package/dist/types/index.d.ts +146 -0
  37. package/dist/types/index.js +4 -0
  38. package/dist/utils/native.d.ts +104 -0
  39. package/dist/utils/native.js +340 -0
  40. package/dist/watch.d.ts +1 -1
  41. package/dist/watch.js +14 -14
  42. package/package.json +154 -14
  43. package/.svgconfig.json +0 -3
  44. package/CODE_OF_CONDUCT.md +0 -79
  45. package/CONTRIBUTING.md +0 -146
  46. package/TESTING.md +0 -143
  47. package/cli-framework.test.js +0 -16
  48. package/cli-test-angular/Arrowbenddownleft.component.ts +0 -27
  49. package/cli-test-angular/Vite.component.ts +0 -27
  50. package/cli-test-angular/index.ts +0 -25
  51. package/cli-test-output/Arrowbenddownleft.vue +0 -33
  52. package/cli-test-output/Vite.vue +0 -33
  53. package/cli-test-output/index.ts +0 -25
  54. package/cli-test-react/Arrowbenddownleft.tsx +0 -39
  55. package/cli-test-react/Vite.tsx +0 -39
  56. package/cli-test-react/index.ts +0 -25
  57. package/cli-test-svelte/Arrowbenddownleft.svelte +0 -22
  58. package/cli-test-svelte/Vite.svelte +0 -22
  59. package/cli-test-svelte/index.ts +0 -25
  60. package/frameworks.test.js +0 -170
  61. package/my-svgs/ArrowBendDownLeft.svg +0 -6
  62. package/my-svgs/vite.svg +0 -1
  63. package/src/builder.ts +0 -104
  64. package/src/clean.ts +0 -21
  65. package/src/cli.ts +0 -221
  66. package/src/config.ts +0 -81
  67. package/src/core/error-handler.ts +0 -303
  68. package/src/core/logger.ts +0 -104
  69. package/src/core/performance-engine.ts +0 -327
  70. package/src/core/plugin-manager.ts +0 -228
  71. package/src/core/style-compiler.ts +0 -605
  72. package/src/lock.ts +0 -74
  73. package/src/processors/svg-processor.ts +0 -288
  74. package/src/services/config.ts +0 -241
  75. package/src/services/file-watcher.ts +0 -218
  76. package/src/services/svg-service.ts +0 -468
  77. package/src/templates/ComponentTemplate.ts +0 -57
  78. package/src/types/index.ts +0 -169
  79. package/src/utils/native.ts +0 -352
  80. package/src/watch.ts +0 -88
  81. package/test-output-mulit/TestIcon-angular-module.component.ts +0 -26
  82. package/test-output-mulit/TestIcon-angular-standalone.component.ts +0 -27
  83. package/test-output-mulit/TestIcon-lit.ts +0 -35
  84. package/test-output-mulit/TestIcon-preact.tsx +0 -38
  85. package/test-output-mulit/TestIcon-react.tsx +0 -35
  86. package/test-output-mulit/TestIcon-solid.tsx +0 -27
  87. package/test-output-mulit/TestIcon-svelte.svelte +0 -22
  88. package/test-output-mulit/TestIcon-vanilla.ts +0 -37
  89. package/test-output-mulit/TestIcon-vue-composition.vue +0 -33
  90. package/test-output-mulit/TestIcon-vue-options.vue +0 -31
  91. package/tsconfig.json +0 -18
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Native Node.js utilities to replace external dependencies
3
+ */
4
+ /**
5
+ * Convert a string to PascalCase.
6
+ * Handles kebab-case, snake_case, and space-separated strings.
7
+ *
8
+ * @param {string} str - Input string to convert.
9
+ * @returns {string} PascalCase string.
10
+ *
11
+ * @example
12
+ * toPascalCase('hello-world') => 'HelloWorld'
13
+ * toPascalCase('hello_world') => 'HelloWorld'
14
+ * toPascalCase('hello world') => 'HelloWorld'
15
+ */
16
+ export declare function toPascalCase(str: string): string;
17
+ /**
18
+ * Convert a string to camelCase.
19
+ *
20
+ * @param {string} str - Input string to convert.
21
+ * @returns {string} camelCase string.
22
+ *
23
+ * @example
24
+ * toCamelCase('hello-world') => 'helloWorld'
25
+ */
26
+ export declare function toCamelCase(str: string): string;
27
+ /**
28
+ * Convert a string to kebab-case.
29
+ *
30
+ * @param {string} str - Input string to convert.
31
+ * @returns {string} kebab-case string.
32
+ *
33
+ * @example
34
+ * toKebabCase('HelloWorld') => 'hello-world'
35
+ * toKebabCase('hello_world') => 'hello-world'
36
+ */
37
+ export declare function toKebabCase(str: string): string;
38
+ /**
39
+ * Native file system utilities (replaces fs-extra package)
40
+ */
41
+ export declare class FileSystem {
42
+ private static _readFile;
43
+ private static _writeFile;
44
+ private static _readdir;
45
+ private static _stat;
46
+ private static _mkdir;
47
+ private static _rmdir;
48
+ private static _unlink;
49
+ static exists(path: string): Promise<boolean>;
50
+ static readFile(path: string, encoding?: BufferEncoding): Promise<string>;
51
+ static writeFile(path: string, content: string, encoding?: BufferEncoding): Promise<void>;
52
+ static readDir(path: string): Promise<string[]>;
53
+ static ensureDir(dirPath: string): Promise<void>;
54
+ static removeDir(dirPath: string): Promise<void>;
55
+ static emptyDir(dirPath: string): Promise<void>;
56
+ static unlink(filePath: string): Promise<void>;
57
+ static readJSONSync(path: string): any;
58
+ static writeJSONSync(path: string, data: any, options?: {
59
+ spaces?: number;
60
+ }): void;
61
+ static existsSync(path: string): boolean;
62
+ static ensureDirSync(dirPath: string): void;
63
+ }
64
+ /**
65
+ * Simple CLI argument parser (replaces commander package)
66
+ */
67
+ export declare class CLI {
68
+ private commands;
69
+ private programName;
70
+ private programDescription;
71
+ private programVersion;
72
+ name(name: string): this;
73
+ description(desc: string): this;
74
+ version(version: string): this;
75
+ command(signature: string): CommandBuilder;
76
+ addCommand(signature: string, description: string, action: Function, options: Map<string, any>): void;
77
+ parse(): Promise<void>;
78
+ private parseArgs;
79
+ private showHelp;
80
+ }
81
+ declare class CommandBuilder {
82
+ private signature;
83
+ private desc;
84
+ private cli;
85
+ private options;
86
+ constructor(signature: string, cli: CLI);
87
+ description(desc: string): this;
88
+ option(flag: string, description: string): this;
89
+ action(fn: Function): void;
90
+ }
91
+ /**
92
+ * File watcher using native fs.watch (replaces chokidar)
93
+ */
94
+ export declare class FileWatcher {
95
+ private watchers;
96
+ private callbacks;
97
+ watch(path: string, options?: {
98
+ recursive?: boolean;
99
+ }): this;
100
+ on(event: string, callback: Function): this;
101
+ private emit;
102
+ close(): void;
103
+ }
104
+ export {};
@@ -0,0 +1,340 @@
1
+ import fs from 'fs';
2
+ import { promisify } from 'util';
3
+ /**
4
+ * Native Node.js utilities to replace external dependencies
5
+ */
6
+ /**
7
+ * Convert a string to PascalCase.
8
+ * Handles kebab-case, snake_case, and space-separated strings.
9
+ *
10
+ * @param {string} str - Input string to convert.
11
+ * @returns {string} PascalCase string.
12
+ *
13
+ * @example
14
+ * toPascalCase('hello-world') => 'HelloWorld'
15
+ * toPascalCase('hello_world') => 'HelloWorld'
16
+ * toPascalCase('hello world') => 'HelloWorld'
17
+ */
18
+ export function toPascalCase(str) {
19
+ return str
20
+ .replace(/[-_\s]+(.)?/g, (_, char) => (char ? char.toUpperCase() : ''))
21
+ .replace(/^(.)/, char => char.toUpperCase());
22
+ }
23
+ /**
24
+ * Convert a string to camelCase.
25
+ *
26
+ * @param {string} str - Input string to convert.
27
+ * @returns {string} camelCase string.
28
+ *
29
+ * @example
30
+ * toCamelCase('hello-world') => 'helloWorld'
31
+ */
32
+ export function toCamelCase(str) {
33
+ const pascal = toPascalCase(str);
34
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
35
+ }
36
+ /**
37
+ * Convert a string to kebab-case.
38
+ *
39
+ * @param {string} str - Input string to convert.
40
+ * @returns {string} kebab-case string.
41
+ *
42
+ * @example
43
+ * toKebabCase('HelloWorld') => 'hello-world'
44
+ * toKebabCase('hello_world') => 'hello-world'
45
+ */
46
+ export function toKebabCase(str) {
47
+ return str
48
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
49
+ .replace(/[\s_]+/g, '-')
50
+ .toLowerCase();
51
+ }
52
+ /**
53
+ * Native file system utilities (replaces fs-extra package)
54
+ */
55
+ export class FileSystem {
56
+ static _readFile = promisify(fs.readFile);
57
+ static _writeFile = promisify(fs.writeFile);
58
+ static _readdir = promisify(fs.readdir);
59
+ static _stat = promisify(fs.stat);
60
+ static _mkdir = promisify(fs.mkdir);
61
+ static _rmdir = promisify(fs.rmdir);
62
+ static _unlink = promisify(fs.unlink);
63
+ static async exists(path) {
64
+ try {
65
+ await this._stat(path);
66
+ return true;
67
+ }
68
+ catch {
69
+ return false;
70
+ }
71
+ }
72
+ static async readFile(path, encoding = 'utf8') {
73
+ return this._readFile(path, encoding);
74
+ }
75
+ static async writeFile(path, content, encoding = 'utf8') {
76
+ return this._writeFile(path, content, encoding);
77
+ }
78
+ static async readDir(path) {
79
+ return this._readdir(path);
80
+ }
81
+ static async ensureDir(dirPath) {
82
+ try {
83
+ await this._mkdir(dirPath, { recursive: true });
84
+ }
85
+ catch (error) {
86
+ if (error.code !== 'EEXIST') {
87
+ throw error;
88
+ }
89
+ }
90
+ }
91
+ static async removeDir(dirPath) {
92
+ try {
93
+ const files = await this._readdir(dirPath);
94
+ for (const file of files) {
95
+ const filePath = `${dirPath}/${file}`;
96
+ const stats = await this._stat(filePath);
97
+ if (stats.isDirectory()) {
98
+ await this.removeDir(filePath);
99
+ }
100
+ else {
101
+ await this._unlink(filePath);
102
+ }
103
+ }
104
+ await this._rmdir(dirPath);
105
+ }
106
+ catch (error) {
107
+ if (error.code !== 'ENOENT') {
108
+ throw error;
109
+ }
110
+ }
111
+ }
112
+ static async emptyDir(dirPath) {
113
+ if (!(await this.exists(dirPath))) {
114
+ return;
115
+ }
116
+ const files = await this._readdir(dirPath);
117
+ for (const file of files) {
118
+ const filePath = `${dirPath}/${file}`;
119
+ const stats = await this._stat(filePath);
120
+ if (stats.isDirectory()) {
121
+ await this.removeDir(filePath);
122
+ }
123
+ else {
124
+ await this._unlink(filePath);
125
+ }
126
+ }
127
+ }
128
+ static async unlink(filePath) {
129
+ return this._unlink(filePath);
130
+ }
131
+ static readJSONSync(path) {
132
+ try {
133
+ const content = fs.readFileSync(path, 'utf8');
134
+ return JSON.parse(content);
135
+ }
136
+ catch {
137
+ return {};
138
+ }
139
+ }
140
+ static writeJSONSync(path, data, options) {
141
+ const content = JSON.stringify(data, null, options?.spaces || 0);
142
+ fs.writeFileSync(path, content, 'utf8');
143
+ }
144
+ static existsSync(path) {
145
+ try {
146
+ fs.statSync(path);
147
+ return true;
148
+ }
149
+ catch {
150
+ return false;
151
+ }
152
+ }
153
+ static ensureDirSync(dirPath) {
154
+ try {
155
+ fs.mkdirSync(dirPath, { recursive: true });
156
+ }
157
+ catch (error) {
158
+ if (error.code !== 'EEXIST') {
159
+ throw error;
160
+ }
161
+ }
162
+ }
163
+ }
164
+ /**
165
+ * Simple CLI argument parser (replaces commander package)
166
+ */
167
+ export class CLI {
168
+ commands = new Map();
169
+ programName = '';
170
+ programDescription = '';
171
+ programVersion = '';
172
+ name(name) {
173
+ this.programName = name;
174
+ return this;
175
+ }
176
+ description(desc) {
177
+ this.programDescription = desc;
178
+ return this;
179
+ }
180
+ version(version) {
181
+ this.programVersion = version;
182
+ return this;
183
+ }
184
+ command(signature) {
185
+ return new CommandBuilder(signature, this);
186
+ }
187
+ addCommand(signature, description, action, options) {
188
+ const [command] = signature.split(' ');
189
+ this.commands.set(command, {
190
+ description,
191
+ action: action,
192
+ options,
193
+ });
194
+ }
195
+ async parse() {
196
+ const args = process.argv.slice(2);
197
+ if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
198
+ this.showHelp();
199
+ return;
200
+ }
201
+ if (args[0] === '--version' || args[0] === '-v') {
202
+ console.log(this.programVersion);
203
+ return;
204
+ }
205
+ const [commandName, ...remainingArgs] = args;
206
+ const command = this.commands.get(commandName);
207
+ if (!command) {
208
+ console.error(`Unknown command: ${commandName}`);
209
+ this.showHelp();
210
+ process.exit(1);
211
+ }
212
+ const { parsedArgs, options } = this.parseArgs(remainingArgs, command.options);
213
+ try {
214
+ await command.action(parsedArgs, options);
215
+ }
216
+ catch (error) {
217
+ console.error('Command failed:', error);
218
+ process.exit(1);
219
+ }
220
+ }
221
+ parseArgs(args, commandOptions) {
222
+ const parsedArgs = [];
223
+ const options = {};
224
+ let i = 0;
225
+ while (i < args.length) {
226
+ const arg = args[i];
227
+ if (arg.startsWith('--')) {
228
+ const optionName = arg.slice(2);
229
+ const optionConfig = commandOptions.get(optionName);
230
+ if (optionConfig) {
231
+ if (optionConfig.hasValue) {
232
+ options[optionName] = args[i + 1];
233
+ i += 2;
234
+ }
235
+ else {
236
+ options[optionName] = true;
237
+ i++;
238
+ }
239
+ }
240
+ else {
241
+ // Handle key=value format
242
+ if (arg.includes('=')) {
243
+ const [key, value] = arg.slice(2).split('=');
244
+ options[key] = value;
245
+ i++;
246
+ }
247
+ else {
248
+ i++;
249
+ }
250
+ }
251
+ }
252
+ else {
253
+ parsedArgs.push(arg);
254
+ i++;
255
+ }
256
+ }
257
+ return { parsedArgs, options };
258
+ }
259
+ showHelp() {
260
+ console.log(`${this.programName} - ${this.programDescription}`);
261
+ console.log(`Version: ${this.programVersion}\n`);
262
+ console.log('Commands:');
263
+ for (const [name, cmd] of this.commands) {
264
+ console.log(` ${name.padEnd(15)} ${cmd.description}`);
265
+ }
266
+ console.log('\nOptions:');
267
+ console.log(' --help, -h Show help');
268
+ console.log(' --version, -v Show version');
269
+ }
270
+ }
271
+ class CommandBuilder {
272
+ signature;
273
+ desc = '';
274
+ cli;
275
+ options = new Map();
276
+ constructor(signature, cli) {
277
+ this.signature = signature;
278
+ this.cli = cli;
279
+ }
280
+ description(desc) {
281
+ this.desc = desc;
282
+ return this;
283
+ }
284
+ option(flag, description) {
285
+ const hasValue = flag.includes('<') || flag.includes('[');
286
+ const optionName = flag.split(' ')[0].replace(/^--/, '');
287
+ this.options.set(optionName, { description, hasValue });
288
+ return this;
289
+ }
290
+ action(fn) {
291
+ this.cli.addCommand(this.signature, this.desc, fn, this.options);
292
+ }
293
+ }
294
+ /**
295
+ * File watcher using native fs.watch (replaces chokidar)
296
+ */
297
+ export class FileWatcher {
298
+ watchers = [];
299
+ callbacks = new Map();
300
+ watch(path, options) {
301
+ try {
302
+ const watcher = fs.watch(path, {
303
+ recursive: options?.recursive || false,
304
+ persistent: true,
305
+ }, (eventType, filename) => {
306
+ if (filename) {
307
+ this.emit(eventType, `${path}/${filename}`);
308
+ }
309
+ });
310
+ this.watchers.push(watcher);
311
+ }
312
+ catch (error) {
313
+ console.error(`Failed to watch ${path}:`, error);
314
+ }
315
+ return this;
316
+ }
317
+ on(event, callback) {
318
+ if (!this.callbacks.has(event)) {
319
+ this.callbacks.set(event, []);
320
+ }
321
+ this.callbacks.get(event).push(callback);
322
+ return this;
323
+ }
324
+ emit(event, ...args) {
325
+ const callbacks = this.callbacks.get(event) || [];
326
+ callbacks.forEach(callback => {
327
+ try {
328
+ callback(...args);
329
+ }
330
+ catch (error) {
331
+ console.error('Watcher callback error:', error);
332
+ }
333
+ });
334
+ }
335
+ close() {
336
+ this.watchers.forEach(watcher => watcher.close());
337
+ this.watchers = [];
338
+ this.callbacks.clear();
339
+ }
340
+ }
package/dist/watch.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { FileWatcher } from "./utils/native.js";
1
+ import { FileWatcher } from './utils/native.js';
2
2
  /**
3
3
  * Watches a source folder for changes to SVG files and automatically
4
4
  * rebuilds React components when SVGs are added, modified, or deleted.
package/dist/watch.js CHANGED
@@ -1,8 +1,8 @@
1
- import path from "path";
2
- import { generateSVG } from "./builder.js";
3
- import { isLocked } from "./lock.js";
4
- import { readConfig } from "./config.js";
5
- import { FileSystem, FileWatcher } from "./utils/native.js";
1
+ import path from 'path';
2
+ import { generateSVG } from './builder.js';
3
+ import { isLocked } from './lock.js';
4
+ import { readConfig } from './config.js';
5
+ import { FileSystem, FileWatcher } from './utils/native.js';
6
6
  /**
7
7
  * Watches a source folder for changes to SVG files and automatically
8
8
  * rebuilds React components when SVGs are added, modified, or deleted.
@@ -25,19 +25,19 @@ export async function watchSVGs(config) {
25
25
  const outDir = path.resolve(config.out);
26
26
  const svgConfig = readConfig();
27
27
  if (!(await FileSystem.exists(srcDir))) {
28
- console.error("❌ Source folder not found:", srcDir);
28
+ console.error('❌ Source folder not found:', srcDir);
29
29
  process.exit(1);
30
30
  }
31
31
  console.log(`👀 Watching for SVG changes in: ${srcDir}`);
32
- console.log("🚀 Watch mode active — waiting for file changes...");
32
+ console.log('🚀 Watch mode active — waiting for file changes...');
33
33
  const watcher = new FileWatcher();
34
34
  // Watch the directory
35
35
  watcher.watch(srcDir, { recursive: false });
36
36
  // Handle file changes
37
- watcher.on("change", async (filePath) => {
38
- if (!filePath.endsWith(".svg"))
37
+ watcher.on('change', async (filePath) => {
38
+ if (!filePath.endsWith('.svg'))
39
39
  return;
40
- console.log("Detected change in file:", filePath);
40
+ console.log('Detected change in file:', filePath);
41
41
  if (isLocked(filePath)) {
42
42
  console.log(`⚠️ Skipped locked file: ${path.basename(filePath)}`);
43
43
  return;
@@ -46,13 +46,13 @@ export async function watchSVGs(config) {
46
46
  await generateSVG({ svgFile: filePath, outDir });
47
47
  });
48
48
  // Handle new files (rename event in fs.watch can indicate new files)
49
- watcher.on("rename", async (filePath) => {
50
- if (!filePath.endsWith(".svg"))
49
+ watcher.on('rename', async (filePath) => {
50
+ if (!filePath.endsWith('.svg'))
51
51
  return;
52
52
  // Check if file exists (new file) or doesn't exist (deleted file)
53
53
  const exists = await FileSystem.exists(filePath);
54
54
  if (exists) {
55
- console.log("Detected new file:", filePath);
55
+ console.log('Detected new file:', filePath);
56
56
  if (isLocked(filePath)) {
57
57
  console.log(`⚠️ Skipped locked file: ${path.basename(filePath)}`);
58
58
  return;
@@ -62,7 +62,7 @@ export async function watchSVGs(config) {
62
62
  }
63
63
  else {
64
64
  // File was deleted
65
- const componentName = path.basename(filePath, ".svg");
65
+ const componentName = path.basename(filePath, '.svg');
66
66
  const outFile = path.join(outDir, `${componentName}.tsx`);
67
67
  if (await FileSystem.exists(outFile)) {
68
68
  await FileSystem.unlink(outFile);