@wkovacs64/add-icon 0.1.0-dev.8f859e16 → 0.1.0-dev.e2dd6945

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,72 +14,27 @@ npm install @wkovacs64/add-icon
14
14
  Or use it directly with npx without installing:
15
15
 
16
16
  ```bash
17
- npx @wkovacs64/add-icon heroicons:arrow-up-circle
17
+ npx @wkovacs64/add-icon <icon> [options]
18
18
  ```
19
19
 
20
20
  ## Usage
21
21
 
22
22
  ### Basic Usage
23
23
 
24
- Download an icon:
24
+ Download an icon to the specified directory:
25
25
 
26
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
27
+ npx @wkovacs64/add-icon heroicons:arrow-up-circle --output-dir ./app/assets/svg-icons
34
28
  ```
35
29
 
36
30
  ### Transformations
37
31
 
38
- Apply built-in transformations:
39
-
40
- ```bash
41
- # Remove width and height attributes
42
- npx @wkovacs64/add-icon heroicons:arrow-up-circle --remove-size
43
-
44
- # Optimize SVG with SVGO
45
- npx @wkovacs64/add-icon heroicons:arrow-up-circle --optimize
46
-
47
- # Minify SVG
48
- npx @wkovacs64/add-icon heroicons:arrow-up-circle --minify
49
-
50
- # Apply multiple transformations
51
- npx @wkovacs64/add-icon heroicons:arrow-up-circle --remove-size --optimize --minify
52
- ```
53
-
54
- ### Custom Transformations
55
-
56
- You can write custom transforms in either JavaScript or TypeScript!
57
-
58
- #### JavaScript Transform
59
-
60
- Create a custom transform file (e.g., `my-transform.js`):
61
-
62
- ```js
63
- /**
64
- * Custom transform to add a title element to SVG
65
- * @param {Object} args - Transform arguments
66
- * @param {string} args.svg - SVG content
67
- * @param {string} args.iconName - Icon name (e.g., 'heroicons:arrow-up-circle')
68
- * @param {string} args.prefix - Icon set prefix (e.g., 'heroicons')
69
- * @param {string} args.name - Icon name without prefix (e.g., 'arrow-up-circle')
70
- * @returns {string} - Transformed SVG
71
- */
72
- export default function addTitle(args) {
73
- const titleElement = `<title>${args.iconName}</title>`;
74
- return args.svg.replace(/<svg([^>]*)>/, `<svg$1>${titleElement}`);
75
- }
76
- ```
32
+ The tool fetches SVG icons directly from the Iconify API with width and height attributes removed automatically. You can optionally provide a transform file using either JavaScript or TypeScript containing custom transformations for more advanced modifications.
77
33
 
78
- #### TypeScript Transform
79
-
80
- Create a custom transform file (e.g., `my-transform.ts`):
34
+ #### TypeScript Transform Example
81
35
 
82
36
  ```ts
37
+ // my-transform.ts
83
38
  import type { TransformArgs } from '@wkovacs64/add-icon';
84
39
 
85
40
  /**
@@ -96,86 +51,29 @@ export default function addTitle(args: TransformArgs): string {
96
51
  Then use it with the CLI:
97
52
 
98
53
  ```bash
99
- # JavaScript transform
100
- npx @wkovacs64/add-icon heroicons:arrow-up-circle --transform ./my-transform.js
101
-
102
- # TypeScript transform
103
54
  npx @wkovacs64/add-icon heroicons:arrow-up-circle --transform ./my-transform.ts
104
55
  ```
105
56
 
106
- ## Configuration File
57
+ ### Configuration File
107
58
 
108
- You can create a configuration file (`add-icon.config.js`) in your project root:
59
+ You can create a configuration file in your project root, using either JavaScript (`add-icon.config.js`) or TypeScript (`add-icon.config.ts`).
109
60
 
110
- ```js
111
- import { transforms } from '@wkovacs64/add-icon';
61
+ #### TypeScript Configuration Example
62
+
63
+ ```ts
64
+ import type { Config, TransformArgs, TransformFunction } from '@wkovacs64/add-icon';
112
65
 
113
66
  // Define custom transform
114
- function addCustomAttribute(args) {
67
+ function addCustomAttribute(args: TransformArgs): string {
115
68
  return args.svg.replace(/<svg/, `<svg data-icon="${args.iconName}"`);
116
69
  }
117
70
 
118
- export default {
71
+ const config = {
119
72
  outputDir: './assets/icons',
120
- transforms: [transforms.removeSize, transforms.optimizeSvg, addCustomAttribute],
121
- };
122
- ```
123
-
124
- ## Using as a Library
125
-
126
- You can also use iconify-cli as a library in your own projects:
127
-
128
- ### JavaScript
129
-
130
- ```js
131
- import { downloadIcon, transforms } from '@wkovacs64/add-icon';
132
-
133
- // Create custom transform
134
- function addCustomAttribute(args) {
135
- return args.svg.replace(/<svg/, `<svg data-custom="${args.iconSet}"`);
136
- }
137
-
138
- // Download an icon with transforms
139
- async function downloadCustomIcon() {
140
- const iconPath = await downloadIcon('heroicons:heart', {
141
- outputDir: './icons',
142
- transforms: [transforms.removeSize, transforms.optimizeSvg, addCustomAttribute],
143
- });
144
-
145
- console.log(`Icon saved to: ${iconPath}`);
146
- }
147
-
148
- downloadCustomIcon();
149
- ```
150
-
151
- ### TypeScript
152
-
153
- ```ts
154
- import { downloadIcon, transforms, type TransformArgs } from '@wkovacs64/add-icon';
155
-
156
- // Create custom transform
157
- const addCustomAttribute = (args: TransformArgs): string => {
158
- return args.svg.replace(/<svg/, `<svg data-custom="${args.iconSet}"`);
159
- };
160
-
161
- // Download an icon with transforms
162
- async function downloadCustomIcon(): Promise<void> {
163
- try {
164
- const iconPath = await downloadIcon('heroicons:heart', {
165
- outputDir: './icons',
166
- transforms: [transforms.removeSize, transforms.optimizeSvg, addCustomAttribute],
167
- });
168
-
169
- console.log(`Icon saved to: ${iconPath}`);
170
- } catch (error) {
171
- console.error(
172
- 'Error downloading icon:',
173
- error instanceof Error ? error.message : String(error),
174
- );
175
- }
176
- }
73
+ transforms: [addCustomAttribute],
74
+ } satisfies Config;
177
75
 
178
- downloadCustomIcon();
76
+ export default config;
179
77
  ```
180
78
 
181
79
  ## License
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+
3
+ import url from 'node:url';
4
+ import path from 'node:path';
5
+ import os from 'node:os';
6
+ import { runCli } from '../dist/index.js';
7
+
8
+ const __filename = url.fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ // This logic ensures we only run the CLI when this file is called directly
12
+ // and not when it's imported as a module
13
+ if (
14
+ os.platform() === 'win32'
15
+ ? process.argv[1] === __filename
16
+ : process.argv[1] === __filename || process.argv[1] === __dirname
17
+ ) {
18
+ runCli();
19
+ }
package/dist/config.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import type { IconifyConfig } from './types.js';
1
+ import type { Config } from './types.js';
2
2
  /**
3
3
  * Default configuration
4
4
  */
5
- export declare const defaultConfig: IconifyConfig;
5
+ export declare const defaultConfig: Config;
6
6
  /**
7
7
  * Loads configuration from file if it exists
8
8
  * @param configPath - Path to config file
9
9
  * @returns Configuration object
10
10
  */
11
- export declare function loadConfig(configPath?: string): Promise<IconifyConfig>;
11
+ export declare function loadConfig(configPath?: string): Promise<Config>;
package/dist/config.js CHANGED
@@ -1,5 +1,6 @@
1
- import fs from 'node:fs';
1
+ import { existsSync } from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { importModule } from './import-module.js';
3
4
  /**
4
5
  * Default configuration
5
6
  */
@@ -12,19 +13,43 @@ export const defaultConfig = {
12
13
  * @returns Configuration object
13
14
  */
14
15
  export async function loadConfig(configPath) {
15
- // Use provided config path or look for default config file
16
- const configFile = configPath || path.resolve(process.cwd(), 'add-icon.config.js');
17
16
  try {
18
- if (fs.existsSync(configFile)) {
19
- // For ESM, we need to use dynamic import with file:// protocol
20
- const fileUrl = `file://${configFile}`;
21
- const config = await import(fileUrl);
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);
22
21
  return { ...defaultConfig, ...config.default };
23
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;
24
50
  }
25
51
  catch (error) {
26
- const errorMessage = error instanceof Error ? error.message : String(error);
27
- console.error(`Error loading config file: ${errorMessage}`);
52
+ console.error('Error loading config, using default config:', error);
53
+ return defaultConfig;
28
54
  }
29
- return defaultConfig;
30
55
  }
package/dist/iconify.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { IconifyConfig } from './types.js';
1
+ import type { Config } from './types.js';
2
2
  /**
3
3
  * Parses an icon reference into iconSet and iconName
4
4
  * @param iconReference - Reference in format 'iconSet:iconName'
@@ -14,4 +14,4 @@ export declare function parseIconReference(iconReference: string): {
14
14
  * @param config - Configuration options
15
15
  * @returns Path to saved icon file
16
16
  */
17
- export declare function downloadIcon(iconReference: string, config: IconifyConfig): Promise<string>;
17
+ export declare function downloadIcon(iconReference: string, config: Config): Promise<string>;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Imports a module file (JavaScript or TypeScript) by processing it with esbuild
3
+ * @param filePath - Path to module file (JS or TS)
4
+ * @returns Module exports
5
+ */
6
+ export declare function importModule(filePath: string): Promise<any>;
@@ -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 CHANGED
@@ -1,4 +1,5 @@
1
- #!/usr/bin/env node
2
- import type { IconTransform, TransformArgs } from './types.js';
3
- export type { IconTransform, TransformArgs };
1
+ import type { TransformFunction, TransformArgs, Config } from './types.js';
2
+ export type { TransformFunction, TransformArgs, Config };
4
3
  export { downloadIcon, parseIconReference } from './iconify.js';
4
+ export { loadConfig, defaultConfig } from './config.js';
5
+ export declare function runCli(): void;
package/dist/index.js CHANGED
@@ -1,25 +1,29 @@
1
- #!/usr/bin/env node
2
- import fs from 'node:fs';
3
1
  import path from 'node:path';
4
- import url from 'node:url';
5
- import os from 'node:os';
6
- import { execSync } from 'node:child_process';
7
2
  import { Command } from 'commander';
8
3
  import { downloadIcon } from './iconify.js';
9
4
  import { loadConfig } from './config.js';
5
+ import { importModule } from './import-module.js';
6
+ import { getPackageInfo } from './package-info.js';
10
7
  // Re-export other useful functions
11
8
  export { downloadIcon, parseIconReference } from './iconify.js';
9
+ export { loadConfig, defaultConfig } from './config.js';
12
10
  // Create CLI program
13
11
  const program = new Command();
14
- program
15
- .name('add-icon')
16
- .description('Download and transform icons from Iconify')
17
- .version('1.0.0')
18
- .argument('<icon>', 'Icon reference (e.g., heroicons:arrow-up-circle)')
19
- .option('-o, --output-dir <dir>', 'Directory to save icon')
20
- .option('-c, --config <path>', 'Path to config file')
21
- .option('-t, --transform <path>', 'Path to custom transform module (.js or .ts)')
22
- .action(async (icon, options) => {
12
+ // Set up the program with package info
13
+ const setupProgram = async () => {
14
+ const { name, version, description } = await getPackageInfo();
15
+ return program
16
+ .name(name.split('/').pop() || name)
17
+ .description(description)
18
+ .version(version, '-v, --version', 'Output the current version')
19
+ .argument('<icon>', 'Icon reference (e.g., heroicons:arrow-up-circle)')
20
+ .option('-o, --output-dir <dir>', 'Directory to save icon')
21
+ .option('-c, --config <path>', 'Path to config file')
22
+ .option('-t, --transform <path>', 'Path to custom transform module (.js or .ts)');
23
+ };
24
+ // Initialize the program
25
+ const initializedProgram = await setupProgram();
26
+ initializedProgram.action(async (icon, options) => {
23
27
  try {
24
28
  // Load config (first from config file, then override with CLI options)
25
29
  const config = await loadConfig(options.config);
@@ -32,30 +36,14 @@ program
32
36
  try {
33
37
  const transformPath = path.resolve(process.cwd(), options.transform);
34
38
  let customTransform;
35
- // Handle TypeScript files
36
- if (transformPath.endsWith('.ts')) {
37
- // Create a temporary JS file for the transform
38
- const jsPath = transformPath.replace(/\.ts$/, '.js');
39
- try {
40
- // Use tsc to compile the TypeScript file
41
- execSync(`npx tsc "${transformPath}" --outDir "${path.dirname(transformPath)}" --target es2020 --module NodeNext --moduleResolution NodeNext --esModuleInterop`);
42
- // Import the compiled JS file
43
- customTransform = await import(`file://${jsPath}`);
44
- // Clean up temporary JS file if not in dev mode
45
- if (process.env.NODE_ENV !== 'development') {
46
- fs.unlinkSync(jsPath);
47
- }
48
- }
49
- catch (err) {
50
- const errorMessage = err instanceof Error ? err.message : String(err);
51
- console.error(`Error transpiling TypeScript transform: ${errorMessage}`);
52
- console.error('Make sure TypeScript is installed or use a JavaScript (.js) transform file.');
53
- process.exit(1);
54
- }
39
+ try {
40
+ // Use unified import method for both JS and TS files
41
+ customTransform = await importModule(transformPath);
55
42
  }
56
- else {
57
- // For JavaScript files, use dynamic import
58
- customTransform = await import(`file://${transformPath}`);
43
+ catch (err) {
44
+ const errorMessage = err instanceof Error ? err.message : String(err);
45
+ console.error(`Error loading transform: ${errorMessage}`);
46
+ process.exit(1);
59
47
  }
60
48
  if (customTransform && typeof customTransform.default === 'function') {
61
49
  config.transforms = [customTransform.default];
@@ -82,12 +70,7 @@ program
82
70
  process.exit(1);
83
71
  }
84
72
  });
85
- const __filename = url.fileURLToPath(import.meta.url);
86
- const __dirname = path.dirname(__filename);
87
- // This logic only runs when executed directly as CLI, not when imported as a library
88
- if (os.platform() === 'win32'
89
- ? process.argv[1] === __filename
90
- : process.argv[1] === __filename || process.argv[1] === __dirname) {
91
- // Parse command line arguments
92
- program.parse();
73
+ // Parse command line arguments if called directly
74
+ export function runCli() {
75
+ program.parse(process.argv);
93
76
  }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Loads package info from package.json
3
+ * @returns Package info with name, version, and description
4
+ */
5
+ export declare function getPackageInfo(): Promise<{
6
+ name: string;
7
+ version: string;
8
+ description: string;
9
+ }>;
@@ -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 CHANGED
@@ -1,24 +1,24 @@
1
1
  /**
2
2
  * Arguments passed to transform functions
3
3
  */
4
- export interface TransformArgs {
4
+ export type TransformArgs = {
5
5
  /** The SVG content as a string */
6
6
  svg: string;
7
7
  /** The icon set (e.g., 'heroicons') */
8
8
  iconSet: string;
9
9
  /** The icon name (e.g., 'arrow-up-circle') */
10
10
  iconName: string;
11
- }
11
+ };
12
12
  /**
13
13
  * SVG transformation function type
14
14
  */
15
- export type IconTransform = (args: TransformArgs) => Promise<string> | string;
15
+ export type TransformFunction = (args: TransformArgs) => Promise<string> | string;
16
16
  /**
17
- * Configuration options for the Iconify CLI
17
+ * Configuration options for the add-icon CLI
18
18
  */
19
- export interface IconifyConfig {
19
+ export type Config = {
20
20
  /** Directory to output icons */
21
21
  outputDir: string;
22
22
  /** Array of transform functions to apply to icons */
23
- transforms?: IconTransform[];
24
- }
23
+ transforms?: TransformFunction[];
24
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wkovacs64/add-icon",
3
- "version": "0.1.0-dev.8f859e16",
3
+ "version": "0.1.0-dev.e2dd6945",
4
4
  "description": "CLI tool to download and transform icons from Iconify",
5
5
  "keywords": [
6
6
  "iconify",
@@ -18,10 +18,11 @@
18
18
  "./package.json": "./package.json"
19
19
  },
20
20
  "bin": {
21
- "add-icon": "dist/index.js"
21
+ "add-icon": "bin/add-icon.js"
22
22
  },
23
23
  "files": [
24
- "dist"
24
+ "dist",
25
+ "bin"
25
26
  ],
26
27
  "scripts": {
27
28
  "build": "tsc",
@@ -44,7 +45,7 @@
44
45
  },
45
46
  "dependencies": {
46
47
  "commander": "^13.1.0",
47
- "tsx": "^4.19.3",
48
+ "esbuild": "~0.25.2",
48
49
  "typescript": "^5.8.3"
49
50
  },
50
51
  "devDependencies": {
@@ -57,6 +58,7 @@
57
58
  "del-cli": "6.0.0",
58
59
  "eslint": "9.24.0",
59
60
  "npm-run-all2": "7.0.2",
60
- "prettier": "3.5.3"
61
+ "prettier": "3.5.3",
62
+ "tsx": "4.19.3"
61
63
  }
62
64
  }