@frontfriend/tailwind 2.1.0 → 2.1.2

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/dist/cli.js CHANGED
@@ -1,23 +1,65 @@
1
1
  #!/usr/bin/env node
2
- var{Command:v}=require("commander"),m=require("./lib/config/config-loader"),h=require("./lib/config/cache-manager"),{APIClient:y}=require("./lib/core/api-client"),{ComponentDownloader:b}=require("./lib/core/component-downloader"),x=require("./lib/core/token-processor"),{ConfigError:d,APIError:D,CacheError:w}=require("./lib/core/errors"),I=require("path"),i=new v;i.name("frontfriend").description("FrontFriend Tailwind CLI - Manage design tokens and cache").version("2.0.0");i.command("init").description("Initialize FrontFriend configuration and fetch from API").option("--force","Force refresh even if cache is valid").action(async n=>{try{console.log("\u{1F527} FrontFriend Setup"),console.log(`==================
3
- `);let o=await new m().load();if(!o.ffId)throw new d(`ff-id not found in frontfriend.config.js
2
+ var O=(e,n)=>()=>(n||e((n={exports:{}}).exports,n),n.exports);var S=O((V,I)=>{var u=require("fs"),F=require("path");function D(){let e=process.cwd(),n=F.join(e,"node_modules",".cache","frontfriend","icons.json"),o=F.join(e,"types","frontfriend-icons.d.ts");u.existsSync(n)||(console.error('[FrontFriend] Error: icons.json not found. Please run "npx frontfriend setup" first.'),process.exit(1));try{let r=function(t){for(let i of Object.values(t))typeof i=="string"?s.add(i):typeof i=="object"&&i!==null&&r(i)},g=function(t,i=" "){let f=[];for(let[k,h]of Object.entries(t))typeof h=="string"?f.push(`${i}${k}: '${h}';`):typeof h=="object"&&h!==null&&(f.push(`${i}${k}: {`),f.push(g(h,i+" ")),f.push(`${i}};`));return f.join(`
3
+ `)};var a=r,c=g;let l=JSON.parse(u.readFileSync(n,"utf-8")),s=new Set;r(l);let p=Array.from(s).map(t=>`'${t}'`).join(" | "),y=`// Generated by @frontfriend/tailwind
4
+ // DO NOT EDIT THIS FILE MANUALLY
5
+
6
+ import type { IconSetStructure } from '@frontfriend/tailwind';
7
+
8
+ declare module '@frontfriend/tailwind' {
9
+ // Icon names used in this project
10
+ export type ProjectIconName = ${p||"string"};
11
+
12
+ // Typed iconSet structure with exact icon names
13
+ export interface TypedIconSet extends IconSetStructure {
14
+ ${g(l)}
15
+ }
16
+
17
+ // Override the iconSet type with the typed version
18
+ export const iconSet: TypedIconSet;
19
+ }
20
+
21
+ // Augment Vue component types with exact icon names
22
+ declare module '*/components/ui/icon/*.vue' {
23
+ export type IconName = ${p||"string"};
24
+ }
25
+
26
+ declare module '@/components/ui/icon/*.vue' {
27
+ export type IconName = ${p||"string"};
28
+ }
29
+
30
+ declare module '@/components/ui/icon' {
31
+ export type IconName = ${p||"string"};
32
+ }
33
+
34
+ declare module '@/src/components/ui/icon' {
35
+ export type IconName = ${p||"string"};
36
+ }
37
+
38
+ declare module '@/src/components/ui/icon/Icon.vue' {
39
+ export type IconName = ${p||"string"};
40
+ }
41
+ `,$=F.dirname(o);u.existsSync($)||u.mkdirSync($,{recursive:!0}),u.writeFileSync(o,y),console.log("[FrontFriend] \u2713 Generated icon types at types/frontfriend-icons.d.ts");let w=F.join(e,"tsconfig.json");if(u.existsSync(w)){let t=JSON.parse(u.readFileSync(w,"utf-8"));t.include||(t.include=[]);let i="types/**/*.d.ts";t.include.includes(i)||(t.include.push(i),u.writeFileSync(w,JSON.stringify(t,null,2)),console.log("[FrontFriend] \u2713 Updated tsconfig.json to include generated types"))}}catch(l){console.error("[FrontFriend] Error generating icon types:",l.message),process.exit(1)}}require.main===I&&D();I.exports={generateIconTypes:D}});var{Command:L}=require("commander"),x=require("./lib/config/config-loader"),j=require("./lib/config/cache-manager"),{APIClient:R}=require("./lib/core/api-client"),{ComponentDownloader:q}=require("./lib/core/component-downloader"),N=require("./lib/core/token-processor"),{ConfigError:C,APIError:E,CacheError:T}=require("./lib/core/errors"),b=require("path"),m=new L;m.name("frontfriend").description("FrontFriend Tailwind CLI - Manage design tokens and cache").version("2.0.0");m.command("init").description("Initialize FrontFriend configuration and fetch from API").option("--force","Force refresh even if cache is valid").action(async e=>{try{console.log("\u{1F527} FrontFriend Setup"),console.log(`==================
42
+ `);let o=await new x().load();if(!o.ffId)throw new C(`ff-id not found in frontfriend.config.js
4
43
  Please add "ff-id" to your frontfriend.config.js file`,"ff-id");console.log("\u{1F4CB} Configuration loaded"),console.log(` FF_ID: ${o.ffId}`),console.log(` App Root: ${o.appRoot}
5
- `);let l=new h(o.appRoot);!n.force&&l.exists()&&l.isValid()&&(console.log("\u2705 Cache is valid and up to date"),console.log(" Use --force to refresh anyway"),process.exit(0)),console.log("\u{1F50D} Checking cache..."),n.force?console.log(` Force refresh requested
6
- `):l.exists()?console.log(` Cache expired
44
+ `);let a=new j(o.appRoot);!e.force&&a.exists()&&a.isValid()&&(console.log("\u2705 Cache is valid and up to date"),console.log(" Use --force to refresh anyway"),process.exit(0)),console.log("\u{1F50D} Checking cache..."),e.force?console.log(` Force refresh requested
45
+ `):a.exists()?console.log(` Cache expired
7
46
  `):console.log(` No cache found
8
- `),console.log("\u{1F310} Fetching configuration...");let c=new y(o.ffId,{baseURL:o["api-url"]||o.apiUrl});(o["api-url"]||o.apiUrl)&&console.log(` Using API URL: ${o["api-url"]||o.apiUrl}`);let r=null;try{r=await c.fetchProcessedTokens()}catch(u){console.log(` \u26A0\uFE0F Failed to fetch pre-processed tokens: ${u.message}`)}r&&(console.log(" \u2713 Pre-processed tokens fetched from server"),console.log(` \u2713 Skipping local processing
9
- `),console.log("\u{1F4BE} Saving cache..."),l.save(r),console.log(` \u2713 Cache saved successfully
10
- `),console.log("\u2705 Setup complete!"),console.log(" Your FrontFriend configuration has been cached."),console.log(" The Tailwind plugin will now use this cached data."),process.exit(0)),console.log(` \u2139\uFE0F Pre-processed tokens not available, falling back to local processing
11
- `);let[e,a,g,k,p,$]=await Promise.all([c.fetchTokens(),c.fetchComponentsConfig(),c.fetchFonts(),c.fetchIcons(),c.fetchVersion(),c.fetchCustomCss()]);if(console.log(" \u2713 Tokens fetched"),e.global||e.colors||e.semantic||e.semanticDark){let u=[e.global?"global":null,e.colors?"colors":null,e.semantic?"semantic":null,e.semanticDark?"semanticDark":null].filter(Boolean).join(", ");console.log(` (Found: ${u})`)}console.log(" \u2713 Components config fetched"),console.log(" \u2713 Fonts fetched"),console.log(" \u2713 Icons fetched"),console.log(" \u2713 Version fetched"),console.log(` \u2713 Custom CSS fetched
12
- `),console.log("\u2699\uFE0F Processing tokens...");let C=new x;console.log(" Processing raw token data...");let t=await C.process({colors:e.colors||e.global,semantic:e.semantic,semanticDark:e.semanticDark,fonts:g,globalTokens:e.global,animations:e.animations,version:p==null?void 0:p.version,ffId:o.ffId,customCss:$});console.log(" \u2713 Colors processed"),console.log(" \u2713 Semantic tokens processed"),t.fontFaces.length>0&&console.log(` \u2713 ${t.fontFaces.length} font faces processed`),Object.keys(t.animations).length>0&&console.log(` \u2713 ${Object.keys(t.animations).length} animations processed`),console.log(`
13
- `);let F=C.extractClassesFromComponentsConfig(a),f={tokens:t.utilities,variables:t.variables,semanticVariables:t.semanticVariables,semanticDarkVariables:t.semanticDarkVariables,fonts:t.fontFaces,icons:k,componentsConfig:a,keyframes:t.keyframes,animations:t.animations,safelist:t.safelist,version:p,metadata:t.metadata,cls:F||[],custom:t.custom};console.log("\u{1F4BE} Saving cache..."),console.log(" Cache data summary:"),console.log(` - Tokens: ${Object.keys(f.tokens||{}).length} utilities`),console.log(` - Variables: ${Object.keys(f.variables||{}).length} CSS variables`),console.log(` - Semantic variables: ${Object.keys(f.semanticVariables||{}).length} semantic variables`),console.log(` - Font faces: ${(f.fonts||[]).length} fonts`),console.log(` - Classes: ${(f.cls||[]).length} extracted classes`),l.save(f),console.log(` \u2713 Cache saved successfully
14
- `),console.log("\u2705 Setup complete!"),console.log(" Your FrontFriend configuration has been cached."),console.log(" The Tailwind plugin will now use this cached data."),process.exit(0)}catch(s){s instanceof d?console.error("\u274C Configuration Error:",s.message):s instanceof D?(console.error("\u274C API Error:",s.message),console.error(` Status: ${s.statusCode}`),console.error(` URL: ${s.url}`)):s instanceof w?(console.error("\u274C Cache Error:",s.message),console.error(` Operation: ${s.operation}`)):console.error("\u274C Setup failed:",s.message),process.exit(1)}});i.command("clean").description("Remove cached configuration").action(async()=>{try{console.log(`\u{1F9F9} Cleaning FrontFriend cache...
15
- `);let s=new m().load(),o=new h(s.appRoot);o.exists()?(o.clear(),console.log("\u2705 Cache cleared successfully")):console.log("\u2139\uFE0F No cache found"),process.exit(0)}catch(n){n instanceof w?(console.error("\u274C Cache Error:",n.message),console.error(` Operation: ${n.operation}`)):console.error("\u274C Clean failed:",n.message),process.exit(1)}});i.command("status").description("Show cache status and configuration").action(async()=>{try{console.log("\u{1F4CA} FrontFriend Status"),console.log(`===================
16
- `);let s=await new m().load();console.log("Configuration:"),console.log(` FF_ID: ${s.ffId||"Not set"}`),console.log(` App Root: ${s.appRoot}
17
- `);let o=new h(s.appRoot),l=o.getCacheDir();if(console.log("Cache:"),console.log(` Directory: ${I.relative(process.cwd(),l)}`),o.exists()){console.log(` Status: ${o.isValid()?"\u2705 Valid":"\u26A0\uFE0F Expired"}`);let c=o.load();if(c&&c.metadata){let r=new Date(c.metadata.timestamp);console.log(` Created: ${r.toLocaleString()}`),console.log(` Version: ${c.metadata.version||"Unknown"}`)}}else console.log(" Status: \u274C Not found");process.exit(0)}catch(n){n instanceof d?console.error("\u274C Configuration Error:",n.message):n instanceof w?(console.error("\u274C Cache Error:",n.message),console.error(` Operation: ${n.operation}`)):console.error("\u274C Status check failed:",n.message),process.exit(1)}});i.command("download").description("Download components from registry").argument("<components...>",'component names to download (or "all" to download all components)').option("-f, --framework <framework>","Framework to download components for (react/vue)").option("-o, --output <path>","Output directory for components").option("--overwrite","Overwrite existing files").action(async(n,s)=>{try{console.log(`\u{1F4E6} Downloading components...
18
- `);let l=await new m().load();if(!l.ffId)throw new d(`ff-id not found in frontfriend.config.js
19
- Please add "ff-id" to your frontfriend.config.js file`,"ff-id");let c=new b({appRoot:l.appRoot,config:l,framework:s.framework,outputPath:s.output,overwrite:s.overwrite}),r=n;if(n.length===1&&n[0].toLowerCase()==="all"){let a=await c.getAllComponents(),g=await c.getCustomComponents();a.length===0&&(console.error("\u274C Failed to fetch component list"),process.exit(1)),r=[...a,...g],console.log("\u{1F4CB} Configuration"),console.log(` Framework: ${c.framework}`),console.log(` Output: ${c.outputPath}`),console.log(` Components: All ${a.length} standard components`),g.length>0&&console.log(` Custom: ${g.length} custom components for ff-id: ${l.ffId||l["ff-id"]}`),console.log("")}else console.log("\u{1F4CB} Configuration"),console.log(` Framework: ${c.framework}`),console.log(` Output: ${c.outputPath}`),console.log(` Components: ${n.join(", ")}
20
- `);let e=await c.downloadComponents(r);e.successful.length>0&&(console.log(`
21
- \u2705 Download complete!`),console.log(` Downloaded: ${e.successful.length} components`)),e.failed.length>0&&(console.log(""),e.failed.forEach(a=>{a.error.includes("not found")?console.log(`\u274C ${a.error}`):console.log(`\u274C Failed to download ${a.component}: ${a.error}`)})),e.successful.length>0&&e.dependencies.length>0&&(console.log(`
22
- \u{1F4E6} Install dependencies:`),console.log(` npm install ${e.dependencies.join(" ")}`)),e.successful.length===0&&e.failed.length>0&&process.exit(1),process.exit(0)}catch(o){o instanceof d?(console.error("\u274C Configuration Error:",o.message),o.missingKey&&console.error(` Missing key: ${o.missingKey}`)):console.error("\u274C Download failed:",o.message),process.exit(1)}});i.parse(process.argv);process.argv.slice(2).length||(i.outputHelp(),process.exit(0));
47
+ `),console.log("\u{1F310} Fetching configuration...");let c=new R(o.ffId,{baseURL:o["api-url"]||o.apiUrl});(o["api-url"]||o.apiUrl)&&console.log(` Using API URL: ${o["api-url"]||o.apiUrl}`);let l=null;try{l=await c.fetchProcessedTokens()}catch(d){console.log(` \u26A0\uFE0F Failed to fetch pre-processed tokens: ${d.message}`)}if(l){console.log(" \u2713 Pre-processed tokens fetched from server"),console.log(` \u2713 Skipping local processing
48
+ `),console.log("\u{1F4BE} Saving cache..."),a.save(l),console.log(` \u2713 Cache saved successfully
49
+ `),console.log("\u2705 Setup complete!"),console.log(" Your FrontFriend configuration has been cached."),console.log(" The Tailwind plugin will now use this cached data.");let d=require("fs"),P=b.join(o.appRoot,"tsconfig.json");if(d.existsSync(P)){console.log(`
50
+ \u{1F524} Generating TypeScript types...`);try{let{generateIconTypes:v}=S();v()}catch(v){console.log(" \u26A0\uFE0F Type generation failed:",v.message),console.log(' Run "npx frontfriend generate-types" manually to retry')}}process.exit(0)}console.log(` \u2139\uFE0F Pre-processed tokens not available, falling back to local processing
51
+ `);let[s,r,g,p,y,$]=await Promise.all([c.fetchTokens(),c.fetchComponentsConfig(),c.fetchFonts(),c.fetchIcons(),c.fetchVersion(),c.fetchCustomCss()]);if(console.log(" \u2713 Tokens fetched"),s.global||s.colors||s.semantic||s.semanticDark){let d=[s.global?"global":null,s.colors?"colors":null,s.semantic?"semantic":null,s.semanticDark?"semanticDark":null].filter(Boolean).join(", ");console.log(` (Found: ${d})`)}console.log(" \u2713 Components config fetched"),console.log(" \u2713 Fonts fetched"),console.log(" \u2713 Icons fetched"),console.log(" \u2713 Version fetched"),console.log(` \u2713 Custom CSS fetched
52
+ `),console.log("\u2699\uFE0F Processing tokens...");let w=new N;console.log(" Processing raw token data...");let t=await w.process({colors:s.colors||s.global,semantic:s.semantic,semanticDark:s.semanticDark,fonts:g,globalTokens:s.global,animations:s.animations,version:y==null?void 0:y.version,ffId:o.ffId,customCss:$});console.log(" \u2713 Colors processed"),console.log(" \u2713 Semantic tokens processed"),t.fontFaces.length>0&&console.log(` \u2713 ${t.fontFaces.length} font faces processed`),Object.keys(t.animations).length>0&&console.log(` \u2713 ${Object.keys(t.animations).length} animations processed`),console.log(`
53
+ `);let i=w.extractClassesFromComponentsConfig(r),f={tokens:t.utilities,variables:t.variables,semanticVariables:t.semanticVariables,semanticDarkVariables:t.semanticDarkVariables,fonts:t.fontFaces,icons:p,componentsConfig:r,keyframes:t.keyframes,animations:t.animations,safelist:t.safelist,version:y,metadata:t.metadata,cls:i||[],custom:t.custom};console.log("\u{1F4BE} Saving cache..."),console.log(" Cache data summary:"),console.log(` - Tokens: ${Object.keys(f.tokens||{}).length} utilities`),console.log(` - Variables: ${Object.keys(f.variables||{}).length} CSS variables`),console.log(` - Semantic variables: ${Object.keys(f.semanticVariables||{}).length} semantic variables`),console.log(` - Font faces: ${(f.fonts||[]).length} fonts`),console.log(` - Classes: ${(f.cls||[]).length} extracted classes`),a.save(f),console.log(` \u2713 Cache saved successfully
54
+ `),console.log("\u2705 Setup complete!"),console.log(" Your FrontFriend configuration has been cached."),console.log(" The Tailwind plugin will now use this cached data.");let k=require("fs"),h=b.join(o.appRoot,"tsconfig.json");if(k.existsSync(h)){console.log(`
55
+ \u{1F524} Generating TypeScript types...`);try{let{generateIconTypes:d}=S();d()}catch(d){console.log(" \u26A0\uFE0F Type generation failed:",d.message),console.log(' Run "npx frontfriend generate-types" manually to retry')}}process.exit(0)}catch(n){n instanceof C?console.error("\u274C Configuration Error:",n.message):n instanceof E?(console.error("\u274C API Error:",n.message),console.error(` Status: ${n.statusCode}`),console.error(` URL: ${n.url}`)):n instanceof T?(console.error("\u274C Cache Error:",n.message),console.error(` Operation: ${n.operation}`)):console.error("\u274C Setup failed:",n.message),process.exit(1)}});m.command("clean").description("Remove cached configuration").action(async()=>{try{console.log(`\u{1F9F9} Cleaning FrontFriend cache...
56
+ `);let n=new x().load(),o=new j(n.appRoot);o.exists()?(o.clear(),console.log("\u2705 Cache cleared successfully")):console.log("\u2139\uFE0F No cache found"),process.exit(0)}catch(e){e instanceof T?(console.error("\u274C Cache Error:",e.message),console.error(` Operation: ${e.operation}`)):console.error("\u274C Clean failed:",e.message),process.exit(1)}});m.command("generate-types").description("Generate TypeScript types from cached icons.json").action(async()=>{try{console.log(`\u{1F524} Generating TypeScript types...
57
+ `);let{generateIconTypes:e}=S();e(),process.exit(0)}catch(e){console.error("\u274C Type generation failed:",e.message),process.exit(1)}});m.command("status").description("Show cache status and configuration").action(async()=>{try{console.log("\u{1F4CA} FrontFriend Status"),console.log(`===================
58
+ `);let n=await new x().load();console.log("Configuration:"),console.log(` FF_ID: ${n.ffId||"Not set"}`),console.log(` App Root: ${n.appRoot}
59
+ `);let o=new j(n.appRoot),a=o.getCacheDir();if(console.log("Cache:"),console.log(` Directory: ${b.relative(process.cwd(),a)}`),o.exists()){console.log(` Status: ${o.isValid()?"\u2705 Valid":"\u26A0\uFE0F Expired"}`);let c=o.load();if(c&&c.metadata){let l=new Date(c.metadata.timestamp);console.log(` Created: ${l.toLocaleString()}`),console.log(` Version: ${c.metadata.version||"Unknown"}`)}}else console.log(" Status: \u274C Not found");process.exit(0)}catch(e){e instanceof C?console.error("\u274C Configuration Error:",e.message):e instanceof T?(console.error("\u274C Cache Error:",e.message),console.error(` Operation: ${e.operation}`)):console.error("\u274C Status check failed:",e.message),process.exit(1)}});m.command("download").description("Download components from registry").argument("<components...>",'component names to download (or "all" to download all components)').option("-f, --framework <framework>","Framework to download components for (react/vue)").option("-o, --output <path>","Output directory for components").option("--overwrite","Overwrite existing files").action(async(e,n)=>{try{console.log(`\u{1F4E6} Downloading components...
60
+ `);let a=await new x().load();if(!a.ffId)throw new C(`ff-id not found in frontfriend.config.js
61
+ Please add "ff-id" to your frontfriend.config.js file`,"ff-id");let c=new q({appRoot:a.appRoot,config:a,framework:n.framework,outputPath:n.output,overwrite:n.overwrite}),l=e;if(e.length===1&&e[0].toLowerCase()==="all"){let r=await c.getAllComponents(),g=await c.getCustomComponents();r.length===0&&(console.error("\u274C Failed to fetch component list"),process.exit(1)),l=[...r,...g],console.log("\u{1F4CB} Configuration"),console.log(` Framework: ${c.framework}`),console.log(` Output: ${c.outputPath}`),console.log(` Components: All ${r.length} standard components`),g.length>0&&console.log(` Custom: ${g.length} custom components for ff-id: ${a.ffId||a["ff-id"]}`),console.log("")}else console.log("\u{1F4CB} Configuration"),console.log(` Framework: ${c.framework}`),console.log(` Output: ${c.outputPath}`),console.log(` Components: ${e.join(", ")}
62
+ `);let s=await c.downloadComponents(l);s.successful.length>0&&(console.log(`
63
+ \u2705 Download complete!`),console.log(` Downloaded: ${s.successful.length} components`)),s.failed.length>0&&(console.log(""),s.failed.forEach(r=>{r.error.includes("not found")?console.log(`\u274C ${r.error}`):console.log(`\u274C Failed to download ${r.component}: ${r.error}`)})),s.successful.length>0&&s.dependencies.length>0&&(console.log(`
64
+ \u{1F4E6} Install dependencies:`),console.log(` npm install ${s.dependencies.join(" ")}`)),s.successful.length===0&&s.failed.length>0&&process.exit(1),process.exit(0)}catch(o){o instanceof C?(console.error("\u274C Configuration Error:",o.message),o.missingKey&&console.error(` Missing key: ${o.missingKey}`)):console.error("\u274C Download failed:",o.message),process.exit(1)}});m.parse(process.argv);process.argv.slice(2).length||(m.outputHelp(),process.exit(0));
23
65
  //# sourceMappingURL=cli-temp.js.map
@@ -1,3 +1,5 @@
1
+ import type { Ref, ComputedRef } from 'vue';
2
+
1
3
  /**
2
4
  * Combines class names with tailwind-merge
3
5
  * @param inputs - Class names to combine
@@ -10,16 +12,16 @@ export function cn(...inputs: any[]): string;
10
12
  * @param query - Media query string
11
13
  * @returns Ref<boolean> indicating if query matches
12
14
  */
13
- export function useMediaQuery(query: string): import('vue').Ref<boolean>;
15
+ export function useMediaQuery(query: string): Ref<boolean>;
14
16
 
15
17
  /**
16
18
  * Vue composition API hook for responsive breakpoints
17
19
  * @returns Object with breakpoint states and active breakpoint
18
20
  */
19
21
  export function useBreakpoints(): {
20
- isXs: import('vue').Ref<boolean>;
21
- isSm: import('vue').Ref<boolean>;
22
- isMd: import('vue').Ref<boolean>;
23
- isLg: import('vue').Ref<boolean>;
24
- active: import('vue').ComputedRef<'xs' | 'sm' | 'md' | 'lg'>;
22
+ isXs: Ref<boolean>;
23
+ isSm: Ref<boolean>;
24
+ isMd: Ref<boolean>;
25
+ isLg: Ref<boolean>;
26
+ active: ComputedRef<'xs' | 'sm' | 'md' | 'lg'>;
25
27
  };
package/dist/runtime.d.ts CHANGED
@@ -1,8 +1,10 @@
1
+ import { IconSetStructure } from './types/index';
2
+
1
3
  export interface FrontFriend {
2
4
  /** Current configuration */
3
5
  config: Record<string, any> | null;
4
6
  /** Icon set */
5
- iconSet: Record<string, string> | null;
7
+ iconSet: IconSetStructure | null;
6
8
  /** Get an icon by name */
7
9
  getIcon(name: string): string | null;
8
10
  /** Refresh configuration from globals */
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ /**
7
+ * Generate TypeScript types from icons.json cache file
8
+ * This script reads the icons.json file and generates proper types for the iconSet
9
+ */
10
+
11
+ function generateIconTypes() {
12
+ const projectRoot = process.cwd();
13
+ const iconsPath = path.join(projectRoot, 'node_modules', '.cache', 'frontfriend', 'icons.json');
14
+ const outputPath = path.join(projectRoot, 'types', 'frontfriend-icons.d.ts');
15
+
16
+ // Check if icons.json exists
17
+ if (!fs.existsSync(iconsPath)) {
18
+ console.error('[FrontFriend] Error: icons.json not found. Please run "npx frontfriend setup" first.');
19
+ process.exit(1);
20
+ }
21
+
22
+ try {
23
+ // Read icons.json
24
+ const icons = JSON.parse(fs.readFileSync(iconsPath, 'utf-8'));
25
+
26
+ // Extract all unique icon names
27
+ const iconNames = new Set();
28
+
29
+ function extractIconNames(obj) {
30
+ for (const value of Object.values(obj)) {
31
+ if (typeof value === 'string') {
32
+ iconNames.add(value);
33
+ } else if (typeof value === 'object' && value !== null) {
34
+ extractIconNames(value);
35
+ }
36
+ }
37
+ }
38
+
39
+ extractIconNames(icons);
40
+
41
+ // Generate the interface structure recursively
42
+ function generateInterface(obj, indent = ' ') {
43
+ const lines = [];
44
+
45
+ for (const [key, value] of Object.entries(obj)) {
46
+ if (typeof value === 'string') {
47
+ lines.push(`${indent}${key}: '${value}';`);
48
+ } else if (typeof value === 'object' && value !== null) {
49
+ lines.push(`${indent}${key}: {`);
50
+ lines.push(generateInterface(value, indent + ' '));
51
+ lines.push(`${indent}};`);
52
+ }
53
+ }
54
+
55
+ return lines.join('\n');
56
+ }
57
+
58
+ // Generate the type definition file
59
+ const iconNameType = Array.from(iconNames).map(name => `'${name}'`).join(' | ');
60
+
61
+ const typeDefinition = `// Generated by @frontfriend/tailwind
62
+ // DO NOT EDIT THIS FILE MANUALLY
63
+
64
+ import type { IconSetStructure } from '@frontfriend/tailwind';
65
+
66
+ declare module '@frontfriend/tailwind' {
67
+ // Icon names used in this project
68
+ export type ProjectIconName = ${iconNameType || 'string'};
69
+
70
+ // Typed iconSet structure with exact icon names
71
+ export interface TypedIconSet extends IconSetStructure {
72
+ ${generateInterface(icons)}
73
+ }
74
+
75
+ // Override the iconSet type with the typed version
76
+ export const iconSet: TypedIconSet;
77
+ }
78
+
79
+ // Augment Vue component types with exact icon names
80
+ declare module '*/components/ui/icon/*.vue' {
81
+ export type IconName = ${iconNameType || 'string'};
82
+ }
83
+
84
+ declare module '@/components/ui/icon/*.vue' {
85
+ export type IconName = ${iconNameType || 'string'};
86
+ }
87
+
88
+ declare module '@/components/ui/icon' {
89
+ export type IconName = ${iconNameType || 'string'};
90
+ }
91
+
92
+ declare module '@/src/components/ui/icon' {
93
+ export type IconName = ${iconNameType || 'string'};
94
+ }
95
+
96
+ declare module '@/src/components/ui/icon/Icon.vue' {
97
+ export type IconName = ${iconNameType || 'string'};
98
+ }
99
+ `;
100
+
101
+ // Ensure types directory exists
102
+ const typesDir = path.dirname(outputPath);
103
+ if (!fs.existsSync(typesDir)) {
104
+ fs.mkdirSync(typesDir, { recursive: true });
105
+ }
106
+
107
+ // Write the type definition file
108
+ fs.writeFileSync(outputPath, typeDefinition);
109
+
110
+ console.log('[FrontFriend] ✓ Generated icon types at types/frontfriend-icons.d.ts');
111
+
112
+ // Check if tsconfig.json exists and update it
113
+ const tsconfigPath = path.join(projectRoot, 'tsconfig.json');
114
+ if (fs.existsSync(tsconfigPath)) {
115
+ const tsconfig = JSON.parse(fs.readFileSync(tsconfigPath, 'utf-8'));
116
+
117
+ // Ensure the types file is included
118
+ if (!tsconfig.include) {
119
+ tsconfig.include = [];
120
+ }
121
+
122
+ const typesPattern = 'types/**/*.d.ts';
123
+ if (!tsconfig.include.includes(typesPattern)) {
124
+ tsconfig.include.push(typesPattern);
125
+ fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2));
126
+ console.log('[FrontFriend] ✓ Updated tsconfig.json to include generated types');
127
+ }
128
+ }
129
+
130
+ } catch (error) {
131
+ console.error('[FrontFriend] Error generating icon types:', error.message);
132
+ process.exit(1);
133
+ }
134
+ }
135
+
136
+ // Run if called directly
137
+ if (require.main === module) {
138
+ generateIconTypes();
139
+ }
140
+
141
+ module.exports = { generateIconTypes };
@@ -1,5 +1,5 @@
1
1
  declare module '@frontfriend/tailwind' {
2
- // Main plugin
2
+ // Main Plugin
3
3
  import { Config, PluginCreator } from 'tailwindcss/types/config';
4
4
 
5
5
  export interface PluginOptions {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontfriend/tailwind",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "description": "Design token management for Tailwind CSS",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/types/index.d.ts",
@@ -53,6 +53,7 @@
53
53
  "license": "SEE LICENSE IN LICENSE",
54
54
  "files": [
55
55
  "dist/",
56
+ "scripts/",
56
57
  "README.md",
57
58
  "LICENSE"
58
59
  ],
@@ -0,0 +1,371 @@
1
+ const esbuild = require('esbuild');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const { glob } = require('glob');
5
+
6
+ // Clean dist directory
7
+ const distDir = path.join(__dirname, '..', 'dist');
8
+ if (fs.existsSync(distDir)) {
9
+ fs.rmSync(distDir, { recursive: true, force: true });
10
+ }
11
+ fs.mkdirSync(distDir, { recursive: true });
12
+
13
+ // Create lib subdirectories
14
+ fs.mkdirSync(path.join(distDir, 'lib', 'core'), { recursive: true });
15
+ fs.mkdirSync(path.join(distDir, 'lib', 'config'), { recursive: true });
16
+ fs.mkdirSync(path.join(distDir, 'lib', 'react'), { recursive: true });
17
+ fs.mkdirSync(path.join(distDir, 'lib', 'vue'), { recursive: true });
18
+ fs.mkdirSync(path.join(distDir, 'scripts'), { recursive: true });
19
+
20
+ // Copy type definitions
21
+ fs.mkdirSync(path.join(distDir, 'types'), { recursive: true });
22
+ fs.copyFileSync(
23
+ path.join(__dirname, '..', 'types', 'index.d.ts'),
24
+ path.join(distDir, 'types', 'index.d.ts')
25
+ );
26
+
27
+ // Create individual type declaration files for each export
28
+ const typeDeclarations = [
29
+ {
30
+ path: 'runtime.d.ts',
31
+ content: `import { IconSetStructure } from './types/index';
32
+
33
+ export interface FrontFriend {
34
+ /** Current configuration */
35
+ config: Record<string, any> | null;
36
+ /** Icon set */
37
+ iconSet: IconSetStructure | null;
38
+ /** Get an icon by name */
39
+ getIcon(name: string): string | null;
40
+ /** Refresh configuration from globals */
41
+ refresh(): void;
42
+ }
43
+
44
+ declare const FrontFriend: FrontFriend;
45
+ export default FrontFriend;`
46
+ },
47
+ {
48
+ path: 'next.d.ts',
49
+ content: `import { NextConfig } from 'next';
50
+
51
+ /**
52
+ * Wraps Next.js configuration to inject FrontFriend globals
53
+ * @param config - Next.js configuration object
54
+ * @returns Modified Next.js configuration
55
+ */
56
+ export default function frontfriend(config?: NextConfig): NextConfig;`
57
+ },
58
+ {
59
+ path: 'vite.d.ts',
60
+ content: `import { Plugin } from 'vite';
61
+
62
+ /**
63
+ * Vite plugin for FrontFriend globals injection
64
+ * @returns Vite plugin configuration
65
+ */
66
+ export default function frontfriend(): Plugin;`
67
+ },
68
+ {
69
+ path: 'ffdc.d.ts',
70
+ content: `/**
71
+ * FrontFriend Design Config processor
72
+ * @param config - Design configuration object
73
+ * @returns Processed configuration
74
+ */
75
+ export function ffdc(config: Record<string, any> | null | undefined): Record<string, any>;`
76
+ },
77
+ {
78
+ path: 'lib/react/utils.d.ts',
79
+ content: `/**
80
+ * Combines class names with tailwind-merge
81
+ * @param inputs - Class names to combine
82
+ * @returns Merged class string
83
+ */
84
+ export function cn(...inputs: any[]): string;
85
+
86
+ /**
87
+ * React hook for media query
88
+ * @param query - Media query string
89
+ * @returns Boolean indicating if query matches
90
+ */
91
+ export function useMediaQuery(query: string): boolean;
92
+
93
+ /**
94
+ * React hook for responsive breakpoints
95
+ * @returns Object with breakpoint states and active breakpoint
96
+ */
97
+ export function useBreakpoints(): {
98
+ isXs: boolean;
99
+ isSm: boolean;
100
+ isMd: boolean;
101
+ isLg: boolean;
102
+ active: 'xs' | 'sm' | 'md' | 'lg';
103
+ };`
104
+ },
105
+ {
106
+ path: 'lib/vue/utils.d.ts',
107
+ content: `import type { Ref, ComputedRef } from 'vue';
108
+
109
+ /**
110
+ * Combines class names with tailwind-merge
111
+ * @param inputs - Class names to combine
112
+ * @returns Merged class string
113
+ */
114
+ export function cn(...inputs: any[]): string;
115
+
116
+ /**
117
+ * Vue composition API hook for media query
118
+ * @param query - Media query string
119
+ * @returns Ref<boolean> indicating if query matches
120
+ */
121
+ export function useMediaQuery(query: string): Ref<boolean>;
122
+
123
+ /**
124
+ * Vue composition API hook for responsive breakpoints
125
+ * @returns Object with breakpoint states and active breakpoint
126
+ */
127
+ export function useBreakpoints(): {
128
+ isXs: Ref<boolean>;
129
+ isSm: Ref<boolean>;
130
+ isMd: Ref<boolean>;
131
+ isLg: Ref<boolean>;
132
+ active: ComputedRef<'xs' | 'sm' | 'md' | 'lg'>;
133
+ };`
134
+ }
135
+ ];
136
+
137
+ // Write individual type declaration files
138
+ typeDeclarations.forEach(({ path: filePath, content }) => {
139
+ const fullPath = path.join(distDir, filePath);
140
+ const dir = path.dirname(fullPath);
141
+ fs.mkdirSync(dir, { recursive: true });
142
+ fs.writeFileSync(fullPath, content);
143
+ });
144
+
145
+ // Files to bundle (exclude dynamic loaders)
146
+ const filesToBundle = [
147
+ // Core utilities (bundle these)
148
+ { input: 'lib/core/api-client.js', output: 'lib/core/api-client.js' },
149
+ { input: 'lib/core/token-processor.js', output: 'lib/core/token-processor.js' },
150
+ { input: 'lib/core/errors.js', output: 'lib/core/errors.js' },
151
+ { input: 'lib/core/constants.js', output: 'lib/core/constants.js' },
152
+ { input: 'lib/core/component-downloader.js', output: 'lib/core/component-downloader.js' },
153
+
154
+ // React/Vue utilities
155
+ { input: 'lib/react/utils.mjs', output: 'lib/react/utils.mjs', format: 'esm' },
156
+ { input: 'lib/vue/utils.mjs', output: 'lib/vue/utils.mjs', format: 'esm' },
157
+
158
+ // Main files
159
+ { input: 'ffdc.js', output: 'ffdc.js' },
160
+ { input: 'runtime.js', output: 'runtime.js' },
161
+ // CLI will be handled separately to preserve shebang
162
+ { input: 'next.js', output: 'next.js' },
163
+ { input: 'vite.js', output: 'vite.js' },
164
+ { input: 'vite.mjs', output: 'vite.mjs', format: 'esm' },
165
+ ];
166
+
167
+ // Files to copy without bundling (dynamic loaders)
168
+ const filesToCopy = [
169
+ { input: 'lib/core/config-loader.js', output: 'lib/config/config-loader.js' },
170
+ { input: 'lib/core/cache-manager.js', output: 'lib/config/cache-manager.js' },
171
+ { input: 'scripts/generate-icon-types.js', output: 'scripts/generate-icon-types.js' },
172
+ ];
173
+
174
+ // Build each file
175
+ async function build() {
176
+ console.log('Building frontfriend-tailwind...');
177
+
178
+ // Bundle files
179
+ for (const file of filesToBundle) {
180
+ try {
181
+ console.log(`Bundling ${file.input}...`);
182
+
183
+ await esbuild.build({
184
+ entryPoints: [path.join(__dirname, '..', file.input)],
185
+ bundle: true, // Bundle but keep external dependencies external
186
+ minify: true,
187
+ sourcemap: true,
188
+ format: file.format || 'cjs',
189
+ platform: 'node',
190
+ target: 'node14',
191
+ outfile: path.join(distDir, file.output),
192
+ external: [
193
+ 'tailwindcss',
194
+ 'commander',
195
+ 'dotenv',
196
+ 'color',
197
+ 'clsx',
198
+ 'tailwind-merge',
199
+ 'fs',
200
+ 'path',
201
+ 'crypto',
202
+ 'child_process',
203
+ 'url',
204
+ 'http',
205
+ 'https',
206
+ '../config/*',
207
+ './lib/config/*',
208
+ '@frontfriend/tailwind',
209
+ '@radix-ui/*',
210
+ 'react',
211
+ 'vue',
212
+ 'class-variance-authority',
213
+ 'lucide-react'
214
+ ],
215
+ });
216
+ } catch (error) {
217
+ console.error(`Error building ${file.input}:`, error);
218
+ process.exit(1);
219
+ }
220
+ }
221
+
222
+ // Copy dynamic loader files
223
+ for (const file of filesToCopy) {
224
+ console.log(`Copying ${file.input}...`);
225
+ let content = fs.readFileSync(path.join(__dirname, '..', file.input), 'utf8');
226
+
227
+ // Update relative imports to point to correct locations
228
+ if (file.input.includes('cache-manager.js')) {
229
+ content = content
230
+ .replace("require('./constants')", "require('../core/constants')")
231
+ .replace("require('./errors')", "require('../core/errors')");
232
+ }
233
+
234
+ if (file.input.includes('config-loader.js')) {
235
+ // No changes needed for config-loader
236
+ }
237
+
238
+ fs.writeFileSync(path.join(distDir, file.output), content);
239
+ }
240
+
241
+ // Special handling for CLI to preserve shebang
242
+ console.log('Building cli.js with shebang...');
243
+ const cliContent = fs.readFileSync(path.join(__dirname, '..', 'cli.js'), 'utf8');
244
+ const cliLines = cliContent.split('\n');
245
+ const shebang = cliLines[0];
246
+ const cliCodeContent = cliLines.slice(1).join('\n');
247
+
248
+ // Update require paths in CLI
249
+ const updatedCliContent = cliCodeContent
250
+ .replace("require('./lib/core/config-loader')", "require('./lib/config/config-loader')")
251
+ .replace("require('./lib/core/cache-manager')", "require('./lib/config/cache-manager')");
252
+
253
+ // Build CLI without shebang
254
+ await esbuild.build({
255
+ stdin: {
256
+ contents: updatedCliContent,
257
+ resolveDir: path.join(__dirname, '..'),
258
+ sourcefile: 'cli.js',
259
+ },
260
+ bundle: true,
261
+ minify: true,
262
+ sourcemap: true,
263
+ format: 'cjs',
264
+ platform: 'node',
265
+ target: 'node14',
266
+ outfile: path.join(distDir, 'cli-temp.js'),
267
+ external: [
268
+ 'commander',
269
+ 'dotenv',
270
+ 'fs',
271
+ 'path',
272
+ 'child_process',
273
+ './lib/config/config-loader',
274
+ './lib/config/cache-manager',
275
+ './lib/core/api-client',
276
+ './lib/core/component-downloader',
277
+ './lib/core/token-processor',
278
+ './lib/core/errors'
279
+ ],
280
+ });
281
+
282
+ // Add shebang back and make executable
283
+ const builtCliContent = fs.readFileSync(path.join(distDir, 'cli-temp.js'), 'utf8');
284
+ fs.writeFileSync(path.join(distDir, 'cli.js'), shebang + '\n' + builtCliContent);
285
+ fs.unlinkSync(path.join(distDir, 'cli-temp.js'));
286
+ // Remove the temp map file if it exists
287
+ if (fs.existsSync(path.join(distDir, 'cli-temp.js.map'))) {
288
+ fs.unlinkSync(path.join(distDir, 'cli-temp.js.map'));
289
+ }
290
+ fs.chmodSync(path.join(distDir, 'cli.js'), '755');
291
+
292
+ // Special handling for index.js and index.mjs (need to update require paths)
293
+ console.log('Building index.js with updated paths...');
294
+
295
+ // Read original index.js
296
+ let indexContent = fs.readFileSync(path.join(__dirname, '..', 'index.js'), 'utf8');
297
+
298
+ // Update require paths for cache-manager
299
+ indexContent = indexContent.replace(
300
+ "require('./lib/core/cache-manager')",
301
+ "require('./lib/config/cache-manager')"
302
+ );
303
+
304
+ // Build CommonJS version
305
+ await esbuild.build({
306
+ stdin: {
307
+ contents: indexContent,
308
+ resolveDir: path.join(__dirname, '..'),
309
+ sourcefile: 'index.js',
310
+ },
311
+ bundle: true,
312
+ minify: true,
313
+ sourcemap: true,
314
+ format: 'cjs',
315
+ platform: 'node',
316
+ target: 'node14',
317
+ outfile: path.join(distDir, 'index.js'),
318
+ external: [
319
+ 'tailwindcss',
320
+ './lib/config/cache-manager',
321
+ './ffdc',
322
+ 'fs',
323
+ 'path'
324
+ ],
325
+ });
326
+
327
+ // Build ESM version
328
+ console.log('Building index.mjs...');
329
+ let indexMjsContent = fs.readFileSync(path.join(__dirname, '..', 'index.mjs'), 'utf8');
330
+
331
+ // Update import paths
332
+ indexMjsContent = indexMjsContent.replace(
333
+ "from './lib/core/cache-manager'",
334
+ "from './lib/config/cache-manager.js'"
335
+ );
336
+
337
+ await esbuild.build({
338
+ stdin: {
339
+ contents: indexMjsContent,
340
+ resolveDir: path.join(__dirname, '..'),
341
+ sourcefile: 'index.mjs',
342
+ },
343
+ bundle: true,
344
+ minify: true,
345
+ sourcemap: true,
346
+ format: 'esm',
347
+ platform: 'node',
348
+ target: 'node14',
349
+ outfile: path.join(distDir, 'index.mjs'),
350
+ external: [
351
+ 'tailwindcss',
352
+ './lib/config/cache-manager.js',
353
+ './ffdc.js',
354
+ 'fs',
355
+ 'path'
356
+ ],
357
+ });
358
+
359
+ console.log('Build completed successfully!');
360
+
361
+ // Show dist structure
362
+ console.log('\nDist structure:');
363
+ const files = await glob('**/*', { cwd: distDir });
364
+ files.forEach(file => console.log(` dist/${file}`));
365
+ }
366
+
367
+ // Run build
368
+ build().catch(error => {
369
+ console.error('Build failed:', error);
370
+ process.exit(1);
371
+ });
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ /**
7
+ * Generate TypeScript types from icons.json cache file
8
+ * This script reads the icons.json file and generates proper types for the iconSet
9
+ */
10
+
11
+ function generateIconTypes() {
12
+ const projectRoot = process.cwd();
13
+ const iconsPath = path.join(projectRoot, 'node_modules', '.cache', 'frontfriend', 'icons.json');
14
+ const outputPath = path.join(projectRoot, 'types', 'frontfriend-icons.d.ts');
15
+
16
+ // Check if icons.json exists
17
+ if (!fs.existsSync(iconsPath)) {
18
+ console.error('[FrontFriend] Error: icons.json not found. Please run "npx frontfriend setup" first.');
19
+ process.exit(1);
20
+ }
21
+
22
+ try {
23
+ // Read icons.json
24
+ const icons = JSON.parse(fs.readFileSync(iconsPath, 'utf-8'));
25
+
26
+ // Extract all unique icon names
27
+ const iconNames = new Set();
28
+
29
+ function extractIconNames(obj) {
30
+ for (const value of Object.values(obj)) {
31
+ if (typeof value === 'string') {
32
+ iconNames.add(value);
33
+ } else if (typeof value === 'object' && value !== null) {
34
+ extractIconNames(value);
35
+ }
36
+ }
37
+ }
38
+
39
+ extractIconNames(icons);
40
+
41
+ // Generate the interface structure recursively
42
+ function generateInterface(obj, indent = ' ') {
43
+ const lines = [];
44
+
45
+ for (const [key, value] of Object.entries(obj)) {
46
+ if (typeof value === 'string') {
47
+ lines.push(`${indent}${key}: '${value}';`);
48
+ } else if (typeof value === 'object' && value !== null) {
49
+ lines.push(`${indent}${key}: {`);
50
+ lines.push(generateInterface(value, indent + ' '));
51
+ lines.push(`${indent}};`);
52
+ }
53
+ }
54
+
55
+ return lines.join('\n');
56
+ }
57
+
58
+ // Generate the type definition file
59
+ const iconNameType = Array.from(iconNames).map(name => `'${name}'`).join(' | ');
60
+
61
+ const typeDefinition = `// Generated by @frontfriend/tailwind
62
+ // DO NOT EDIT THIS FILE MANUALLY
63
+
64
+ import type { IconSetStructure } from '@frontfriend/tailwind';
65
+
66
+ declare module '@frontfriend/tailwind' {
67
+ // Icon names used in this project
68
+ export type ProjectIconName = ${iconNameType || 'string'};
69
+
70
+ // Typed iconSet structure with exact icon names
71
+ export interface TypedIconSet extends IconSetStructure {
72
+ ${generateInterface(icons)}
73
+ }
74
+
75
+ // Override the iconSet type with the typed version
76
+ export const iconSet: TypedIconSet;
77
+ }
78
+
79
+ // Augment Vue component types with exact icon names
80
+ declare module '*/components/ui/icon/*.vue' {
81
+ export type IconName = ${iconNameType || 'string'};
82
+ }
83
+
84
+ declare module '@/components/ui/icon/*.vue' {
85
+ export type IconName = ${iconNameType || 'string'};
86
+ }
87
+
88
+ declare module '@/components/ui/icon' {
89
+ export type IconName = ${iconNameType || 'string'};
90
+ }
91
+
92
+ declare module '@/src/components/ui/icon' {
93
+ export type IconName = ${iconNameType || 'string'};
94
+ }
95
+
96
+ declare module '@/src/components/ui/icon/Icon.vue' {
97
+ export type IconName = ${iconNameType || 'string'};
98
+ }
99
+ `;
100
+
101
+ // Ensure types directory exists
102
+ const typesDir = path.dirname(outputPath);
103
+ if (!fs.existsSync(typesDir)) {
104
+ fs.mkdirSync(typesDir, { recursive: true });
105
+ }
106
+
107
+ // Write the type definition file
108
+ fs.writeFileSync(outputPath, typeDefinition);
109
+
110
+ console.log('[FrontFriend] ✓ Generated icon types at types/frontfriend-icons.d.ts');
111
+
112
+ // Check if tsconfig.json exists and update it
113
+ const tsconfigPath = path.join(projectRoot, 'tsconfig.json');
114
+ if (fs.existsSync(tsconfigPath)) {
115
+ const tsconfig = JSON.parse(fs.readFileSync(tsconfigPath, 'utf-8'));
116
+
117
+ // Ensure the types file is included
118
+ if (!tsconfig.include) {
119
+ tsconfig.include = [];
120
+ }
121
+
122
+ const typesPattern = 'types/**/*.d.ts';
123
+ if (!tsconfig.include.includes(typesPattern)) {
124
+ tsconfig.include.push(typesPattern);
125
+ fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2));
126
+ console.log('[FrontFriend] ✓ Updated tsconfig.json to include generated types');
127
+ }
128
+ }
129
+
130
+ } catch (error) {
131
+ console.error('[FrontFriend] Error generating icon types:', error.message);
132
+ process.exit(1);
133
+ }
134
+ }
135
+
136
+ // Run if called directly
137
+ if (require.main === module) {
138
+ generateIconTypes();
139
+ }
140
+
141
+ module.exports = { generateIconTypes };