@wkovacs64/add-icon 0.1.0-dev.3b555c68
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/LICENSE.txt +21 -0
- package/README.md +191 -0
- package/bin/add-icon.js +4 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +55 -0
- package/dist/iconify.d.ts +17 -0
- package/dist/iconify.js +78 -0
- package/dist/import-module.d.ts +6 -0
- package/dist/import-module.js +35 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +83 -0
- package/dist/package-info.d.ts +9 -0
- package/dist/package-info.js +34 -0
- package/dist/types.d.ts +24 -0
- package/dist/types.js +1 -0
- package/package.json +64 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Justin R. Hall
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# @wkovacs64/add-icon
|
|
2
|
+
|
|
3
|
+
A command-line tool to download icons from the [Iconify Framework](https://iconify.design/) and
|
|
4
|
+
apply custom transformations.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
Add it to your project:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @wkovacs64/add-icon
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Or use it directly with npx without installing:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx @wkovacs64/add-icon heroicons:arrow-up-circle
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
### Basic Usage
|
|
23
|
+
|
|
24
|
+
Download an icon:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx @wkovacs64/add-icon heroicons:arrow-up-circle
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Specify an output directory:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx @wkovacs64/add-icon heroicons:arrow-up-circle --output-dir ./my-icons
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Transformations
|
|
37
|
+
|
|
38
|
+
The tool fetches SVG icons directly from the Iconify API with width and height attributes removed automatically. You can add custom transformations for more advanced modifications.
|
|
39
|
+
|
|
40
|
+
### Custom Transformations
|
|
41
|
+
|
|
42
|
+
You can write custom transforms in either JavaScript or TypeScript!
|
|
43
|
+
|
|
44
|
+
#### JavaScript Transform
|
|
45
|
+
|
|
46
|
+
Create a custom transform file (e.g., `my-transform.js`):
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
/**
|
|
50
|
+
* Custom transform to add a title element to SVG
|
|
51
|
+
* @param {Object} args - Transform arguments
|
|
52
|
+
* @param {string} args.svg - SVG content
|
|
53
|
+
* @param {string} args.iconName - Icon name (e.g., 'heroicons:arrow-up-circle')
|
|
54
|
+
* @param {string} args.prefix - Icon set prefix (e.g., 'heroicons')
|
|
55
|
+
* @param {string} args.name - Icon name without prefix (e.g., 'arrow-up-circle')
|
|
56
|
+
* @returns {string} - Transformed SVG
|
|
57
|
+
*/
|
|
58
|
+
export default function addTitle(args) {
|
|
59
|
+
const titleElement = `<title>${args.iconName}</title>`;
|
|
60
|
+
return args.svg.replace(/<svg([^>]*)>/, `<svg$1>${titleElement}`);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### TypeScript Transform
|
|
65
|
+
|
|
66
|
+
Create a custom transform file (e.g., `my-transform.ts`) that will be imported directly:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import type { TransformArgs } from '@wkovacs64/add-icon';
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Custom transform to add a title element to SVG
|
|
73
|
+
* @param args - Transform arguments containing SVG content and icon information
|
|
74
|
+
* @returns The transformed SVG
|
|
75
|
+
*/
|
|
76
|
+
export default function addTitle(args: TransformArgs): string {
|
|
77
|
+
const titleElement = `<title>${args.iconSet}:${args.iconName}</title>`;
|
|
78
|
+
return args.svg.replace(/<svg([^>]*)>/, `<svg$1>${titleElement}`);
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Then use it with the CLI:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# JavaScript transform
|
|
86
|
+
npx @wkovacs64/add-icon heroicons:arrow-up-circle --transform ./my-transform.js
|
|
87
|
+
|
|
88
|
+
# TypeScript transform
|
|
89
|
+
npx @wkovacs64/add-icon heroicons:arrow-up-circle --transform ./my-transform.ts
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Configuration File
|
|
93
|
+
|
|
94
|
+
You can create a configuration file in your project root, using either JavaScript (`add-icon.config.js`) or TypeScript (`add-icon.config.ts`).
|
|
95
|
+
|
|
96
|
+
### JavaScript Configuration
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
// Define custom transform
|
|
100
|
+
function addCustomAttribute(args) {
|
|
101
|
+
return args.svg.replace(/<svg/, `<svg data-icon="${args.iconName}"`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export default {
|
|
105
|
+
outputDir: './assets/icons',
|
|
106
|
+
transforms: [addCustomAttribute],
|
|
107
|
+
};
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### TypeScript Configuration
|
|
111
|
+
|
|
112
|
+
You can create a TypeScript configuration file and the tool will import it directly:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import type { IconifyConfig, TransformArgs } from '@wkovacs64/add-icon';
|
|
116
|
+
|
|
117
|
+
// Define custom transform
|
|
118
|
+
function addCustomAttribute(args: TransformArgs): string {
|
|
119
|
+
return args.svg.replace(/<svg/, `<svg data-icon="${args.iconName}"`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const config = {
|
|
123
|
+
outputDir: './assets/icons',
|
|
124
|
+
transforms: [addCustomAttribute],
|
|
125
|
+
} satisfies IconifyConfig;
|
|
126
|
+
|
|
127
|
+
export default config;
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
> **Note:** TypeScript configuration and transform files are compiled in-memory using esbuild, without creating temporary JavaScript files. This provides a seamless TypeScript experience with no extra build steps.
|
|
131
|
+
|
|
132
|
+
## Using as a Library
|
|
133
|
+
|
|
134
|
+
You can also use iconify-cli as a library in your own projects:
|
|
135
|
+
|
|
136
|
+
### JavaScript
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
import { downloadIcon } from '@wkovacs64/add-icon';
|
|
140
|
+
|
|
141
|
+
// Create custom transform
|
|
142
|
+
function addCustomAttribute(args) {
|
|
143
|
+
return args.svg.replace(/<svg/, `<svg data-custom="${args.iconSet}"`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Download an icon with transforms
|
|
147
|
+
async function downloadCustomIcon() {
|
|
148
|
+
const iconPath = await downloadIcon('heroicons:heart', {
|
|
149
|
+
outputDir: './icons',
|
|
150
|
+
transforms: [addCustomAttribute],
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
console.log(`Icon saved to: ${iconPath}`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
downloadCustomIcon();
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### TypeScript
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
import { downloadIcon, type TransformArgs } from '@wkovacs64/add-icon';
|
|
163
|
+
|
|
164
|
+
// Create custom transform
|
|
165
|
+
const addCustomAttribute = (args: TransformArgs): string => {
|
|
166
|
+
return args.svg.replace(/<svg/, `<svg data-custom="${args.iconSet}"`);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// Download an icon with transforms
|
|
170
|
+
async function downloadCustomIcon(): Promise<void> {
|
|
171
|
+
try {
|
|
172
|
+
const iconPath = await downloadIcon('heroicons:heart', {
|
|
173
|
+
outputDir: './icons',
|
|
174
|
+
transforms: [addCustomAttribute],
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
console.log(`Icon saved to: ${iconPath}`);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.error(
|
|
180
|
+
'Error downloading icon:',
|
|
181
|
+
error instanceof Error ? error.message : String(error),
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
downloadCustomIcon();
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
MIT
|
package/bin/add-icon.js
ADDED
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { IconifyConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Default configuration
|
|
4
|
+
*/
|
|
5
|
+
export declare const defaultConfig: IconifyConfig;
|
|
6
|
+
/**
|
|
7
|
+
* Loads configuration from file if it exists
|
|
8
|
+
* @param configPath - Path to config file
|
|
9
|
+
* @returns Configuration object
|
|
10
|
+
*/
|
|
11
|
+
export declare function loadConfig(configPath?: string): Promise<IconifyConfig>;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { importModule } from './import-module.js';
|
|
4
|
+
/**
|
|
5
|
+
* Default configuration
|
|
6
|
+
*/
|
|
7
|
+
export const defaultConfig = {
|
|
8
|
+
outputDir: '.', // Current directory
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Loads configuration from file if it exists
|
|
12
|
+
* @param configPath - Path to config file
|
|
13
|
+
* @returns Configuration object
|
|
14
|
+
*/
|
|
15
|
+
export async function loadConfig(configPath) {
|
|
16
|
+
try {
|
|
17
|
+
// If a specific config path is provided, use it
|
|
18
|
+
if (configPath) {
|
|
19
|
+
// Use the unified import method for both JS and TS files
|
|
20
|
+
const config = await importModule(configPath);
|
|
21
|
+
return { ...defaultConfig, ...config.default };
|
|
22
|
+
}
|
|
23
|
+
// Try to find a config file in the current directory, checking both JS and TS
|
|
24
|
+
const jsConfigPath = path.resolve(process.cwd(), 'add-icon.config.js');
|
|
25
|
+
const tsConfigPath = path.resolve(process.cwd(), 'add-icon.config.ts');
|
|
26
|
+
// Check for TypeScript config first
|
|
27
|
+
if (existsSync(tsConfigPath)) {
|
|
28
|
+
try {
|
|
29
|
+
const config = await importModule(tsConfigPath);
|
|
30
|
+
return { ...defaultConfig, ...config.default };
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
console.error('Error loading TypeScript config, falling back to default config:', err);
|
|
34
|
+
return defaultConfig;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Then check for JavaScript config
|
|
38
|
+
if (existsSync(jsConfigPath)) {
|
|
39
|
+
try {
|
|
40
|
+
const config = await importModule(jsConfigPath);
|
|
41
|
+
return { ...defaultConfig, ...config.default };
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
console.error('Error loading JavaScript config, falling back to default config:', err);
|
|
45
|
+
return defaultConfig;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Fall back to default config
|
|
49
|
+
return defaultConfig;
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
console.error('Error loading config, using default config:', error);
|
|
53
|
+
return defaultConfig;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { IconifyConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parses an icon reference into iconSet and iconName
|
|
4
|
+
* @param iconReference - Reference in format 'iconSet:iconName'
|
|
5
|
+
* @returns Object with iconSet and iconName
|
|
6
|
+
*/
|
|
7
|
+
export declare function parseIconReference(iconReference: string): {
|
|
8
|
+
iconSet: string;
|
|
9
|
+
iconName: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Downloads an icon and applies transforms
|
|
13
|
+
* @param iconReference - Icon reference (e.g., 'heroicons:arrow-up-circle')
|
|
14
|
+
* @param config - Configuration options
|
|
15
|
+
* @returns Path to saved icon file
|
|
16
|
+
*/
|
|
17
|
+
export declare function downloadIcon(iconReference: string, config: IconifyConfig): Promise<string>;
|
package/dist/iconify.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Parses an icon reference into iconSet and iconName
|
|
5
|
+
* @param iconReference - Reference in format 'iconSet:iconName'
|
|
6
|
+
* @returns Object with iconSet and iconName
|
|
7
|
+
*/
|
|
8
|
+
export function parseIconReference(iconReference) {
|
|
9
|
+
const parts = iconReference.split(':');
|
|
10
|
+
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
11
|
+
throw new Error(`Invalid icon reference: ${iconReference}. Expected format: iconSet:iconName`);
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
iconSet: parts[0],
|
|
15
|
+
iconName: parts[1],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Fetches icon SVG directly from Iconify API
|
|
20
|
+
* @param iconSet - Icon set name
|
|
21
|
+
* @param iconName - Icon name
|
|
22
|
+
* @returns Promise with SVG string
|
|
23
|
+
*/
|
|
24
|
+
async function fetchIconSvg(iconSet, iconName) {
|
|
25
|
+
// Using width=unset parameter to remove width/height attributes automatically
|
|
26
|
+
const apiUrl = `https://api.iconify.design/${iconSet}/${iconName}.svg?width=unset`;
|
|
27
|
+
try {
|
|
28
|
+
const response = await fetch(apiUrl);
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
31
|
+
}
|
|
32
|
+
return await response.text();
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
36
|
+
throw new Error(`Failed to fetch icon SVG: ${errorMessage}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Downloads an icon and applies transforms
|
|
41
|
+
* @param iconReference - Icon reference (e.g., 'heroicons:arrow-up-circle')
|
|
42
|
+
* @param config - Configuration options
|
|
43
|
+
* @returns Path to saved icon file
|
|
44
|
+
*/
|
|
45
|
+
export async function downloadIcon(iconReference, config) {
|
|
46
|
+
try {
|
|
47
|
+
const { iconSet, iconName } = parseIconReference(iconReference);
|
|
48
|
+
// Ensure the output directory exists
|
|
49
|
+
if (!fs.existsSync(config.outputDir)) {
|
|
50
|
+
fs.mkdirSync(config.outputDir, { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
// Fetch SVG directly with width=unset parameter to remove width/height attributes
|
|
53
|
+
let svg = await fetchIconSvg(iconSet, iconName);
|
|
54
|
+
// Apply custom transforms if specified
|
|
55
|
+
if (config.transforms && config.transforms.length > 0) {
|
|
56
|
+
for (const transform of config.transforms) {
|
|
57
|
+
// Create transform arguments object
|
|
58
|
+
const transformArgs = {
|
|
59
|
+
svg,
|
|
60
|
+
iconSet,
|
|
61
|
+
iconName,
|
|
62
|
+
};
|
|
63
|
+
// Apply transform
|
|
64
|
+
svg = await Promise.resolve(transform(transformArgs));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Create file name
|
|
68
|
+
const fileName = `${iconSet}-${iconName}.svg`;
|
|
69
|
+
const filePath = path.join(config.outputDir, fileName);
|
|
70
|
+
// Write the SVG file
|
|
71
|
+
fs.writeFileSync(filePath, svg, 'utf8');
|
|
72
|
+
return filePath;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
76
|
+
throw new Error(`Failed to download icon: ${errorMessage}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import * as esbuild from 'esbuild';
|
|
4
|
+
/**
|
|
5
|
+
* Imports a module file (JavaScript or TypeScript) by processing it with esbuild
|
|
6
|
+
* @param filePath - Path to module file (JS or TS)
|
|
7
|
+
* @returns Module exports
|
|
8
|
+
*/
|
|
9
|
+
export async function importModule(filePath) {
|
|
10
|
+
const absolutePath = path.resolve(filePath);
|
|
11
|
+
try {
|
|
12
|
+
// Read the module file content
|
|
13
|
+
const code = await fs.readFile(absolutePath, "utf-8");
|
|
14
|
+
// Determine the appropriate loader based on file extension
|
|
15
|
+
const loader = absolutePath.endsWith('.ts') ? 'ts' : 'js';
|
|
16
|
+
// Use esbuild to transform the code to ESM JS
|
|
17
|
+
const result = await esbuild.transform(code, {
|
|
18
|
+
loader, // Automatically use the appropriate loader
|
|
19
|
+
format: "esm", // Output format
|
|
20
|
+
sourcemap: false, // Disable source maps for data URI
|
|
21
|
+
sourcefile: absolutePath, // Helps with error messages
|
|
22
|
+
target: 'esnext',
|
|
23
|
+
});
|
|
24
|
+
const jsCode = result.code;
|
|
25
|
+
// Create data URI and import
|
|
26
|
+
const base64Code = Buffer.from(jsCode).toString("base64");
|
|
27
|
+
const dataUri = `data:text/javascript;base64,${base64Code}`;
|
|
28
|
+
// Import the transformed code as a module
|
|
29
|
+
return await import(dataUri);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error(`Error importing module ${filePath} with esbuild:`, error);
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import url from 'node:url';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import { downloadIcon } from './iconify.js';
|
|
7
|
+
import { loadConfig } from './config.js';
|
|
8
|
+
import { importModule } from './import-module.js';
|
|
9
|
+
import { getPackageInfo } from './package-info.js';
|
|
10
|
+
// Re-export other useful functions
|
|
11
|
+
export { downloadIcon, parseIconReference } from './iconify.js';
|
|
12
|
+
// Create CLI program
|
|
13
|
+
const program = new Command();
|
|
14
|
+
// Set up the program with package info
|
|
15
|
+
const setupProgram = async () => {
|
|
16
|
+
const { name, version, description } = await getPackageInfo();
|
|
17
|
+
return program
|
|
18
|
+
.name(name.split('/').pop() || name)
|
|
19
|
+
.description(description)
|
|
20
|
+
.version(version, '-v, --version', 'Output the current version')
|
|
21
|
+
.argument('<icon>', 'Icon reference (e.g., heroicons:arrow-up-circle)')
|
|
22
|
+
.option('-o, --output-dir <dir>', 'Directory to save icon')
|
|
23
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
24
|
+
.option('-t, --transform <path>', 'Path to custom transform module (.js or .ts)');
|
|
25
|
+
};
|
|
26
|
+
// Initialize the program
|
|
27
|
+
const initializedProgram = await setupProgram();
|
|
28
|
+
initializedProgram.action(async (icon, options) => {
|
|
29
|
+
try {
|
|
30
|
+
// Load config (first from config file, then override with CLI options)
|
|
31
|
+
const config = await loadConfig(options.config);
|
|
32
|
+
// Override output directory if specified in CLI
|
|
33
|
+
if (options.outputDir) {
|
|
34
|
+
config.outputDir = options.outputDir;
|
|
35
|
+
}
|
|
36
|
+
// Load custom transform if specified
|
|
37
|
+
if (options.transform) {
|
|
38
|
+
try {
|
|
39
|
+
const transformPath = path.resolve(process.cwd(), options.transform);
|
|
40
|
+
let customTransform;
|
|
41
|
+
try {
|
|
42
|
+
// Use unified import method for both JS and TS files
|
|
43
|
+
customTransform = await importModule(transformPath);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
47
|
+
console.error(`Error loading transform: ${errorMessage}`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
if (customTransform && typeof customTransform.default === 'function') {
|
|
51
|
+
config.transforms = [customTransform.default];
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
console.error('Custom transform must export a default function');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
60
|
+
console.error(`Failed to load custom transform: ${errorMessage}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Download the icon
|
|
65
|
+
console.log(`Downloading icon: ${icon}...`);
|
|
66
|
+
const savedPath = await downloadIcon(icon, config);
|
|
67
|
+
console.log(`✓ Icon saved to: ${savedPath}`);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
71
|
+
console.error(`Error: ${errorMessage}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
const __filename = url.fileURLToPath(import.meta.url);
|
|
76
|
+
const __dirname = path.dirname(__filename);
|
|
77
|
+
// This logic only runs when executed directly as CLI, not when imported as a library
|
|
78
|
+
if (os.platform() === 'win32'
|
|
79
|
+
? process.argv[1] === __filename
|
|
80
|
+
: process.argv[1] === __filename || process.argv[1] === __dirname) {
|
|
81
|
+
// Parse command line arguments
|
|
82
|
+
program.parse(process.argv);
|
|
83
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
/**
|
|
5
|
+
* Loads package info from package.json
|
|
6
|
+
* @returns Package info with name, version, and description
|
|
7
|
+
*/
|
|
8
|
+
export async function getPackageInfo() {
|
|
9
|
+
// Get the directory of the current module
|
|
10
|
+
const currentFileUrl = import.meta.url;
|
|
11
|
+
const currentFilePath = fileURLToPath(currentFileUrl);
|
|
12
|
+
const currentDir = path.dirname(currentFilePath);
|
|
13
|
+
// Go up one level from src/ to the package root
|
|
14
|
+
const packageJsonPath = path.resolve(currentDir, '..', 'package.json');
|
|
15
|
+
try {
|
|
16
|
+
// Read and parse package.json
|
|
17
|
+
const packageJsonContent = await fs.readFile(packageJsonPath, 'utf-8');
|
|
18
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
19
|
+
return {
|
|
20
|
+
name: packageJson.name || 'unknown',
|
|
21
|
+
version: packageJson.version || '0.0.0',
|
|
22
|
+
description: packageJson.description || 'unknown',
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
console.warn('Failed to read package.json:', error);
|
|
27
|
+
// Fallback values
|
|
28
|
+
return {
|
|
29
|
+
name: 'unknown',
|
|
30
|
+
version: '0.0.0',
|
|
31
|
+
description: 'unknown',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arguments passed to transform functions
|
|
3
|
+
*/
|
|
4
|
+
export type TransformArgs = {
|
|
5
|
+
/** The SVG content as a string */
|
|
6
|
+
svg: string;
|
|
7
|
+
/** The icon set (e.g., 'heroicons') */
|
|
8
|
+
iconSet: string;
|
|
9
|
+
/** The icon name (e.g., 'arrow-up-circle') */
|
|
10
|
+
iconName: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* SVG transformation function type
|
|
14
|
+
*/
|
|
15
|
+
export type IconTransform = (args: TransformArgs) => Promise<string> | string;
|
|
16
|
+
/**
|
|
17
|
+
* Configuration options for the Iconify CLI
|
|
18
|
+
*/
|
|
19
|
+
export type IconifyConfig = {
|
|
20
|
+
/** Directory to output icons */
|
|
21
|
+
outputDir: string;
|
|
22
|
+
/** Array of transform functions to apply to icons */
|
|
23
|
+
transforms?: IconTransform[];
|
|
24
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wkovacs64/add-icon",
|
|
3
|
+
"version": "0.1.0-dev.3b555c68",
|
|
4
|
+
"description": "CLI tool to download and transform icons from Iconify",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"iconify",
|
|
7
|
+
"icons",
|
|
8
|
+
"svg",
|
|
9
|
+
"cli",
|
|
10
|
+
"download"
|
|
11
|
+
],
|
|
12
|
+
"author": "Justin R. Hall <justin.r.hall@gmail.com>",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "dist/index.js",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": "./dist/index.js",
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
20
|
+
"bin": {
|
|
21
|
+
"add-icon": "bin/add-icon.js"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"bin"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc",
|
|
29
|
+
"clean": "del-cli dist",
|
|
30
|
+
"prebuild": "npm run --silent clean",
|
|
31
|
+
"prepublishOnly": "run-p --silent lint typecheck build",
|
|
32
|
+
"start": "node dist/index.js",
|
|
33
|
+
"dev": "tsx src/index.ts",
|
|
34
|
+
"typecheck": "attw --pack --profile esm-only",
|
|
35
|
+
"lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .",
|
|
36
|
+
"format": "prettier --cache --write .",
|
|
37
|
+
"format:check": "prettier --cache --check .",
|
|
38
|
+
"changeset": "changeset",
|
|
39
|
+
"changeset:version": "changeset version && npm install --package-lock-only",
|
|
40
|
+
"changeset:publish": "changeset publish"
|
|
41
|
+
},
|
|
42
|
+
"prettier": "@wkovacs64/prettier-config",
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=20.19.0"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"commander": "^13.1.0",
|
|
48
|
+
"esbuild": "~0.25.2",
|
|
49
|
+
"typescript": "^5.8.3"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@arethetypeswrong/cli": "0.17.4",
|
|
53
|
+
"@changesets/changelog-github": "0.5.1",
|
|
54
|
+
"@changesets/cli": "2.29.0",
|
|
55
|
+
"@types/node": "22.14.1",
|
|
56
|
+
"@wkovacs64/eslint-config": "7.5.2",
|
|
57
|
+
"@wkovacs64/prettier-config": "4.1.1",
|
|
58
|
+
"del-cli": "6.0.0",
|
|
59
|
+
"eslint": "9.24.0",
|
|
60
|
+
"npm-run-all2": "7.0.2",
|
|
61
|
+
"prettier": "3.5.3",
|
|
62
|
+
"tsx": "4.19.3"
|
|
63
|
+
}
|
|
64
|
+
}
|