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.
- package/.svgerconfig.example.json +38 -0
- package/CHANGELOG.md +64 -0
- package/DEVELOPMENT.md +353 -0
- package/README.md +24 -5
- package/SECURITY.md +69 -0
- package/dist/builder.js +16 -16
- package/dist/clean.js +2 -2
- package/dist/cli.js +38 -38
- package/dist/config.js +11 -11
- package/dist/core/error-handler.d.ts +63 -0
- package/dist/core/error-handler.js +227 -0
- package/dist/core/framework-templates.d.ts +17 -0
- package/{src/core/framework-templates.ts → dist/core/framework-templates.js} +104 -139
- package/dist/core/logger.d.ts +22 -0
- package/dist/core/logger.js +85 -0
- package/dist/core/performance-engine.d.ts +67 -0
- package/dist/core/performance-engine.js +252 -0
- package/dist/core/plugin-manager.d.ts +56 -0
- package/dist/core/plugin-manager.js +191 -0
- package/dist/core/style-compiler.d.ts +88 -0
- package/dist/core/style-compiler.js +468 -0
- package/dist/core/template-manager.d.ts +64 -0
- package/{src/core/template-manager.ts → dist/core/template-manager.js} +172 -255
- package/dist/index.d.ts +153 -0
- package/{src/index.ts → dist/index.js} +32 -110
- package/dist/lock.js +7 -7
- package/dist/processors/svg-processor.d.ts +73 -0
- package/dist/processors/svg-processor.js +261 -0
- package/dist/services/config.d.ts +55 -0
- package/dist/services/config.js +211 -0
- package/dist/services/file-watcher.d.ts +54 -0
- package/dist/services/file-watcher.js +180 -0
- package/dist/services/svg-service.d.ts +81 -0
- package/dist/services/svg-service.js +395 -0
- package/dist/templates/ComponentTemplate.js +25 -25
- package/dist/types/index.d.ts +146 -0
- package/dist/types/index.js +4 -0
- package/dist/utils/native.d.ts +104 -0
- package/dist/utils/native.js +340 -0
- package/dist/watch.d.ts +1 -1
- package/dist/watch.js +14 -14
- package/package.json +154 -14
- package/.svgconfig.json +0 -3
- package/CODE_OF_CONDUCT.md +0 -79
- package/CONTRIBUTING.md +0 -146
- package/TESTING.md +0 -143
- package/cli-framework.test.js +0 -16
- package/cli-test-angular/Arrowbenddownleft.component.ts +0 -27
- package/cli-test-angular/Vite.component.ts +0 -27
- package/cli-test-angular/index.ts +0 -25
- package/cli-test-output/Arrowbenddownleft.vue +0 -33
- package/cli-test-output/Vite.vue +0 -33
- package/cli-test-output/index.ts +0 -25
- package/cli-test-react/Arrowbenddownleft.tsx +0 -39
- package/cli-test-react/Vite.tsx +0 -39
- package/cli-test-react/index.ts +0 -25
- package/cli-test-svelte/Arrowbenddownleft.svelte +0 -22
- package/cli-test-svelte/Vite.svelte +0 -22
- package/cli-test-svelte/index.ts +0 -25
- package/frameworks.test.js +0 -170
- package/my-svgs/ArrowBendDownLeft.svg +0 -6
- package/my-svgs/vite.svg +0 -1
- package/src/builder.ts +0 -104
- package/src/clean.ts +0 -21
- package/src/cli.ts +0 -221
- package/src/config.ts +0 -81
- package/src/core/error-handler.ts +0 -303
- package/src/core/logger.ts +0 -104
- package/src/core/performance-engine.ts +0 -327
- package/src/core/plugin-manager.ts +0 -228
- package/src/core/style-compiler.ts +0 -605
- package/src/lock.ts +0 -74
- package/src/processors/svg-processor.ts +0 -288
- package/src/services/config.ts +0 -241
- package/src/services/file-watcher.ts +0 -218
- package/src/services/svg-service.ts +0 -468
- package/src/templates/ComponentTemplate.ts +0 -57
- package/src/types/index.ts +0 -169
- package/src/utils/native.ts +0 -352
- package/src/watch.ts +0 -88
- package/test-output-mulit/TestIcon-angular-module.component.ts +0 -26
- package/test-output-mulit/TestIcon-angular-standalone.component.ts +0 -27
- package/test-output-mulit/TestIcon-lit.ts +0 -35
- package/test-output-mulit/TestIcon-preact.tsx +0 -38
- package/test-output-mulit/TestIcon-react.tsx +0 -35
- package/test-output-mulit/TestIcon-solid.tsx +0 -27
- package/test-output-mulit/TestIcon-svelte.svelte +0 -22
- package/test-output-mulit/TestIcon-vanilla.ts +0 -37
- package/test-output-mulit/TestIcon-vue-composition.vue +0 -33
- package/test-output-mulit/TestIcon-vue-options.vue +0 -31
- package/tsconfig.json +0 -18
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generates a React SVG component template from provided SVG content.
|
|
3
|
-
*
|
|
4
|
-
* This function processes raw SVG content by cleaning it (removing XML declarations,
|
|
5
|
-
* DOCTYPE, extra whitespace, inline styles, and xmlns attributes), then wraps it in
|
|
6
|
-
* a React functional component that accepts standard SVGProps for flexibility.
|
|
7
|
-
*
|
|
8
|
-
* @param options - Configuration object for generating the component.
|
|
9
|
-
* @param options.componentName - The name of the React component (e.g., "IconName").
|
|
10
|
-
* @param options.svgContent - The raw SVG markup as a string.
|
|
11
|
-
* @param [options.defaultWidth=24] - The default width attribute for the SVG.
|
|
12
|
-
* @param [options.defaultHeight=24] - The default height attribute for the SVG.
|
|
13
|
-
* @param [options.defaultFill="currentColor"] - The default fill color for the SVG.
|
|
14
|
-
* @returns A string containing the complete TypeScript code for the React SVG component.
|
|
15
|
-
*/
|
|
16
|
-
export function reactTemplate({
|
|
17
|
-
componentName,
|
|
18
|
-
svgContent,
|
|
19
|
-
defaultWidth = 24,
|
|
20
|
-
defaultHeight = 24,
|
|
21
|
-
defaultFill = "currentColor",
|
|
22
|
-
}: {
|
|
23
|
-
componentName: string;
|
|
24
|
-
svgContent: string;
|
|
25
|
-
defaultWidth?: number;
|
|
26
|
-
defaultHeight?: number;
|
|
27
|
-
defaultFill?: string;
|
|
28
|
-
}) {
|
|
29
|
-
const cleaned = svgContent
|
|
30
|
-
.replace(/<\?xml.*?\?>/g, "")
|
|
31
|
-
.replace(/<!DOCTYPE.*?>/g, "")
|
|
32
|
-
.replace(/\r?\n|\r/g, "")
|
|
33
|
-
.replace(/\s{2,}/g, " ")
|
|
34
|
-
.replace(/style="[^"]*"/g, "")
|
|
35
|
-
.replace(/\s+xmlns(:xlink)?="[^"]*"/g, "")
|
|
36
|
-
.trim()
|
|
37
|
-
.replace(/^.*?<svg[^>]*>(.*?)<\/svg>.*$/i, "$1");
|
|
38
|
-
|
|
39
|
-
return `import type { SVGProps } from "react";
|
|
40
|
-
const Svg${componentName} = (props: SVGProps<SVGSVGElement>) => (
|
|
41
|
-
<svg
|
|
42
|
-
viewBox="0 0 ${defaultWidth} ${defaultHeight}"
|
|
43
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
44
|
-
xmlnsXlink="http://www.w3.org/1999/xlink"
|
|
45
|
-
width={props.width || ${defaultWidth}}
|
|
46
|
-
height={props.height || ${defaultHeight}}
|
|
47
|
-
fill={props.fill || "${defaultFill}"}
|
|
48
|
-
stroke={props.stroke || "none"}
|
|
49
|
-
className={props.className}
|
|
50
|
-
{...props}
|
|
51
|
-
>
|
|
52
|
-
${cleaned}
|
|
53
|
-
</svg>
|
|
54
|
-
);
|
|
55
|
-
export default Svg${componentName};
|
|
56
|
-
`;
|
|
57
|
-
}
|
package/src/types/index.ts
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type definitions for svger-cli
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export type FrameworkType = 'react' | 'vue' | 'svelte' | 'angular' | 'solid' | 'preact' | 'lit' | 'vanilla';
|
|
6
|
-
|
|
7
|
-
export interface SVGConfig {
|
|
8
|
-
source: string;
|
|
9
|
-
output: string;
|
|
10
|
-
watch: boolean;
|
|
11
|
-
framework: FrameworkType;
|
|
12
|
-
typescript: boolean;
|
|
13
|
-
defaultWidth: number;
|
|
14
|
-
defaultHeight: number;
|
|
15
|
-
defaultFill: string;
|
|
16
|
-
exclude: string[];
|
|
17
|
-
styleRules: {
|
|
18
|
-
fill?: string;
|
|
19
|
-
stroke?: string;
|
|
20
|
-
[key: string]: string | undefined;
|
|
21
|
-
};
|
|
22
|
-
plugins?: PluginConfig[];
|
|
23
|
-
template?: TemplateConfig;
|
|
24
|
-
frameworkOptions?: FrameworkOptions;
|
|
25
|
-
errorHandling?: {
|
|
26
|
-
skipOnError: boolean;
|
|
27
|
-
logLevel: 'debug' | 'info' | 'warn' | 'error';
|
|
28
|
-
maxRetries: number;
|
|
29
|
-
};
|
|
30
|
-
performance?: {
|
|
31
|
-
batchSize: number;
|
|
32
|
-
parallel: boolean;
|
|
33
|
-
timeout: number;
|
|
34
|
-
enableCache: boolean;
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface FrameworkOptions {
|
|
39
|
-
// Vue-specific
|
|
40
|
-
composition?: boolean;
|
|
41
|
-
setup?: boolean;
|
|
42
|
-
scriptSetup?: boolean;
|
|
43
|
-
|
|
44
|
-
// Angular-specific
|
|
45
|
-
standalone?: boolean;
|
|
46
|
-
moduleImport?: boolean;
|
|
47
|
-
|
|
48
|
-
// Solid-specific
|
|
49
|
-
signals?: boolean;
|
|
50
|
-
|
|
51
|
-
// React/Preact-specific
|
|
52
|
-
forwardRef?: boolean;
|
|
53
|
-
memo?: boolean;
|
|
54
|
-
|
|
55
|
-
// Lit-specific
|
|
56
|
-
customElement?: boolean;
|
|
57
|
-
shadowDom?: boolean;
|
|
58
|
-
|
|
59
|
-
// General
|
|
60
|
-
cssModules?: boolean;
|
|
61
|
-
styledComponents?: boolean;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export interface BuildOptions {
|
|
65
|
-
src: string;
|
|
66
|
-
out: string;
|
|
67
|
-
config?: Partial<SVGConfig>;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export interface GenerateOptions {
|
|
71
|
-
svgFile: string;
|
|
72
|
-
outDir: string;
|
|
73
|
-
config?: Partial<SVGConfig>;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export interface WatchOptions {
|
|
77
|
-
src: string;
|
|
78
|
-
out: string;
|
|
79
|
-
config?: Partial<SVGConfig>;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export interface ComponentGenerationOptions {
|
|
83
|
-
componentName: string;
|
|
84
|
-
svgContent: string;
|
|
85
|
-
framework: FrameworkType;
|
|
86
|
-
typescript: boolean;
|
|
87
|
-
defaultWidth?: number;
|
|
88
|
-
defaultHeight?: number;
|
|
89
|
-
defaultFill?: string;
|
|
90
|
-
styleRules?: Record<string, string>;
|
|
91
|
-
template?: TemplateConfig;
|
|
92
|
-
frameworkOptions?: FrameworkOptions;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export interface TemplateConfig {
|
|
96
|
-
type: 'default' | 'custom';
|
|
97
|
-
path?: string;
|
|
98
|
-
options?: Record<string, any>;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export interface PluginConfig {
|
|
102
|
-
name: string;
|
|
103
|
-
options?: Record<string, any>;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export interface SVGProcessorResult {
|
|
107
|
-
success: boolean;
|
|
108
|
-
componentName: string;
|
|
109
|
-
filePath: string;
|
|
110
|
-
error?: Error;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export interface FileWatchEvent {
|
|
114
|
-
type: 'add' | 'change' | 'unlink';
|
|
115
|
-
filePath: string;
|
|
116
|
-
timestamp: number;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export interface ProcessingContext {
|
|
120
|
-
config: SVGConfig;
|
|
121
|
-
sourceDir: string;
|
|
122
|
-
outputDir: string;
|
|
123
|
-
fileQueue: string[];
|
|
124
|
-
locks: Set<string>;
|
|
125
|
-
cache?: Map<string, CachedComponent>;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export interface CachedComponent {
|
|
129
|
-
hash: string;
|
|
130
|
-
componentName: string;
|
|
131
|
-
filePath: string;
|
|
132
|
-
framework: FrameworkType;
|
|
133
|
-
timestamp: number;
|
|
134
|
-
svgHash: string;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export interface Logger {
|
|
138
|
-
info(message: string, ...args: any[]): void;
|
|
139
|
-
warn(message: string, ...args: any[]): void;
|
|
140
|
-
error(message: string, ...args: any[]): void;
|
|
141
|
-
success(message: string, ...args: any[]): void;
|
|
142
|
-
debug(message: string, ...args: any[]): void;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export interface Plugin {
|
|
146
|
-
name: string;
|
|
147
|
-
version: string;
|
|
148
|
-
process(content: string, options?: any): Promise<string>;
|
|
149
|
-
validate?(options?: any): boolean;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export interface Template {
|
|
153
|
-
name: string;
|
|
154
|
-
generate(options: ComponentGenerationOptions): string;
|
|
155
|
-
validate?(options: ComponentGenerationOptions): boolean;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export type FileSystemEvent = 'change' | 'rename';
|
|
159
|
-
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
160
|
-
export type ProcessingStatus = 'pending' | 'processing' | 'completed' | 'failed';
|
|
161
|
-
|
|
162
|
-
export interface ProcessingJob {
|
|
163
|
-
id: string;
|
|
164
|
-
filePath: string;
|
|
165
|
-
status: ProcessingStatus;
|
|
166
|
-
startTime: number;
|
|
167
|
-
endTime?: number;
|
|
168
|
-
error?: Error;
|
|
169
|
-
}
|
package/src/utils/native.ts
DELETED
|
@@ -1,352 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import { promisify } from 'util';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Native Node.js utilities to replace external dependencies
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Convert string to PascalCase (replaces change-case package)
|
|
10
|
-
*/
|
|
11
|
-
export function toPascalCase(str: string): string {
|
|
12
|
-
return str
|
|
13
|
-
.replace(/[^a-zA-Z0-9]/g, ' ')
|
|
14
|
-
.split(' ')
|
|
15
|
-
.filter(Boolean)
|
|
16
|
-
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
17
|
-
.join('');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Native file system utilities (replaces fs-extra package)
|
|
22
|
-
*/
|
|
23
|
-
export class FileSystem {
|
|
24
|
-
private static _readFile = promisify(fs.readFile);
|
|
25
|
-
private static _writeFile = promisify(fs.writeFile);
|
|
26
|
-
private static _readdir = promisify(fs.readdir);
|
|
27
|
-
private static _stat = promisify(fs.stat);
|
|
28
|
-
private static _mkdir = promisify(fs.mkdir);
|
|
29
|
-
private static _rmdir = promisify(fs.rmdir);
|
|
30
|
-
private static _unlink = promisify(fs.unlink);
|
|
31
|
-
|
|
32
|
-
static async exists(path: string): Promise<boolean> {
|
|
33
|
-
try {
|
|
34
|
-
await this._stat(path);
|
|
35
|
-
return true;
|
|
36
|
-
} catch {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
static async readFile(path: string, encoding: BufferEncoding = 'utf8'): Promise<string> {
|
|
42
|
-
return this._readFile(path, encoding);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
static async writeFile(path: string, content: string, encoding: BufferEncoding = 'utf8'): Promise<void> {
|
|
46
|
-
return this._writeFile(path, content, encoding);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
static async readDir(path: string): Promise<string[]> {
|
|
50
|
-
return this._readdir(path);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
static async ensureDir(dirPath: string): Promise<void> {
|
|
54
|
-
try {
|
|
55
|
-
await this._mkdir(dirPath, { recursive: true });
|
|
56
|
-
} catch (error: any) {
|
|
57
|
-
if (error.code !== 'EEXIST') {
|
|
58
|
-
throw error;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
static async removeDir(dirPath: string): Promise<void> {
|
|
64
|
-
try {
|
|
65
|
-
const files = await this._readdir(dirPath);
|
|
66
|
-
|
|
67
|
-
for (const file of files) {
|
|
68
|
-
const filePath = `${dirPath}/${file}`;
|
|
69
|
-
const stats = await this._stat(filePath);
|
|
70
|
-
|
|
71
|
-
if (stats.isDirectory()) {
|
|
72
|
-
await this.removeDir(filePath);
|
|
73
|
-
} else {
|
|
74
|
-
await this._unlink(filePath);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
await this._rmdir(dirPath);
|
|
79
|
-
} catch (error: any) {
|
|
80
|
-
if (error.code !== 'ENOENT') {
|
|
81
|
-
throw error;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
static async emptyDir(dirPath: string): Promise<void> {
|
|
87
|
-
if (!(await this.exists(dirPath))) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const files = await this._readdir(dirPath);
|
|
92
|
-
|
|
93
|
-
for (const file of files) {
|
|
94
|
-
const filePath = `${dirPath}/${file}`;
|
|
95
|
-
const stats = await this._stat(filePath);
|
|
96
|
-
|
|
97
|
-
if (stats.isDirectory()) {
|
|
98
|
-
await this.removeDir(filePath);
|
|
99
|
-
} else {
|
|
100
|
-
await this._unlink(filePath);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
static async unlink(filePath: string): Promise<void> {
|
|
106
|
-
return this._unlink(filePath);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
static readJSONSync(path: string): any {
|
|
110
|
-
try {
|
|
111
|
-
const content = fs.readFileSync(path, 'utf8');
|
|
112
|
-
return JSON.parse(content);
|
|
113
|
-
} catch {
|
|
114
|
-
return {};
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
static writeJSONSync(path: string, data: any, options?: { spaces?: number }): void {
|
|
119
|
-
const content = JSON.stringify(data, null, options?.spaces || 0);
|
|
120
|
-
fs.writeFileSync(path, content, 'utf8');
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
static existsSync(path: string): boolean {
|
|
124
|
-
try {
|
|
125
|
-
fs.statSync(path);
|
|
126
|
-
return true;
|
|
127
|
-
} catch {
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
static ensureDirSync(dirPath: string): void {
|
|
133
|
-
try {
|
|
134
|
-
fs.mkdirSync(dirPath, { recursive: true });
|
|
135
|
-
} catch (error: any) {
|
|
136
|
-
if (error.code !== 'EEXIST') {
|
|
137
|
-
throw error;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Simple CLI argument parser (replaces commander package)
|
|
145
|
-
*/
|
|
146
|
-
export class CLI {
|
|
147
|
-
private commands: Map<string, {
|
|
148
|
-
description: string;
|
|
149
|
-
action: (args: string[], options: Record<string, any>) => void | Promise<void>;
|
|
150
|
-
options: Map<string, { description: string; hasValue: boolean }>;
|
|
151
|
-
}> = new Map();
|
|
152
|
-
|
|
153
|
-
private programName = '';
|
|
154
|
-
private programDescription = '';
|
|
155
|
-
private programVersion = '';
|
|
156
|
-
|
|
157
|
-
name(name: string): this {
|
|
158
|
-
this.programName = name;
|
|
159
|
-
return this;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
description(desc: string): this {
|
|
163
|
-
this.programDescription = desc;
|
|
164
|
-
return this;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
version(version: string): this {
|
|
168
|
-
this.programVersion = version;
|
|
169
|
-
return this;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
command(signature: string): CommandBuilder {
|
|
173
|
-
return new CommandBuilder(signature, this);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
addCommand(signature: string, description: string, action: Function, options: Map<string, any>): void {
|
|
177
|
-
const [command] = signature.split(' ');
|
|
178
|
-
this.commands.set(command, {
|
|
179
|
-
description,
|
|
180
|
-
action: action as any,
|
|
181
|
-
options
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
async parse(): Promise<void> {
|
|
186
|
-
const args = process.argv.slice(2);
|
|
187
|
-
|
|
188
|
-
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
189
|
-
this.showHelp();
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (args[0] === '--version' || args[0] === '-v') {
|
|
194
|
-
console.log(this.programVersion);
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const [commandName, ...remainingArgs] = args;
|
|
199
|
-
const command = this.commands.get(commandName);
|
|
200
|
-
|
|
201
|
-
if (!command) {
|
|
202
|
-
console.error(`Unknown command: ${commandName}`);
|
|
203
|
-
this.showHelp();
|
|
204
|
-
process.exit(1);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const { parsedArgs, options } = this.parseArgs(remainingArgs, command.options);
|
|
208
|
-
|
|
209
|
-
try {
|
|
210
|
-
await command.action(parsedArgs, options);
|
|
211
|
-
} catch (error) {
|
|
212
|
-
console.error('Command failed:', error);
|
|
213
|
-
process.exit(1);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
private parseArgs(args: string[], commandOptions: Map<string, any>): {
|
|
218
|
-
parsedArgs: string[];
|
|
219
|
-
options: Record<string, any>;
|
|
220
|
-
} {
|
|
221
|
-
const parsedArgs: string[] = [];
|
|
222
|
-
const options: Record<string, any> = {};
|
|
223
|
-
|
|
224
|
-
let i = 0;
|
|
225
|
-
while (i < args.length) {
|
|
226
|
-
const arg = args[i];
|
|
227
|
-
|
|
228
|
-
if (arg.startsWith('--')) {
|
|
229
|
-
const optionName = arg.slice(2);
|
|
230
|
-
const optionConfig = commandOptions.get(optionName);
|
|
231
|
-
|
|
232
|
-
if (optionConfig) {
|
|
233
|
-
if (optionConfig.hasValue) {
|
|
234
|
-
options[optionName] = args[i + 1];
|
|
235
|
-
i += 2;
|
|
236
|
-
} else {
|
|
237
|
-
options[optionName] = true;
|
|
238
|
-
i++;
|
|
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
|
-
} else {
|
|
247
|
-
i++;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
} else {
|
|
251
|
-
parsedArgs.push(arg);
|
|
252
|
-
i++;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
return { parsedArgs, options };
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
private showHelp(): void {
|
|
260
|
-
console.log(`${this.programName} - ${this.programDescription}`);
|
|
261
|
-
console.log(`Version: ${this.programVersion}\n`);
|
|
262
|
-
console.log('Commands:');
|
|
263
|
-
|
|
264
|
-
for (const [name, cmd] of this.commands) {
|
|
265
|
-
console.log(` ${name.padEnd(15)} ${cmd.description}`);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
console.log('\nOptions:');
|
|
269
|
-
console.log(' --help, -h Show help');
|
|
270
|
-
console.log(' --version, -v Show version');
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
class CommandBuilder {
|
|
275
|
-
private signature: string;
|
|
276
|
-
private desc = '';
|
|
277
|
-
private cli: CLI;
|
|
278
|
-
private options: Map<string, { description: string; hasValue: boolean }> = new Map();
|
|
279
|
-
|
|
280
|
-
constructor(signature: string, cli: CLI) {
|
|
281
|
-
this.signature = signature;
|
|
282
|
-
this.cli = cli;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
description(desc: string): this {
|
|
286
|
-
this.desc = desc;
|
|
287
|
-
return this;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
option(flag: string, description: string): this {
|
|
291
|
-
const hasValue = flag.includes('<') || flag.includes('[');
|
|
292
|
-
const optionName = flag.split(' ')[0].replace(/^--/, '');
|
|
293
|
-
this.options.set(optionName, { description, hasValue });
|
|
294
|
-
return this;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
action(fn: Function): void {
|
|
298
|
-
this.cli.addCommand(this.signature, this.desc, fn, this.options);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* File watcher using native fs.watch (replaces chokidar)
|
|
304
|
-
*/
|
|
305
|
-
export class FileWatcher {
|
|
306
|
-
private watchers: fs.FSWatcher[] = [];
|
|
307
|
-
private callbacks: Map<string, Function[]> = new Map();
|
|
308
|
-
|
|
309
|
-
watch(path: string, options?: { recursive?: boolean }): this {
|
|
310
|
-
try {
|
|
311
|
-
const watcher = fs.watch(path, {
|
|
312
|
-
recursive: options?.recursive || false,
|
|
313
|
-
persistent: true
|
|
314
|
-
}, (eventType, filename) => {
|
|
315
|
-
if (filename) {
|
|
316
|
-
this.emit(eventType, `${path}/${filename}`);
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
this.watchers.push(watcher);
|
|
321
|
-
} catch (error) {
|
|
322
|
-
console.error(`Failed to watch ${path}:`, error);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
return this;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
on(event: string, callback: Function): this {
|
|
329
|
-
if (!this.callbacks.has(event)) {
|
|
330
|
-
this.callbacks.set(event, []);
|
|
331
|
-
}
|
|
332
|
-
this.callbacks.get(event)!.push(callback);
|
|
333
|
-
return this;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
private emit(event: string, ...args: any[]): void {
|
|
337
|
-
const callbacks = this.callbacks.get(event) || [];
|
|
338
|
-
callbacks.forEach(callback => {
|
|
339
|
-
try {
|
|
340
|
-
callback(...args);
|
|
341
|
-
} catch (error) {
|
|
342
|
-
console.error('Watcher callback error:', error);
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
close(): void {
|
|
348
|
-
this.watchers.forEach(watcher => watcher.close());
|
|
349
|
-
this.watchers = [];
|
|
350
|
-
this.callbacks.clear();
|
|
351
|
-
}
|
|
352
|
-
}
|
package/src/watch.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { generateSVG } from "./builder.js";
|
|
4
|
-
import { isLocked } from "./lock.js";
|
|
5
|
-
import { readConfig } from "./config.js";
|
|
6
|
-
import { FileSystem, FileWatcher } from "./utils/native.js";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Watches a source folder for changes to SVG files and automatically
|
|
10
|
-
* rebuilds React components when SVGs are added, modified, or deleted.
|
|
11
|
-
*
|
|
12
|
-
* @param {Object} config - Watch configuration object.
|
|
13
|
-
* @param {string} config.src - Source folder containing SVG files to watch.
|
|
14
|
-
* @param {string} config.out - Output folder where React components are generated.
|
|
15
|
-
* @returns {import("chokidar").FSWatcher} A chokidar file watcher instance.
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* watchSVGs({ src: "./src/assets/svg", out: "./src/components/icons" });
|
|
19
|
-
*
|
|
20
|
-
* // Watches the SVG folder and:
|
|
21
|
-
* // - Generates new components when files are added.
|
|
22
|
-
* // - Updates components when files change.
|
|
23
|
-
* // - Removes components when SVGs are deleted.
|
|
24
|
-
*/
|
|
25
|
-
export async function watchSVGs(config: { src: string; out: string }) {
|
|
26
|
-
const srcDir = path.resolve(config.src);
|
|
27
|
-
const outDir = path.resolve(config.out);
|
|
28
|
-
const svgConfig = readConfig();
|
|
29
|
-
|
|
30
|
-
if (!(await FileSystem.exists(srcDir))) {
|
|
31
|
-
console.error("❌ Source folder not found:", srcDir);
|
|
32
|
-
process.exit(1);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
console.log(`👀 Watching for SVG changes in: ${srcDir}`);
|
|
36
|
-
console.log("🚀 Watch mode active — waiting for file changes...");
|
|
37
|
-
|
|
38
|
-
const watcher = new FileWatcher();
|
|
39
|
-
|
|
40
|
-
// Watch the directory
|
|
41
|
-
watcher.watch(srcDir, { recursive: false });
|
|
42
|
-
|
|
43
|
-
// Handle file changes
|
|
44
|
-
watcher.on("change", async (filePath: string) => {
|
|
45
|
-
if (!filePath.endsWith(".svg")) return;
|
|
46
|
-
|
|
47
|
-
console.log("Detected change in file:", filePath);
|
|
48
|
-
|
|
49
|
-
if (isLocked(filePath)) {
|
|
50
|
-
console.log(`⚠️ Skipped locked file: ${path.basename(filePath)}`);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
console.log(`✏️ SVG updated: ${path.basename(filePath)}`);
|
|
55
|
-
await generateSVG({ svgFile: filePath, outDir });
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
// Handle new files (rename event in fs.watch can indicate new files)
|
|
59
|
-
watcher.on("rename", async (filePath: string) => {
|
|
60
|
-
if (!filePath.endsWith(".svg")) return;
|
|
61
|
-
|
|
62
|
-
// Check if file exists (new file) or doesn't exist (deleted file)
|
|
63
|
-
const exists = await FileSystem.exists(filePath);
|
|
64
|
-
|
|
65
|
-
if (exists) {
|
|
66
|
-
console.log("Detected new file:", filePath);
|
|
67
|
-
|
|
68
|
-
if (isLocked(filePath)) {
|
|
69
|
-
console.log(`⚠️ Skipped locked file: ${path.basename(filePath)}`);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
console.log(`➕ New SVG detected: ${path.basename(filePath)}`);
|
|
74
|
-
await generateSVG({ svgFile: filePath, outDir });
|
|
75
|
-
} else {
|
|
76
|
-
// File was deleted
|
|
77
|
-
const componentName = path.basename(filePath, ".svg");
|
|
78
|
-
const outFile = path.join(outDir, `${componentName}.tsx`);
|
|
79
|
-
|
|
80
|
-
if (await FileSystem.exists(outFile)) {
|
|
81
|
-
await FileSystem.unlink(outFile);
|
|
82
|
-
console.log(`🗑️ Removed component: ${componentName}.tsx`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
return watcher;
|
|
88
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Component, Input } from '@angular/core';
|
|
2
|
-
|
|
3
|
-
@Component({
|
|
4
|
-
selector: 'test-icon',
|
|
5
|
-
|
|
6
|
-
template: `
|
|
7
|
-
<svg
|
|
8
|
-
[attr.class]="className"
|
|
9
|
-
[attr.width]="width"
|
|
10
|
-
[attr.height]="height"
|
|
11
|
-
[attr.fill]="fill"
|
|
12
|
-
[attr.stroke]="stroke"
|
|
13
|
-
viewBox="0 0 24 24"
|
|
14
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
15
|
-
>
|
|
16
|
-
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
|
|
17
|
-
</svg>
|
|
18
|
-
`,
|
|
19
|
-
})
|
|
20
|
-
export class TestIconComponent {
|
|
21
|
-
@Input() className: string = '';
|
|
22
|
-
@Input() width: string | number = 24;
|
|
23
|
-
@Input() height: string | number = 24;
|
|
24
|
-
@Input() fill: string = 'currentColor';
|
|
25
|
-
@Input() stroke: string = '';
|
|
26
|
-
}
|