@frontfriend/tailwind 3.0.4 → 4.0.0
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 +94 -1
- package/dist/browser.mjs +2 -0
- package/dist/browser.mjs.map +7 -0
- package/dist/cli.js +63 -41
- package/dist/components-config-schema.d.ts +2 -0
- package/dist/components-config-schema.js +2 -0
- package/dist/components-config-schema.js.map +7 -0
- package/dist/ffdc.d.ts +3 -1
- package/dist/ffdc.js +1 -1
- package/dist/ffdc.js.map +4 -4
- package/dist/index.js +3 -3
- package/dist/index.js.map +4 -4
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +3 -3
- package/dist/lib/core/api-client.js +2 -1
- package/dist/lib/core/api-client.js.map +4 -4
- package/dist/lib/core/cache-manager.js +11 -2
- package/dist/lib/core/component-downloader.js +3 -2
- package/dist/lib/core/component-downloader.js.map +4 -4
- package/dist/lib/core/components-config-schema.js +2 -0
- package/dist/lib/core/components-config-schema.js.map +7 -0
- package/dist/lib/core/constants.js +1 -1
- package/dist/lib/core/constants.js.map +2 -2
- package/dist/lib/core/credentials.js +3 -0
- package/dist/lib/core/credentials.js.map +7 -0
- package/dist/lib/core/default-config-data.js +2 -0
- package/dist/lib/core/default-config-data.js.map +7 -0
- package/dist/lib/core/default-config.js +2 -0
- package/dist/lib/core/default-config.js.map +7 -0
- package/dist/lib/core/env-utils.js +1 -1
- package/dist/lib/core/env-utils.js.map +3 -3
- package/dist/lib/core/file-utils.js +1 -1
- package/dist/lib/core/file-utils.js.map +3 -3
- package/dist/lib/core/local-token-reader.js +1 -1
- package/dist/lib/core/local-token-reader.js.map +3 -3
- package/dist/lib/core/path-utils.js +1 -1
- package/dist/lib/core/path-utils.js.map +3 -3
- package/dist/lib/core/token-processor.js +3 -1
- package/dist/lib/core/token-processor.js.map +3 -3
- package/dist/next.js +1 -1
- package/dist/next.js.map +4 -4
- package/dist/types/index.d.ts +107 -11
- package/dist/vite.js +8 -8
- package/dist/vite.js.map +4 -4
- package/dist/vite.mjs +1 -1
- package/dist/vite.mjs.map +3 -3
- package/package.json +15 -5
- package/scripts/build.js +21 -4
- package/scripts/update-default-config-data.js +589 -0
- package/src/theme.css +13 -0
package/README.md
CHANGED
|
@@ -51,6 +51,37 @@ module.exports = {
|
|
|
51
51
|
}
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
### Tailwind v4 CSS-first setup
|
|
55
|
+
|
|
56
|
+
For Tailwind v4 projects, run `init` as usual and import the generated FrontFriend CSS theme from your app CSS:
|
|
57
|
+
|
|
58
|
+
```css
|
|
59
|
+
@import "tailwindcss";
|
|
60
|
+
@import "@frontfriend/tailwind/theme.css";
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
FrontFriend keeps readable semantic utility classes such as `bg-brand-mid`, `text-onbrand-strong`, and `border-neutral-subtle`.
|
|
64
|
+
The generated cache theme provides Tailwind v4 `@theme`, `@utility`, and `@source` output so registry components do not need arbitrary CSS-variable class names.
|
|
65
|
+
|
|
66
|
+
Use the v4 component registry for Tailwind v4/React 19 apps:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npx frontfriend add button --registry v4
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The initial v4 React registry intentionally includes only `button, icon, and spinner`.
|
|
73
|
+
Requests for non-migrated v4 components fail explicitly instead of falling back to legacy components.
|
|
74
|
+
|
|
75
|
+
#### Installable with the shadcn CLI
|
|
76
|
+
|
|
77
|
+
v4 items are shadcn-protocol-compliant and can be added directly with the shadcn CLI:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npx shadcn@latest add https://registry.frontfriend.dev/r/v4/react/button.json
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Each v4 item declares `@frontfriend/tailwind` in its `dependencies` so the shadcn CLI installs it automatically. After the first install, run `npx frontfriend init` once and import `@frontfriend/tailwind/theme.css` from your app CSS to get the generated v4 theme.
|
|
84
|
+
|
|
54
85
|
## Framework Integration
|
|
55
86
|
|
|
56
87
|
### Vite Plugin
|
|
@@ -128,12 +159,33 @@ npx frontfriend download <components...> [options]
|
|
|
128
159
|
Options:
|
|
129
160
|
-f, --framework <framework> Framework to download for (react/vue)
|
|
130
161
|
-o, --output <path> Output directory for components
|
|
162
|
+
--registry <generation> Registry generation (legacy/v4)
|
|
131
163
|
--overwrite Overwrite existing files
|
|
132
164
|
|
|
133
165
|
Examples:
|
|
134
166
|
npx frontfriend download button card # Download specific components
|
|
135
167
|
npx frontfriend download all -f react # Download all React components
|
|
136
168
|
npx frontfriend download all -f vue -o ./src # Download all Vue components to src/
|
|
169
|
+
npx frontfriend download button --registry v4 # Download from the Tailwind v4 registry
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Add Components or Generated Pages
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
npx frontfriend add <component...> [options]
|
|
176
|
+
npx frontfriend add page <id> [options]
|
|
177
|
+
npx frontfriend add project <id> [options]
|
|
178
|
+
|
|
179
|
+
Options:
|
|
180
|
+
-f, --framework <framework> Framework for component installs (react/vue)
|
|
181
|
+
-o, --output <path> Output directory for components
|
|
182
|
+
--registry <generation> Registry generation (legacy/v4)
|
|
183
|
+
--overwrite Overwrite existing component files
|
|
184
|
+
|
|
185
|
+
Examples:
|
|
186
|
+
npx frontfriend add button --registry v4
|
|
187
|
+
npx frontfriend add button icon --registry v4 --output components/ui
|
|
188
|
+
npx frontfriend add page page-id --registry v4
|
|
137
189
|
```
|
|
138
190
|
|
|
139
191
|
### Clean Cache
|
|
@@ -184,6 +236,47 @@ export default {
|
|
|
184
236
|
};
|
|
185
237
|
```
|
|
186
238
|
|
|
239
|
+
Select a registry generation in config when you do not want to pass `--registry` on every command:
|
|
240
|
+
|
|
241
|
+
```js
|
|
242
|
+
module.exports = {
|
|
243
|
+
'ff-id': 'your-frontfriend-id',
|
|
244
|
+
registry: 'v4'
|
|
245
|
+
};
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Registry selection precedence is:
|
|
249
|
+
|
|
250
|
+
```txt
|
|
251
|
+
CLI flag > frontfriend.config.js > auto-detect Tailwind version > legacy fallback
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
React compatibility is enforced for registry installs:
|
|
255
|
+
|
|
256
|
+
- `legacy` targets React 18-era components.
|
|
257
|
+
- `v4` targets Tailwind v4 and React 19-era React components.
|
|
258
|
+
|
|
259
|
+
### shadcn-compatible registry item support
|
|
260
|
+
|
|
261
|
+
The FrontFriend downloader can also consume shadcn-compatible registry items
|
|
262
|
+
emitted by the FrontFriend `/r/...` routes.
|
|
263
|
+
|
|
264
|
+
- file `target` aliases are resolved before writing files:
|
|
265
|
+
- `@ui/...` writes under the configured component output directory;
|
|
266
|
+
- `@lib/...`, `@hooks/...`, and `@components/...` use matching `frontfriend.config.js`
|
|
267
|
+
aliases when present, otherwise conventional app directories like `lib/` and `hooks/`;
|
|
268
|
+
- `~/...` writes relative to the app root.
|
|
269
|
+
- `registryDependencies` may be plain component names or shadcn-compatible
|
|
270
|
+
FrontFriend URLs such as `https://.../r/v4/react/icon.json`; URL dependencies
|
|
271
|
+
are normalized back to component names and still use FrontFriend same-generation
|
|
272
|
+
custom-first resolution. Only FrontFriend `/r/{generation}/{framework}/{component}.json`
|
|
273
|
+
routes matching the selected registry generation and framework are accepted;
|
|
274
|
+
custom dependency URLs must also match the active `ff-id`. External URLs,
|
|
275
|
+
cross-generation URLs, or cross-client custom URLs fail explicitly instead of
|
|
276
|
+
being reinterpreted as local FrontFriend components.
|
|
277
|
+
- registry file paths are validated before writing so a malicious `../` target
|
|
278
|
+
cannot escape the selected alias root.
|
|
279
|
+
|
|
187
280
|
## TypeScript
|
|
188
281
|
|
|
189
282
|
Full TypeScript support is included:
|
|
@@ -228,4 +321,4 @@ module.exports = {
|
|
|
228
321
|
This is proprietary software. Usage is restricted to licensed FrontFriend clients only.
|
|
229
322
|
See LICENSE file for details.
|
|
230
323
|
|
|
231
|
-
© 2025 FrontFriend. All rights reserved.
|
|
324
|
+
© 2025 FrontFriend. All rights reserved.
|
package/dist/browser.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var o=typeof window<"u"&&typeof window.document<"u";function i(t=""){let e=new Map;return new Proxy({},{get(r,n){return n===Symbol.toPrimitive||n==="valueOf"?()=>t:n==="toString"||n===Symbol.toStringTag?()=>t:n==="constructor"?Object:(e.has(n)||e.set(n,i(t)),e.get(n))},has(){return!0},ownKeys(){return[]},getOwnPropertyDescriptor(){return{enumerable:!0,configurable:!0}}})}function u(t){return t==="__FF_CONFIG__"&&typeof __FF_CONFIG__<"u"?__FF_CONFIG__:t==="__FF_ICONS__"&&typeof __FF_ICONS__<"u"?__FF_ICONS__:typeof globalThis<"u"&&globalThis[t]?globalThis[t]:o&&window[t]?window[t]:null}function _(t){return!t||typeof t!="object"?i():t}var c=new Proxy({},{get(t,e){if(e===Symbol.toPrimitive||e==="valueOf")return()=>"";if(e==="toString"||e===Symbol.toStringTag)return()=>"[object Object]";if(e==="constructor")return Object;let r=u("__FF_CONFIG__");return r&&e in r?r[e]:i()},has(){return!0},ownKeys(){return[]},getOwnPropertyDescriptor(){return{enumerable:!0,configurable:!0}}}),s=new Proxy({},{get(t,e){if(e===Symbol.toPrimitive||e==="valueOf")return()=>"";if(e==="toString"||e===Symbol.toStringTag)return()=>"[object Object]";if(e==="constructor")return Object;let r=u("__FF_ICONS__");return r&&e in r?r[e]:i()},has(){return!0},ownKeys(){return[]},getOwnPropertyDescriptor(){return{enumerable:!0,configurable:!0}}}),g={};function y(t){return typeof t=="string"&&t.trim().length>0}function f(){return{}}export{g as componentsConfigSchema,c as config,f as default,_ as ffdc,s as iconSet,y as isCSSClassString};
|
|
2
|
+
//# sourceMappingURL=browser.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../browser.mjs"],
|
|
4
|
+
"sourcesContent": ["// Browser-safe FrontFriend runtime used by Vite-built component consumers.\n\nconst isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';\n\nfunction createDeepProxy(fallbackValue = '') {\n const cache = new Map();\n\n return new Proxy({}, {\n get(target, prop) {\n if (prop === Symbol.toPrimitive || prop === 'valueOf') {\n return () => fallbackValue;\n }\n if (prop === 'toString' || prop === Symbol.toStringTag) {\n return () => fallbackValue;\n }\n if (prop === 'constructor') {\n return Object;\n }\n if (!cache.has(prop)) {\n cache.set(prop, createDeepProxy(fallbackValue));\n }\n return cache.get(prop);\n },\n has() {\n return true;\n },\n ownKeys() {\n return [];\n },\n getOwnPropertyDescriptor() {\n return { enumerable: true, configurable: true };\n },\n });\n}\n\nfunction readGlobal(name) {\n if (name === '__FF_CONFIG__' && typeof __FF_CONFIG__ !== 'undefined') {\n return __FF_CONFIG__;\n }\n if (name === '__FF_ICONS__' && typeof __FF_ICONS__ !== 'undefined') {\n return __FF_ICONS__;\n }\n if (typeof globalThis !== 'undefined' && globalThis[name]) {\n return globalThis[name];\n }\n if (isBrowser && window[name]) {\n return window[name];\n }\n return null;\n}\n\nexport function ffdc(config) {\n if (!config || typeof config !== 'object') {\n return createDeepProxy();\n }\n return config;\n}\n\nexport const config = new Proxy({}, {\n get(target, prop) {\n if (prop === Symbol.toPrimitive || prop === 'valueOf') return () => '';\n if (prop === 'toString' || prop === Symbol.toStringTag) return () => '[object Object]';\n if (prop === 'constructor') return Object;\n\n const currentConfig = readGlobal('__FF_CONFIG__');\n if (currentConfig && prop in currentConfig) {\n return currentConfig[prop];\n }\n return createDeepProxy();\n },\n has() {\n return true;\n },\n ownKeys() {\n return [];\n },\n getOwnPropertyDescriptor() {\n return { enumerable: true, configurable: true };\n },\n});\n\nexport const iconSet = new Proxy({}, {\n get(target, prop) {\n if (prop === Symbol.toPrimitive || prop === 'valueOf') return () => '';\n if (prop === 'toString' || prop === Symbol.toStringTag) return () => '[object Object]';\n if (prop === 'constructor') return Object;\n\n const icons = readGlobal('__FF_ICONS__');\n if (icons && prop in icons) {\n return icons[prop];\n }\n return createDeepProxy();\n },\n has() {\n return true;\n },\n ownKeys() {\n return [];\n },\n getOwnPropertyDescriptor() {\n return { enumerable: true, configurable: true };\n },\n});\n\nexport const componentsConfigSchema = {};\n\nexport function isCSSClassString(value) {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nexport default function frontfriend() {\n return {};\n}\n"],
|
|
5
|
+
"mappings": "AAEA,IAAMA,EAAY,OAAO,OAAW,KAAe,OAAO,OAAO,SAAa,IAE9E,SAASC,EAAgBC,EAAgB,GAAI,CAC3C,IAAMC,EAAQ,IAAI,IAElB,OAAO,IAAI,MAAM,CAAC,EAAG,CACnB,IAAIC,EAAQC,EAAM,CAChB,OAAIA,IAAS,OAAO,aAAeA,IAAS,UACnC,IAAMH,EAEXG,IAAS,YAAcA,IAAS,OAAO,YAClC,IAAMH,EAEXG,IAAS,cACJ,QAEJF,EAAM,IAAIE,CAAI,GACjBF,EAAM,IAAIE,EAAMJ,EAAgBC,CAAa,CAAC,EAEzCC,EAAM,IAAIE,CAAI,EACvB,EACA,KAAM,CACJ,MAAO,EACT,EACA,SAAU,CACR,MAAO,CAAC,CACV,EACA,0BAA2B,CACzB,MAAO,CAAE,WAAY,GAAM,aAAc,EAAK,CAChD,CACF,CAAC,CACH,CAEA,SAASC,EAAWC,EAAM,CACxB,OAAIA,IAAS,iBAAmB,OAAO,cAAkB,IAChD,cAELA,IAAS,gBAAkB,OAAO,aAAiB,IAC9C,aAEL,OAAO,WAAe,KAAe,WAAWA,CAAI,EAC/C,WAAWA,CAAI,EAEpBP,GAAa,OAAOO,CAAI,EACnB,OAAOA,CAAI,EAEb,IACT,CAEO,SAASC,EAAKC,EAAQ,CAC3B,MAAI,CAACA,GAAU,OAAOA,GAAW,SACxBR,EAAgB,EAElBQ,CACT,CAEO,IAAMA,EAAS,IAAI,MAAM,CAAC,EAAG,CAClC,IAAIL,EAAQC,EAAM,CAChB,GAAIA,IAAS,OAAO,aAAeA,IAAS,UAAW,MAAO,IAAM,GACpE,GAAIA,IAAS,YAAcA,IAAS,OAAO,YAAa,MAAO,IAAM,kBACrE,GAAIA,IAAS,cAAe,OAAO,OAEnC,IAAMK,EAAgBJ,EAAW,eAAe,EAChD,OAAII,GAAiBL,KAAQK,EACpBA,EAAcL,CAAI,EAEpBJ,EAAgB,CACzB,EACA,KAAM,CACJ,MAAO,EACT,EACA,SAAU,CACR,MAAO,CAAC,CACV,EACA,0BAA2B,CACzB,MAAO,CAAE,WAAY,GAAM,aAAc,EAAK,CAChD,CACF,CAAC,EAEYU,EAAU,IAAI,MAAM,CAAC,EAAG,CACnC,IAAIP,EAAQC,EAAM,CAChB,GAAIA,IAAS,OAAO,aAAeA,IAAS,UAAW,MAAO,IAAM,GACpE,GAAIA,IAAS,YAAcA,IAAS,OAAO,YAAa,MAAO,IAAM,kBACrE,GAAIA,IAAS,cAAe,OAAO,OAEnC,IAAMO,EAAQN,EAAW,cAAc,EACvC,OAAIM,GAASP,KAAQO,EACZA,EAAMP,CAAI,EAEZJ,EAAgB,CACzB,EACA,KAAM,CACJ,MAAO,EACT,EACA,SAAU,CACR,MAAO,CAAC,CACV,EACA,0BAA2B,CACzB,MAAO,CAAE,WAAY,GAAM,aAAc,EAAK,CAChD,CACF,CAAC,EAEYY,EAAyB,CAAC,EAEhC,SAASC,EAAiBC,EAAO,CACtC,OAAO,OAAOA,GAAU,UAAYA,EAAM,KAAK,EAAE,OAAS,CAC5D,CAEe,SAARC,GAA+B,CACpC,MAAO,CAAC,CACV",
|
|
6
|
+
"names": ["isBrowser", "createDeepProxy", "fallbackValue", "cache", "target", "prop", "readGlobal", "name", "ffdc", "config", "currentConfig", "iconSet", "icons", "componentsConfigSchema", "isCSSClassString", "value", "frontfriend"]
|
|
7
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -1,72 +1,94 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
|
|
2
|
+
var G=(e,n)=>()=>(n||e((n={exports:{}}).exports,n),n.exports);var Q=G((Ze,Z)=>{var me="https://app.frontfriend.dev",he="https://registry-legacy.frontfriend.dev",ye=".cache/frontfriend",we={JS:["tokens.js","variables.js","semanticVariables.js","semanticDarkVariables.js","safelist.js","custom.js"],JSON:["fonts.json","icons.json","components-config.json","version.json","metadata.json"]},ve={FF_ID:"FF_ID",FF_API_URL:"FF_API_URL",FF_USE_LOCAL:"FF_USE_LOCAL",FF_AUTH_TOKEN:"FF_AUTH_TOKEN"};Z.exports={DEFAULT_API_URL:me,LEGACY_API_URL:he,CACHE_DIR_NAME:ye,CACHE_TTL_DAYS:7,CACHE_TTL_MS:6048e5,CACHE_FILES:we,ENV_VARS:ve}});var ae=G((Qe,ce)=>{var O=require("fs"),X=require("path"),Ce=require("os"),Se=require("https"),xe=require("http"),{URL:$e}=require("url"),{DEFAULT_API_URL:ee}=Q(),oe=X.join(Ce.homedir(),".frontfriend"),R=X.join(oe,"credentials");function ke(){return R}function ne(){try{return O.existsSync(R)?JSON.parse(O.readFileSync(R,"utf8")):null}catch{return null}}function te(e){O.mkdirSync(oe,{recursive:!0,mode:448}),O.writeFileSync(R,`${JSON.stringify(e,null,2)}
|
|
3
|
+
`,{mode:384});try{O.chmodSync(R,384)}catch{}}function Fe(){return O.existsSync(R)?(O.unlinkSync(R),!0):!1}function se(e,n=60*1e3){return!(e!=null&&e.accessToken)||!e.expiresAt||Date.now()+n>=e.expiresAt}function re(e,n,o){return new Promise((t,r)=>{let i=new $e(e),g=i.protocol==="https:"?Se:xe,d=JSON.stringify(n||{}),f=g.request({hostname:i.hostname,port:i.port,path:`${i.pathname}${i.search}`,method:"POST",headers:{"content-type":"application/json","content-length":Buffer.byteLength(d),...o?{authorization:`Bearer ${o}`}:{}}},l=>{let s="";l.on("data",a=>{s+=a}),l.on("end",()=>{let a=null;try{a=s?JSON.parse(s):null}catch($){r($);return}if(l.statusCode>=400){let $=new Error((a==null?void 0:a.error_description)||(a==null?void 0:a.error)||`HTTP ${l.statusCode}`);$.statusCode=l.statusCode,$.body=a,r($);return}t(a)})});f.on("error",r),f.write(d),f.end()})}async function ie(e,n=ee){if(!(e!=null&&e.refreshToken)||!(e!=null&&e.deviceId))return e;let o=await re(`${n}/api/plugin/token`,{grant_type:"refresh_token",refresh_token:e.refreshToken,device_id:e.deviceId}),t={accessToken:o.access_token,refreshToken:o.refresh_token,deviceId:o.device_id||e.deviceId,expiresAt:Date.now()+(o.expires_in||900)*1e3,refreshExpiresAt:Date.now()+(o.refresh_expires_in||604800)*1e3,ffApiUrl:n};return te(t),t}async function je(e={}){if(!e.skipCredentials){let n=ne();if(n!=null&&n.accessToken){let o=e.baseURL||n.ffApiUrl||ee;if(se(n))try{let t=await ie(n,o);return(t==null?void 0:t.accessToken)||null}catch{return null}return n.accessToken}}return e.envToken?e.envToken:e.legacyToken?(e.silent||console.warn("\u26A0\uFE0F auth-token in frontfriend.config.js is deprecated. Run `frontfriend login` to store credentials outside the repo."),e.legacyToken):null}ce.exports={getCredentialsPath:ke,readCredentials:ne,writeCredentials:te,deleteCredentials:Fe,isExpired:se,postJson:re,refreshCredentials:ie,resolveAuthToken:je}});var B=G((Xe,H)=>{var L=require("fs"),q=require("path");function le(e={}){let n=e.projectRoot||process.cwd(),o=q.join(n,"node_modules",".cache","frontfriend","icons.json"),t=e.outputPath||q.join(n,"types","frontfriend.d.ts");L.existsSync(o)||(console.error('[FrontFriend] Error: icons.json not found. Please run "npx frontfriend init" first.'),process.exit(1));try{let f=function(j){for(let u of Object.values(j))typeof u=="string"?d.add(u):typeof u=="object"&&u!==null&&f(u)},l=function(j,u=" "){let D=[];for(let[S,A]of Object.entries(j))typeof A=="string"?D.push(`${u}${S}: '${A}';`):typeof A=="object"&&A!==null&&(D.push(`${u}${S}: {`),D.push(l(A,u+" ")),D.push(`${u}};`));return D.join(`
|
|
4
|
+
`)};var r=f,i=l;let g=JSON.parse(L.readFileSync(o,"utf-8")),d=new Set;f(g);let s=Array.from(d).map(j=>`'${j}'`).join(" | "),a=`// Generated by @frontfriend/tailwind
|
|
4
5
|
// DO NOT EDIT THIS FILE MANUALLY
|
|
5
6
|
|
|
6
7
|
import type { IconSetStructure } from '@frontfriend/tailwind';
|
|
7
8
|
|
|
8
9
|
declare module '@frontfriend/tailwind' {
|
|
9
10
|
// Icon names used in this project
|
|
10
|
-
export type ProjectIconName = ${
|
|
11
|
+
export type ProjectIconName = ${s||"string"};
|
|
11
12
|
|
|
12
13
|
// Typed iconSet structure with exact icon names
|
|
13
14
|
export interface TypedIconSet extends IconSetStructure {
|
|
14
|
-
${
|
|
15
|
+
${l(g)}
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
// Augment Vue component types with exact icon names
|
|
19
20
|
declare module '*/components/ui/icon/*.vue' {
|
|
20
|
-
export type IconName = ${
|
|
21
|
+
export type IconName = ${s||"string"};
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
declare module '@/components/ui/icon/*.vue' {
|
|
24
|
-
export type IconName = ${
|
|
25
|
+
export type IconName = ${s||"string"};
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
declare module '@/components/ui/icon' {
|
|
28
|
-
export type IconName = ${
|
|
29
|
+
export type IconName = ${s||"string"};
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
declare module '@/src/components/ui/icon' {
|
|
32
|
-
export type IconName = ${
|
|
33
|
+
export type IconName = ${s||"string"};
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
declare module '@/src/components/ui/icon/Icon.vue' {
|
|
36
|
-
export type IconName = ${
|
|
37
|
+
export type IconName = ${s||"string"};
|
|
37
38
|
}
|
|
38
|
-
|
|
39
|
-
`);let e=
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
`,$=q.dirname(t);L.existsSync($)||L.mkdirSync($,{recursive:!0}),L.writeFileSync(t,a),console.log("[FrontFriend] \u2713 Generated types at",q.relative(n,t));let v=q.join(n,"tsconfig.json");if(L.existsSync(v)){let j=JSON.parse(L.readFileSync(v,"utf-8"));j.include||(j.include=[]);let u="types/**/*.d.ts";j.include.includes(u)||(j.include.push(u),L.writeFileSync(v,JSON.stringify(j,null,2)),console.log("[FrontFriend] \u2713 Updated tsconfig.json to include generated types"))}}catch(g){console.error("[FrontFriend] Error generating icon types:",g.message),process.exit(1)}}require.main===H&&le();H.exports={generateIconTypes:le}});var{loadEnvLocal:Te}=require("./lib/core/env-utils");Te();var{Command:Ie}=require("commander"),N=require("./lib/core/config-loader"),W=require("./lib/core/cache-manager"),{APIClient:ge}=require("./lib/core/api-client"),{getCredentialsPath:Pe,writeCredentials:be,readCredentials:_e,deleteCredentials:De,postJson:z}=ae(),{ComponentDownloader:M}=require("./lib/core/component-downloader"),Ae=require("./lib/core/token-processor"),{ConfigError:_,APIError:Ee,CacheError:Y}=require("./lib/core/errors"),y=require("path"),w=require("fs"),{spawn:Le}=require("child_process"),P=new Ie,Oe="https://registry.frontfriend.dev/r/v4",fe='@import "@frontfriend/tailwind/theme.css";';function J(e){try{return JSON.parse(w.readFileSync(e,"utf8"))}catch{return null}}function K(e,n){w.writeFileSync(e,`${JSON.stringify(n,null,2)}
|
|
40
|
+
`)}function Re(e){let n=J(y.join(e,"package.json"))||{},o={...n.dependencies||{},...n.devDependencies||{}};return o.vue||o["@vitejs/plugin-vue"]?"vue":"react"}function Ne(e){let n=J(y.join(e,"package.json"))||{},o={...n.dependencies||{},...n.devDependencies||{}};return o.next||w.existsSync(y.join(e,"next.config.js"))||w.existsSync(y.join(e,"next.config.mjs"))?"next":o.vite||w.existsSync(y.join(e,"vite.config.ts"))||w.existsSync(y.join(e,"vite.config.js"))?"vite":null}function Ue(e,n){let o=y.join(e,"components.json");if(!w.existsSync(o))return!1;let t=J(o)||{};t.registries=t.registries||{};let r=`${Oe}/${n}`;return t.registries["@frontfriend"]===r?!1:(t.registries["@frontfriend"]=r,K(o,t),!0)}function qe(e){let n=y.join(e,"package.json"),o=J(n);return o?(o.dependencies=o.dependencies||{},o.dependencies["@frontfriend/tailwind"]?!1:(o.dependencies["@frontfriend/tailwind"]="^4.0.0",K(n,o),!0)):!1}function Ve(e){let n=y.join(e,"next.config.js");return w.existsSync(n)&&w.readFileSync(n,"utf8").includes("@frontfriend/tailwind/next")?!1:(w.writeFileSync(n,`const frontfriend = require('@frontfriend/tailwind/next');
|
|
41
|
+
|
|
42
|
+
module.exports = frontfriend({});
|
|
43
|
+
`),!0)}function Me(e){let n=w.existsSync(y.join(e,"vite.config.ts"))?y.join(e,"vite.config.ts"):y.join(e,"vite.config.js");return w.existsSync(n)&&w.readFileSync(n,"utf8").includes("@frontfriend/tailwind/vite")?!1:(w.writeFileSync(n,`import { defineConfig } from 'vite';
|
|
44
|
+
import frontfriend from '@frontfriend/tailwind/vite';
|
|
45
|
+
|
|
46
|
+
export default defineConfig({
|
|
47
|
+
plugins: [frontfriend()],
|
|
48
|
+
});
|
|
49
|
+
`),!0)}function Je(e){let t=["app/globals.css","src/app/globals.css","styles/globals.css","src/styles/globals.css","src/index.css"].find(i=>w.existsSync(y.join(e,i)))||"app/globals.css",r=y.join(e,t);return w.mkdirSync(y.dirname(r),{recursive:!0}),w.existsSync(r)||w.writeFileSync(r,""),{fullPath:r,relativePath:t}}function Ge(e){let{fullPath:n,relativePath:o}=Je(e),t=w.readFileSync(n,"utf8");return t.includes(fe)?{changed:!1,relativePath:o}:(w.writeFileSync(n,`${fe}
|
|
50
|
+
${t}`),{changed:!0,relativePath:o})}function He(e){let n=y.join(e,"components.json");if(!w.existsSync(n))return{isShadcnProject:!1,changes:[]};let o=Re(e),t=Ne(e),r=[];Ue(e,o)&&r.push(`registered @frontfriend registry for ${o}`),qe(e)&&r.push("added @frontfriend/tailwind dependency"),t==="next"&&Ve(e)&&r.push("wired Next.js plugin"),t==="vite"&&Me(e)&&r.push("wired Vite plugin");let i=Ge(e);return i.changed&&r.push(`imported bundled theme in ${i.relativePath}`),{isShadcnProject:!0,changes:r,framework:o,buildTool:t}}function Be(e){return new Promise(n=>setTimeout(n,e))}function ze(e){let n=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",o=process.platform==="win32"?["/c","start","",e]:[e];try{return Le(n,o,{detached:!0,stdio:"ignore"}).unref(),!0}catch{return!1}}function We(e){e.isShadcnProject&&(console.log("\u{1F9E9} shadcn project wiring"),e.changes.length===0?console.log(" \u2713 Already configured"):e.changes.forEach(n=>console.log(` \u2713 ${n}`)),console.log(` Next: npx shadcn add @frontfriend/button
|
|
51
|
+
`))}function de(e,n){let o=e.indexOf(`${n}:`);if(o===-1)return null;let t=e.indexOf("{",o);if(t===-1)return null;let r=0;for(let i=t;i<e.length;i+=1){let g=e[i];if(g==="{"&&(r+=1),g==="}"&&(r-=1),r===0)return e.slice(t+1,i)}return null}function pe(e){let n={},o=/([A-Za-z0-9_-]+):\s*(['"`])([\s\S]*?)\2/g,t;for(;t=o.exec(e||"");)n[t[1]]=t[3].replace(/\s+/g," ").trim();return n}function Ye(e){if(!e.includes("cva(")||!e.includes("variants:"))return{skipped:!0,reason:"button source is not cva-structured"};let n=e.match(/cva\(\s*(['"`])([\s\S]*?)\1\s*,/),o=de(e,"variant"),t=de(e,"size"),r=pe(o),i=pe(t);if(Object.keys(r).length===0&&Object.keys(i).length===0)return{skipped:!0,reason:"no cva variant class maps found"};let g={default:"main",outline:"tertiary",destructive:"destructive",secondary:"secondary",ghost:"ghost",link:"link"},d={root:n?n[2].replace(/\s+/g," ").trim():"",variants:{variant:{},size:{}}};for(let[f,l]of Object.entries(r))d.variants.variant[g[f]||f]=l;for(let[f,l]of Object.entries(i))d.variants.size[f]=l;return{skipped:!1,config:{button:d}}}P.name("frontfriend").description("FrontFriend Tailwind CLI - Manage design tokens and cache").version("4.0.0");P.command("login").description("Authenticate FrontFriend CLI with an 8-digit device code").option("--api-url <url>","FrontFriend API URL",process.env.FF_API_URL||"https://app.frontfriend.dev").option("--no-open","Do not open the browser automatically").action(async e=>{try{let n=e.apiUrl.replace(/\/$/,""),o=await z(`${n}/api/auth/device/start`,{}),t=o.interval||5,r=Date.now()+(o.expires_in||600)*1e3;for(console.log("\u{1F510} FrontFriend Login"),console.log(`====================
|
|
52
|
+
`),console.log(`Vai su ${o.verification_uri} e inserisci il codice:`),console.log(`
|
|
53
|
+
${o.user_code}
|
|
54
|
+
`),e.open!==!1&&ze(o.verification_uri_complete||o.verification_uri)&&console.log("Ho aperto il browser per te."),console.log(`In attesa di approvazione...
|
|
55
|
+
`);Date.now()<r;){await Be(t*1e3);let i=await z(`${n}/api/auth/device/poll`,{device_code:o.device_code});if(i.error!=="authorization_pending"){if(i.error==="slow_down"){t=i.interval||t+5;continue}if(i.error)throw new Error(`${i.error}: ${i.error_description||"Authentication failed"}`);be({accessToken:i.access_token,refreshToken:i.refresh_token,deviceId:i.device_id,expiresAt:Date.now()+(i.expires_in||900)*1e3,refreshExpiresAt:Date.now()+(i.refresh_expires_in||604800)*1e3,ffApiUrl:n}),console.log("\u2705 Login completato!"),console.log(` Credenziali salvate in ${Pe()}`),process.exit(0)}}throw new Error("expired_token: codice scaduto, esegui di nuovo `frontfriend login`.")}catch(n){console.error("\u274C Login failed:",n.message),process.exit(1)}});P.command("logout").description("Remove FrontFriend CLI credentials").option("--api-url <url>","FrontFriend API URL",process.env.FF_API_URL||"https://app.frontfriend.dev").action(async e=>{let n=_e(),o=(e.apiUrl||(n==null?void 0:n.ffApiUrl)||"https://app.frontfriend.dev").replace(/\/$/,"");if(n!=null&&n.accessToken&&(n!=null&&n.deviceId))try{await z(`${o}/api/plugin/logout`,{device_id:n.deviceId},n.accessToken)}catch{}let t=De();console.log(t?"\u2705 Logged out. Credentials removed.":"\u2139\uFE0F No stored credentials found."),process.exit(0)});P.command("init").description("Initialize FrontFriend configuration and fetch from API").option("--force","Force refresh even if cache is valid").option("--verbose","Show detailed output including safelist size and token counts").option("--tag <tag>","Fetch specific tag (e.g., dev, next, latest)").option("--version <version>","Fetch specific version (e.g., v1.0.0)").action(async e=>{var n;try{console.log("\u{1F527} FrontFriend Setup"),console.log(`==================
|
|
56
|
+
`);let t=await new N().load(),r=He(t.appRoot);if(We(r),!t.ffId)throw r.isShadcnProject&&(console.log("\u2705 Local FrontFriend wiring complete!"),console.log(' Add frontfriend.config.js with "ff-id" later to fetch cloud design tokens.'),process.exit(0)),new _(`ff-id not found in frontfriend.config.js
|
|
57
|
+
Please add "ff-id" to your frontfriend.config.js file`,"ff-id");let i=e.tag||t.tag,g=e.version||t.version;console.log("\u{1F4CB} Configuration loaded"),console.log(` FF_ID: ${t.ffId}`),console.log(` App Root: ${t.appRoot}`),i&&console.log(` Tag: ${i}`),g&&console.log(` Version: ${g}`),console.log("");let d=new W(t.appRoot);!e.force&&d.exists()&&d.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
|
|
58
|
+
`):d.exists()?console.log(` Cache expired
|
|
42
59
|
`):console.log(` No cache found
|
|
43
|
-
`),console.log("\u{1F310} Fetching configuration...");let
|
|
60
|
+
`),console.log("\u{1F310} Fetching configuration...");let f=new ge(t.ffId,{baseURL:t["api-url"]||t.apiUrl,authToken:t["auth-token"]||t.authToken});(t["api-url"]||t.apiUrl)&&console.log(` Using API URL: ${t["api-url"]||t.apiUrl}`);let l=new Ae,s=null;try{s=await f.fetchProcessedTokens({tag:i,version:g,tailwindV4:l.useTailwindV4})}catch(c){console.log(` \u26A0\uFE0F Failed to fetch pre-processed tokens: ${c.message}`)}if(s){if(console.log(" \u2713 Pre-processed tokens fetched from server"),console.log(" \u2713 Skipping local processing"),s.versionMetadata){let c=s.versionMetadata;console.log(" \u2139\uFE0F Version Information:"),c.version&&console.log(` Version: ${c.version}`),c.tag&&console.log(` Tag: ${c.tag}`),c.cached!==void 0&&console.log(` From Cache: ${c.cached?"Yes":"No"}`)}if(console.log(""),(!((n=s.tokens)!=null&&n.fontFamily)||Object.keys(s.tokens.fontFamily).length===0)&&(console.log("\u26A0\uFE0F Warning: No font family utilities found"),console.log(" Font classes like font-primary and font-secondary will not be available"),s.globalTokens?console.log(` Global tokens are present - font processor may need to be updated
|
|
44
61
|
`):console.log(` Consider adding font family tokens to your design system
|
|
45
|
-
`)),console.log("\u{1F4BE} Saving cache..."),(process.env.FF_VERBOSE==="true"||
|
|
46
|
-
`),console.log("\u2705 Setup complete!"),console.log(" Your FrontFriend configuration has been cached."),console.log(" The Tailwind plugin will now use this cached data."),
|
|
47
|
-
\u{1F524} Generating TypeScript types...`);try{let{generateIconTypes:c}=
|
|
48
|
-
`);let[a,
|
|
49
|
-
`),console.log("\u2699\uFE0F Processing tokens...")
|
|
50
|
-
`);let
|
|
51
|
-
`),console.log("\
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
`)
|
|
55
|
-
`);let e=await new
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
\
|
|
60
|
-
`)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
\
|
|
66
|
-
\
|
|
67
|
-
\u{
|
|
68
|
-
|
|
69
|
-
\u{
|
|
70
|
-
\
|
|
71
|
-
\u2705
|
|
62
|
+
`)),l.useTailwindV4){let c=l.extractClassesFromComponentsConfig(s.componentsConfig),m=[...s.safelist||[],...c||[]];s.themeCSS||(s.themeCSS=l.generateThemeCSS({variables:s.variables||{},semanticVariables:s.semanticVariables||{},semanticDarkVariables:s.semanticDarkVariables||{},fontFaces:s.fonts||[],keyframes:s.keyframes||{},animations:s.animations||{},custom:s.custom||{}})),s.classesContent||(s.classesContent=l.generateClassesContent(m))}if(console.log("\u{1F4BE} Saving cache..."),(process.env.FF_VERBOSE==="true"||e.verbose)&&console.log(` Safelist has ${s.safelist?s.safelist.length:0} items`),d.save(s),console.log(` \u2713 Cache saved successfully
|
|
63
|
+
`),console.log("\u2705 Setup complete!"),console.log(" Your FrontFriend configuration has been cached."),console.log(" The Tailwind plugin will now use this cached data."),t.types){console.log(`
|
|
64
|
+
\u{1F524} Generating TypeScript types...`);try{let{generateIconTypes:c}=B(),m=t.aliases&&t.aliases.types?y.join(t.appRoot,t.aliases.types,"frontfriend.d.ts"):void 0;c({projectRoot:t.appRoot,outputPath:m})}catch(c){console.log(" \u26A0\uFE0F Type generation failed:",c.message),console.log(' Enable type generation in frontfriend.config.js with "types": true')}}process.exit(0)}console.log(` \u2139\uFE0F Pre-processed tokens not available, falling back to local processing
|
|
65
|
+
`);let[a,$,v,j,u,D]=await Promise.all([f.fetchTokens(),f.fetchComponentsConfig(),f.fetchFonts(),f.fetchIcons(),f.fetchVersion(),f.fetchCustomCss()]);if(console.log(" \u2713 Tokens fetched"),a.global||a.colors||a.semantic||a.semanticDark){let c=[a.global?"global":null,a.colors?"colors":null,a.semantic?"semantic":null,a.semanticDark?"semanticDark":null].filter(Boolean).join(", ");console.log(` (Found: ${c})`)}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
|
|
66
|
+
`),console.log("\u2699\uFE0F Processing tokens..."),console.log(" Processing raw token data...");let S=await l.process({colors:a.colors||a.global,semantic:a.semantic,semanticDark:a.semanticDark,fonts:v,globalTokens:a.global,animations:a.animations,version:u==null?void 0:u.version,ffId:t.ffId,customCss:D});console.log(" \u2713 Colors processed"),console.log(" \u2713 Semantic tokens processed"),S.fontFaces.length>0&&console.log(` \u2713 ${S.fontFaces.length} font faces processed`),Object.keys(S.animations).length>0&&console.log(` \u2713 ${Object.keys(S.animations).length} animations processed`),console.log(`
|
|
67
|
+
`);let A=l.extractClassesFromComponentsConfig($),V=[...S.safelist||[],...A||[]],U=[...new Set(V)].sort(),h,p;l.useTailwindV4&&(console.log("\u{1F3A8} Generating Tailwind v4 CSS artifacts..."),h=l.generateThemeCSS(S),p=l.generateClassesContent(U),console.log(" \u2713 theme.css generated"),console.log(` \u2713 classes.css generated for @source scanning
|
|
68
|
+
`));let C={tokens:S.utilities,variables:S.variables,semanticVariables:S.semanticVariables,semanticDarkVariables:S.semanticDarkVariables,fonts:S.fontFaces,icons:j,componentsConfig:$,keyframes:S.keyframes,animations:S.animations,safelist:U,version:u,metadata:S.metadata,custom:S.custom,themeCSS:h,classesContent:p};if(console.log("\u{1F4BE} Saving cache..."),(process.env.FF_VERBOSE==="true"||e.verbose)&&(console.log(" Cache data summary:"),console.log(` - Tokens: ${Object.keys(C.tokens||{}).length} utilities`),console.log(` - Variables: ${Object.keys(C.variables||{}).length} CSS variables`),console.log(` - Semantic variables: ${Object.keys(C.semanticVariables||{}).length} semantic variables`),console.log(` - Font faces: ${(C.fonts||[]).length} fonts`),console.log(` - Safelist: ${(C.safelist||[]).length} safelist classes`)),d.save(C),console.log(` \u2713 Cache saved successfully
|
|
69
|
+
`),console.log("\u2705 Setup complete!"),console.log(" Your FrontFriend configuration has been cached."),console.log(" The Tailwind plugin will now use this cached data."),t.types){console.log(`
|
|
70
|
+
\u{1F524} Generating TypeScript types...`);try{let{generateIconTypes:c}=B(),m=t.aliases&&t.aliases.types?y.join(t.appRoot,t.aliases.types,"frontfriend.d.ts"):void 0;c({projectRoot:t.appRoot,outputPath:m})}catch(c){console.log(" \u26A0\uFE0F Type generation failed:",c.message),console.log(' Enable type generation in frontfriend.config.js with "types": true')}}process.exit(0)}catch(o){o instanceof _?console.error("\u274C Configuration Error:",o.message):o instanceof Ee?(console.error("\u274C API Error:",o.message),console.error(` Status: ${o.statusCode}`),console.error(` URL: ${o.url}`)):o instanceof Y?(console.error("\u274C Cache Error:",o.message),console.error(` Operation: ${o.operation}`)):console.error("\u274C Setup failed:",o.message),process.exit(1)}});P.command("clean").description("Remove cached configuration").action(async()=>{try{console.log(`\u{1F9F9} Cleaning FrontFriend cache...
|
|
71
|
+
`);let n=new N().load(),o=new W(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 Y?(console.error("\u274C Cache Error:",e.message),console.error(` Operation: ${e.operation}`)):console.error("\u274C Clean failed:",e.message),process.exit(1)}});P.command("status").description("Show cache status and configuration").action(async()=>{try{console.log("\u{1F4CA} FrontFriend Status"),console.log(`===================
|
|
72
|
+
`),console.log("Package:"),console.log(" Version: 4.0.0"),console.log("");let n=await new N().load();console.log("Configuration:"),console.log(` FF_ID: ${n.ffId||"Not set"}`),console.log(` App Root: ${n.appRoot}`),n.tag&&console.log(` Tag: ${n.tag}`),n.version&&console.log(` Version: ${n.version}`),console.log("");let o=new W(n.appRoot),t=o.getCacheDir();if(console.log("Cache:"),console.log(` Directory: ${y.relative(process.cwd(),t)}`),o.exists()){console.log(` Status: ${o.isValid()?"\u2705 Valid":"\u26A0\uFE0F Expired"}`);let r=o.load();if(r&&r.metadata){let i=new Date(r.metadata.timestamp);console.log(` Created: ${i.toLocaleString()}`),console.log(` Format Version: ${r.metadata.version||"Unknown"}`)}r&&r.versionMetadata&&(console.log(" Cached Version Info:"),r.versionMetadata.version&&console.log(` Version: ${r.versionMetadata.version}`),r.versionMetadata.tag&&console.log(` Tag: ${r.versionMetadata.tag}`))}else console.log(" Status: \u274C Not found");process.exit(0)}catch(e){e instanceof _?console.error("\u274C Configuration Error:",e.message):e instanceof Y?(console.error("\u274C Cache Error:",e.message),console.error(` Operation: ${e.operation}`)):console.error("\u274C Status check failed:",e.message),process.exit(1)}});P.command("migrate").description("Migrate cva-structured shadcn components into FrontFriend config").option("--component <name>","Component to migrate (currently: button)","button").option("--input <path>","Path to the shadcn component source").option("--output <path>","Output JSON path for migrated config","frontfriend.migrated-components.json").option("--no-push","Do not push migrated config to the configured FrontFriend cloud").action(async e=>{try{let o=await new N().load(),t=o.appRoot,r=e.component;if(r!=="button")throw new _(`migrate currently supports only "button" (received "${r}")`);let i=y.resolve(t,e.input||"components/ui/button.tsx");if(!w.existsSync(i))throw new _(`Component source not found: ${y.relative(t,i)}`);let g=w.readFileSync(i,"utf8"),d=Ye(g);d.skipped&&(console.log(`\u23ED\uFE0F Skipped ${r}: ${d.reason}`),process.exit(0));let f=y.resolve(t,e.output);K(f,d.config);let l=!1;e.push&&o.ffId&&(await new ge(o.ffId,{baseURL:o["api-url"]||o.apiUrl,authToken:o["auth-token"]||o.authToken}).pushComponentsConfig(d.config),l=!0),console.log("\u2705 Migration complete!"),console.log(` Component: ${r}`),console.log(` Output: ${y.relative(t,f)}`),console.log(l?" Cloud config: pushed user override":" Cloud config: not pushed (missing ff-id or --no-push)"),process.exit(0)}catch(n){n instanceof _?console.error("\u274C Migration Error:",n.message):console.error("\u274C Migration failed:",n.message),process.exit(1)}});P.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("--registry <generation>","Registry generation to use (legacy/v4)").option("--overwrite","Overwrite existing files").action(async(e,n)=>{try{console.log(`\u{1F4E6} Downloading components...
|
|
73
|
+
`);let t=await new N().load();if(!t.ffId)throw new _(`ff-id not found in frontfriend.config.js
|
|
74
|
+
Please add "ff-id" to your frontfriend.config.js file`,"ff-id");let r=new M({appRoot:t.appRoot,config:t,framework:n.framework,registryGeneration:n.registry,outputPath:n.output,overwrite:n.overwrite}),i=e;if(e.length===1&&e[0].toLowerCase()==="all"){let d=await r.getAllComponents(),f=await r.getCustomComponents();process.env.FF_DEBUG&&console.log(`Debug: Found ${f.length} custom components: ${f.join(", ")}`),d.length===0&&(console.error("\u274C Failed to fetch component list"),process.exit(1));let l=new Set(f),s=d.filter(a=>!l.has(a));i=[...f,...s],console.log("\u{1F4CB} Configuration"),console.log(` Framework: ${r.framework}`),console.log(` Registry: ${r.registryGeneration}`),console.log(` Output: ${r.outputPath}`),console.log(` Components: All ${i.length} components`),process.env.FF_DEBUG&&f.length>0&&console.log(` Debug: ${f.length} custom components will override standard ones`),console.log("")}else console.log("\u{1F4CB} Configuration"),console.log(` Framework: ${r.framework}`),console.log(` Registry: ${r.registryGeneration}`),console.log(` Output: ${r.outputPath}`),console.log(` Components: ${e.join(", ")}
|
|
75
|
+
`);let g=await r.downloadComponents(i);g.successful.length>0&&(console.log(`
|
|
76
|
+
\u2705 Download complete!`),console.log(` Downloaded: ${g.successful.length} components`)),g.failed.length>0&&(console.log(""),g.failed.forEach(d=>{d.error.includes("not found")?console.log(`\u274C ${d.error}`):console.log(`\u274C Failed to download ${d.component}: ${d.error}`)})),g.successful.length>0&&g.dependencies.length>0&&(console.log(`
|
|
77
|
+
\u{1F4E6} Install dependencies:`),console.log(` npm install ${g.dependencies.join(" ")}`)),g.successful.length===0&&g.failed.length>0&&process.exit(1),process.exit(0)}catch(o){o instanceof _?(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)}});P.command("add").description("Add UI components or generated pages from Frontfriend platform").argument("<target>","Component name, or resource type (page/project)").argument("[rest...]","Additional component names, or resource ID for page/project").option("-f, --framework <framework>","Framework to download components for (react/vue)").option("-o, --output <path>","Output directory for components").option("--name <name>","Component name for the page").option("--path <path>","Output path (overrides aliases.pages)").option("--force","Overwrite existing files").option("--overwrite","Overwrite existing component files").option("--pages <ids>","Specific page IDs for project (comma-separated)").option("--all","Add all pages from project").option("--registry <generation>","Registry generation to use for missing UI components (legacy/v4)").action(async(e,n=[],o)=>{var t,r,i,g,d,f;try{console.log(`\u{1F680} Frontfriend Add
|
|
78
|
+
`);let s=await new N().load();if(!s.ffId)throw new _(`ff-id not found in frontfriend.config.js
|
|
79
|
+
Please add "ff-id" to your frontfriend.config.js file`,"ff-id");let a=e,$=n[0];if(a!=="page"&&a!=="project"){console.log(`\u{1F4E6} Downloading components...
|
|
80
|
+
`);let h=[e,...n],p=new M({appRoot:s.appRoot,config:s,framework:o.framework||s.framework,registryGeneration:o.registry,outputPath:o.output||o.path,overwrite:o.overwrite||o.force}),C=h;if(h.length===1&&h[0].toLowerCase()==="all"){let m=await p.getAllComponents(),b=await p.getCustomComponents();m.length===0&&(console.error("\u274C Failed to fetch component list"),process.exit(1));let T=new Set(b),k=m.filter(x=>!T.has(x));C=[...b,...k]}console.log("\u{1F4CB} Configuration"),console.log(` Framework: ${p.framework}`),console.log(` Registry: ${p.registryGeneration}`),console.log(` Output: ${p.outputPath}`),console.log(` Components: ${C.join(", ")}
|
|
81
|
+
`);let c=await p.downloadComponents(C);c.successful.length>0&&(console.log(`
|
|
82
|
+
\u2705 Download complete!`),console.log(` Downloaded: ${c.successful.length} components`)),c.failed.length>0&&(console.log(""),c.failed.forEach(m=>{console.log(`\u274C Failed to download ${m.component}: ${m.error}`)})),c.successful.length>0&&c.dependencies.length>0&&(console.log(`
|
|
83
|
+
\u{1F4E6} Install dependencies:`),console.log(` npm install ${c.dependencies.join(" ")}`)),c.successful.length===0&&c.failed.length>0&&process.exit(1),process.exit(0)}$||(console.error(`\u274C Missing ${a} ID`),process.exit(1));let v=require("fs"),{spawn:j}=require("child_process"),u=require("path"),D=require("https"),S=require("http"),{URL:A}=require("url"),V=h=>new Promise((p,C)=>{let c=new A(h),m=c.protocol==="https:",b=m?D:S,T={hostname:c.hostname,port:c.port||(m?443:80),path:c.pathname+c.search,method:"GET",headers:{Accept:"application/json"}};b.get(T,k=>{let x="";k.on("data",F=>x+=F),k.on("end",()=>{if(k.statusCode===200)try{p(JSON.parse(x))}catch{C(new Error("Invalid JSON response"))}else try{let F=JSON.parse(x);C(new Error(F.error||`HTTP ${k.statusCode}`))}catch{C(new Error(`HTTP ${k.statusCode}: ${x}`))}})}).on("error",C)}),U=s["api-url"]||s.apiUrl||"https://app.frontfriend.dev";if(a==="page"){console.log("\u{1F4CB} Configuration"),console.log(` FF_ID: ${s.ffId}`),console.log(` Page ID: ${$}`);let h=o.path||((t=s.aliases)==null?void 0:t.pages)||"src/pages";console.log(` Output: ${h}
|
|
84
|
+
`),console.log("\u{1F310} Fetching page...");let p=await V(`${U}/api/export/pages/${$}?ffId=${s.ffId}`);if(console.log(` \u2713 Page fetched: ${p.name}`),p.components&&p.components.length>0){console.log(`
|
|
85
|
+
\u{1F4E6} Checking UI components...`);let k=new M({appRoot:s.appRoot,config:s,framework:s.framework||"react",registryGeneration:o.registry}),x=((i=(r=s.aliases)==null?void 0:r.ui)==null?void 0:i.replace("@/",""))||"src/components/ui",F=[];for(let I of p.components){let E=u.join(s.appRoot,x,I);v.existsSync(E)||v.existsSync(E+".tsx")||v.existsSync(E+".jsx")||v.existsSync(E+".vue")||F.push(I)}if(F.length>0){console.log(` Missing components: ${F.join(", ")}`),console.log(" Downloading missing components...");let I=await k.downloadComponents(F);I.successful.length>0&&console.log(` \u2713 Downloaded ${I.successful.length} components`)}else console.log(" \u2713 All required components present")}console.log(`
|
|
86
|
+
\u{1F4BE} Writing page file...`);let C=o.name?`${o.name}.tsx`:`${p.componentName}.tsx`,c=h.startsWith("@/")?h.replace("@/",""):h,m=u.join(s.appRoot,c,C);v.existsSync(m)&&!o.force&&(console.error(`
|
|
87
|
+
\u274C File already exists: ${m}`),console.error(" Use --force to overwrite"),process.exit(1));let b=u.dirname(m);v.existsSync(b)||v.mkdirSync(b,{recursive:!0}),v.writeFileSync(m,p.code),console.log(` \u2713 Created: ${u.relative(s.appRoot,m)}`),console.log(`
|
|
88
|
+
\u2705 Page added successfully!`),console.log(` Component: ${p.componentName}`),console.log(` Location: ${u.relative(s.appRoot,m)}`),console.log(`
|
|
89
|
+
\u{1F4DA} Import in your app:`);let T;h.startsWith("@/")?T=`${h}/${p.componentName}`:h.startsWith("src/")?T=`@/${h.substring(4)}/${p.componentName}`:T=`@/${h}/${p.componentName}`,console.log(` import ${p.componentName} from '${T}';`)}else if(a==="project"){!o.all&&!o.pages&&(console.error("\u274C Please specify --all or --pages <ids>"),process.exit(1)),console.log("\u{1F4CB} Configuration"),console.log(` FF_ID: ${s.ffId}`),console.log(` Project ID: ${$}`);let h=o.path||((g=s.aliases)==null?void 0:g.pages)||"src/pages";console.log(` Output: ${h}
|
|
90
|
+
`),console.log("\u{1F310} Fetching project pages...");let p=await V(`${U}/api/export/projects/${$}?ffId=${s.ffId}`);console.log(` \u2713 Project fetched: ${p.projectTitle}`),console.log(` \u2713 Found ${p.pages.length} pages`);let C=p.pages;if(o.pages){let k=o.pages.split(",").map(x=>x.trim());C=p.pages.filter(x=>k.includes(x.id)),console.log(` \u2713 Adding ${C.length} specific pages`)}if(p.components&&p.components.length>0){console.log(`
|
|
91
|
+
\u{1F4E6} Checking UI components...`);let k=new M({appRoot:s.appRoot,config:s,framework:s.framework||"react",registryGeneration:o.registry}),x=((f=(d=s.aliases)==null?void 0:d.ui)==null?void 0:f.replace("@/",""))||"src/components/ui",F=[];for(let I of p.components){let E=u.join(s.appRoot,x,I);v.existsSync(E)||v.existsSync(E+".tsx")||v.existsSync(E+".jsx")||v.existsSync(E+".vue")||F.push(I)}if(F.length>0){console.log(` Missing components: ${F.join(", ")}`),console.log(" Downloading missing components...");let I=await k.downloadComponents(F);I.successful.length>0&&console.log(` \u2713 Downloaded ${I.successful.length} components`)}else console.log(" \u2713 All required components present")}console.log(`
|
|
92
|
+
\u{1F4BE} Writing page files...`);let c=h.startsWith("@/")?h.replace("@/",""):h,m=u.join(s.appRoot,c);v.existsSync(m)||v.mkdirSync(m,{recursive:!0});let b=0,T=0;for(let k of C){let x=`${k.componentName}.tsx`,F=u.join(m,x);if(v.existsSync(F)&&!o.force){console.log(` \u26A0\uFE0F Skipped (exists): ${x}`),T++;continue}v.writeFileSync(F,k.code),console.log(` \u2713 Created: ${x}`),b++}console.log(`
|
|
93
|
+
\u2705 Project pages added!`),console.log(` Added: ${b} pages`),T>0&&(console.log(` Skipped: ${T} pages (already exist)`),console.log(" Use --force to overwrite existing files"))}else console.error(`\u274C Unknown type: ${a}`),console.error(' Use "page" or "project"'),process.exit(1);process.exit(0)}catch(l){l instanceof _?console.error("\u274C Configuration Error:",l.message):console.error("\u274C Failed:",l.message),process.exit(1)}});P.parse(process.argv);process.argv.slice(2).length||(P.outputHelp(),process.exit(0));
|
|
72
94
|
//# sourceMappingURL=cli-temp.js.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var a={anyOf:[{type:"string"},{type:"number"},{type:"boolean"},{type:"null"}]},r={$id:"https://frontfriend.dev/schemas/components-config.schema.json",type:"object",additionalProperties:{$ref:"#/$defs/componentEnvelope"},$defs:{classValue:a,classMap:{type:"object",additionalProperties:{anyOf:[{$ref:"#/$defs/classValue"},{$ref:"#/$defs/classMap"}]}},propMap:{type:"object",additionalProperties:{anyOf:[{$ref:"#/$defs/classValue"},{$ref:"#/$defs/propMap"}]}},componentEnvelope:{type:"object",additionalProperties:!1,properties:{root:{anyOf:[{type:"string"},{type:"null"}]},variants:{type:"object",additionalProperties:{$ref:"#/$defs/classMap"}},variant:{$ref:"#/$defs/classMap"},size:{$ref:"#/$defs/classMap"},sizes:{$ref:"#/$defs/classMap"},iconPosition:{$ref:"#/$defs/classMap"},color:{$ref:"#/$defs/classMap"},fullwidth:{$ref:"#/$defs/classMap"},slots:{type:"object",additionalProperties:{$ref:"#/$defs/componentEnvelope"}},icon:{$ref:"#/$defs/componentEnvelope"},states:{$ref:"#/$defs/classMap"},compoundVariants:{$ref:"#/$defs/classMap"},defaultVariants:{$ref:"#/$defs/propMap"},props:{$ref:"#/$defs/propMap"}}}}};function o(e){if(typeof e!="string"||e.trim().length===0||/^[0-9]+$/.test(e)||e==="true"||e==="false")return!1;let s=/\s/.test(e),t=/[-:\[\]\/]/.test(e);return!0}module.exports={componentsConfigSchema:r,isCSSClassString:o};
|
|
2
|
+
//# sourceMappingURL=components-config-schema.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../lib/core/components-config-schema.js"],
|
|
4
|
+
"sourcesContent": ["/**\n * Shared componentsConfig schema for FrontFriend tools.\n *\n * The schema defines the canonical per-component envelope used by ffdc,\n * validation scripts, migration tooling, and registry completeness checks.\n * Keep this file as the single source of truth; consumers should re-export it\n * instead of maintaining local schema copies.\n */\n\nconst classValueSchema = {\n anyOf: [\n { type: 'string' },\n { type: 'number' },\n { type: 'boolean' },\n { type: 'null' },\n ],\n};\n\nconst componentsConfigSchema = {\n $id: 'https://frontfriend.dev/schemas/components-config.schema.json',\n type: 'object',\n additionalProperties: { $ref: '#/$defs/componentEnvelope' },\n $defs: {\n classValue: classValueSchema,\n classMap: {\n type: 'object',\n additionalProperties: {\n anyOf: [\n { $ref: '#/$defs/classValue' },\n { $ref: '#/$defs/classMap' },\n ],\n },\n },\n propMap: {\n type: 'object',\n additionalProperties: {\n anyOf: [\n { $ref: '#/$defs/classValue' },\n { $ref: '#/$defs/propMap' },\n ],\n },\n },\n componentEnvelope: {\n type: 'object',\n additionalProperties: false,\n properties: {\n // Root classes for the component itself.\n root: { anyOf: [{ type: 'string' }, { type: 'null' }] },\n\n // Canonical variant buckets: variants.variant, variants.size,\n // variants.iconPosition, etc. Values stay recursive so leaves can be\n // overridden independently during deep merges.\n variants: {\n type: 'object',\n additionalProperties: { $ref: '#/$defs/classMap' },\n },\n\n // Compatibility aliases for existing component code while v4 sources\n // move to the canonical variants/slots envelope.\n variant: { $ref: '#/$defs/classMap' },\n size: { $ref: '#/$defs/classMap' },\n sizes: { $ref: '#/$defs/classMap' },\n iconPosition: { $ref: '#/$defs/classMap' },\n color: { $ref: '#/$defs/classMap' },\n fullwidth: { $ref: '#/$defs/classMap' },\n\n // Named child slots. Each slot uses the same envelope so tooling can be\n // generic instead of component-specific.\n slots: {\n type: 'object',\n additionalProperties: { $ref: '#/$defs/componentEnvelope' },\n },\n\n // Compatibility alias for the Button icon slot.\n icon: { $ref: '#/$defs/componentEnvelope' },\n\n states: { $ref: '#/$defs/classMap' },\n compoundVariants: { $ref: '#/$defs/classMap' },\n defaultVariants: { $ref: '#/$defs/propMap' },\n props: { $ref: '#/$defs/propMap' },\n },\n },\n },\n};\n\nfunction isCSSClassString(value) {\n if (typeof value !== 'string') return false;\n\n if (value.trim().length === 0) return false;\n\n if (/^[0-9]+$/.test(value)) return false;\n\n if (value === 'true' || value === 'false') return false;\n\n const hasSpaces = /\\s/.test(value);\n const hasTailwindSyntax = /[-:\\[\\]\\/]/.test(value);\n\n if (hasSpaces || hasTailwindSyntax) return true;\n\n return true;\n}\n\nmodule.exports = {\n componentsConfigSchema,\n isCSSClassString,\n};\n"],
|
|
5
|
+
"mappings": "AASA,IAAMA,EAAmB,CACvB,MAAO,CACL,CAAE,KAAM,QAAS,EACjB,CAAE,KAAM,QAAS,EACjB,CAAE,KAAM,SAAU,EAClB,CAAE,KAAM,MAAO,CACjB,CACF,EAEMC,EAAyB,CAC7B,IAAK,gEACL,KAAM,SACN,qBAAsB,CAAE,KAAM,2BAA4B,EAC1D,MAAO,CACL,WAAYD,EACZ,SAAU,CACR,KAAM,SACN,qBAAsB,CACpB,MAAO,CACL,CAAE,KAAM,oBAAqB,EAC7B,CAAE,KAAM,kBAAmB,CAC7B,CACF,CACF,EACA,QAAS,CACP,KAAM,SACN,qBAAsB,CACpB,MAAO,CACL,CAAE,KAAM,oBAAqB,EAC7B,CAAE,KAAM,iBAAkB,CAC5B,CACF,CACF,EACA,kBAAmB,CACjB,KAAM,SACN,qBAAsB,GACtB,WAAY,CAEV,KAAM,CAAE,MAAO,CAAC,CAAE,KAAM,QAAS,EAAG,CAAE,KAAM,MAAO,CAAC,CAAE,EAKtD,SAAU,CACR,KAAM,SACN,qBAAsB,CAAE,KAAM,kBAAmB,CACnD,EAIA,QAAS,CAAE,KAAM,kBAAmB,EACpC,KAAM,CAAE,KAAM,kBAAmB,EACjC,MAAO,CAAE,KAAM,kBAAmB,EAClC,aAAc,CAAE,KAAM,kBAAmB,EACzC,MAAO,CAAE,KAAM,kBAAmB,EAClC,UAAW,CAAE,KAAM,kBAAmB,EAItC,MAAO,CACL,KAAM,SACN,qBAAsB,CAAE,KAAM,2BAA4B,CAC5D,EAGA,KAAM,CAAE,KAAM,2BAA4B,EAE1C,OAAQ,CAAE,KAAM,kBAAmB,EACnC,iBAAkB,CAAE,KAAM,kBAAmB,EAC7C,gBAAiB,CAAE,KAAM,iBAAkB,EAC3C,MAAO,CAAE,KAAM,iBAAkB,CACnC,CACF,CACF,CACF,EAEA,SAASE,EAAiBC,EAAO,CAO/B,GANI,OAAOA,GAAU,UAEjBA,EAAM,KAAK,EAAE,SAAW,GAExB,WAAW,KAAKA,CAAK,GAErBA,IAAU,QAAUA,IAAU,QAAS,MAAO,GAElD,IAAMC,EAAY,KAAK,KAAKD,CAAK,EAC3BE,EAAoB,aAAa,KAAKF,CAAK,EAEjD,MAA2C,EAG7C,CAEA,OAAO,QAAU,CACf,uBAAAF,EACA,iBAAAC,CACF",
|
|
6
|
+
"names": ["classValueSchema", "componentsConfigSchema", "isCSSClassString", "value", "hasSpaces", "hasTailwindSyntax"]
|
|
7
|
+
}
|
package/dist/ffdc.d.ts
CHANGED
|
@@ -3,4 +3,6 @@
|
|
|
3
3
|
* @param config - Design configuration object
|
|
4
4
|
* @returns Processed configuration
|
|
5
5
|
*/
|
|
6
|
-
export function ffdc(config: Record<string, any> | null | undefined): Record<string, any>;
|
|
6
|
+
export function ffdc(config: Record<string, any> | null | undefined): Record<string, any>;
|
|
7
|
+
export const componentsConfigSchema: Record<string, any>;
|
|
8
|
+
export function isCSSClassString(value: string): boolean;
|
package/dist/ffdc.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
var n=(e,s)=>()=>(s||e((s={exports:{}}).exports,s),s.exports);var o=n(($,r)=>{var f={anyOf:[{type:"string"},{type:"number"},{type:"boolean"},{type:"null"}]},p={$id:"https://frontfriend.dev/schemas/components-config.schema.json",type:"object",additionalProperties:{$ref:"#/$defs/componentEnvelope"},$defs:{classValue:f,classMap:{type:"object",additionalProperties:{anyOf:[{$ref:"#/$defs/classValue"},{$ref:"#/$defs/classMap"}]}},propMap:{type:"object",additionalProperties:{anyOf:[{$ref:"#/$defs/classValue"},{$ref:"#/$defs/propMap"}]}},componentEnvelope:{type:"object",additionalProperties:!1,properties:{root:{anyOf:[{type:"string"},{type:"null"}]},variants:{type:"object",additionalProperties:{$ref:"#/$defs/classMap"}},variant:{$ref:"#/$defs/classMap"},size:{$ref:"#/$defs/classMap"},sizes:{$ref:"#/$defs/classMap"},iconPosition:{$ref:"#/$defs/classMap"},color:{$ref:"#/$defs/classMap"},fullwidth:{$ref:"#/$defs/classMap"},slots:{type:"object",additionalProperties:{$ref:"#/$defs/componentEnvelope"}},icon:{$ref:"#/$defs/componentEnvelope"},states:{$ref:"#/$defs/classMap"},compoundVariants:{$ref:"#/$defs/classMap"},defaultVariants:{$ref:"#/$defs/propMap"},props:{$ref:"#/$defs/propMap"}}}}};function i(e){if(typeof e!="string"||e.trim().length===0||/^[0-9]+$/.test(e)||e==="true"||e==="false")return!1;let s=/\s/.test(e),a=/[-:\[\]\/]/.test(e);return!0}r.exports={componentsConfigSchema:p,isCSSClassString:i}});var{componentsConfigSchema:c,isCSSClassString:l}=o();function t(e){if(!e)return{};if(typeof e!="object")throw new Error("Config must be an object");return e}t.componentsConfigSchema=c;t.isCSSClassString=l;module.exports=t;
|
|
2
2
|
//# sourceMappingURL=ffdc.js.map
|
package/dist/ffdc.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../ffdc.js"],
|
|
4
|
-
"sourcesContent": ["// ffdc.js - Simple config accessor\n// No base64 decoding, just returns the config object directly\n\
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": ["config"]
|
|
3
|
+
"sources": ["../lib/core/components-config-schema.js", "../ffdc.js"],
|
|
4
|
+
"sourcesContent": ["/**\n * Shared componentsConfig schema for FrontFriend tools.\n *\n * The schema defines the canonical per-component envelope used by ffdc,\n * validation scripts, migration tooling, and registry completeness checks.\n * Keep this file as the single source of truth; consumers should re-export it\n * instead of maintaining local schema copies.\n */\n\nconst classValueSchema = {\n anyOf: [\n { type: 'string' },\n { type: 'number' },\n { type: 'boolean' },\n { type: 'null' },\n ],\n};\n\nconst componentsConfigSchema = {\n $id: 'https://frontfriend.dev/schemas/components-config.schema.json',\n type: 'object',\n additionalProperties: { $ref: '#/$defs/componentEnvelope' },\n $defs: {\n classValue: classValueSchema,\n classMap: {\n type: 'object',\n additionalProperties: {\n anyOf: [\n { $ref: '#/$defs/classValue' },\n { $ref: '#/$defs/classMap' },\n ],\n },\n },\n propMap: {\n type: 'object',\n additionalProperties: {\n anyOf: [\n { $ref: '#/$defs/classValue' },\n { $ref: '#/$defs/propMap' },\n ],\n },\n },\n componentEnvelope: {\n type: 'object',\n additionalProperties: false,\n properties: {\n // Root classes for the component itself.\n root: { anyOf: [{ type: 'string' }, { type: 'null' }] },\n\n // Canonical variant buckets: variants.variant, variants.size,\n // variants.iconPosition, etc. Values stay recursive so leaves can be\n // overridden independently during deep merges.\n variants: {\n type: 'object',\n additionalProperties: { $ref: '#/$defs/classMap' },\n },\n\n // Compatibility aliases for existing component code while v4 sources\n // move to the canonical variants/slots envelope.\n variant: { $ref: '#/$defs/classMap' },\n size: { $ref: '#/$defs/classMap' },\n sizes: { $ref: '#/$defs/classMap' },\n iconPosition: { $ref: '#/$defs/classMap' },\n color: { $ref: '#/$defs/classMap' },\n fullwidth: { $ref: '#/$defs/classMap' },\n\n // Named child slots. Each slot uses the same envelope so tooling can be\n // generic instead of component-specific.\n slots: {\n type: 'object',\n additionalProperties: { $ref: '#/$defs/componentEnvelope' },\n },\n\n // Compatibility alias for the Button icon slot.\n icon: { $ref: '#/$defs/componentEnvelope' },\n\n states: { $ref: '#/$defs/classMap' },\n compoundVariants: { $ref: '#/$defs/classMap' },\n defaultVariants: { $ref: '#/$defs/propMap' },\n props: { $ref: '#/$defs/propMap' },\n },\n },\n },\n};\n\nfunction isCSSClassString(value) {\n if (typeof value !== 'string') return false;\n\n if (value.trim().length === 0) return false;\n\n if (/^[0-9]+$/.test(value)) return false;\n\n if (value === 'true' || value === 'false') return false;\n\n const hasSpaces = /\\s/.test(value);\n const hasTailwindSyntax = /[-:\\[\\]\\/]/.test(value);\n\n if (hasSpaces || hasTailwindSyntax) return true;\n\n return true;\n}\n\nmodule.exports = {\n componentsConfigSchema,\n isCSSClassString,\n};\n", "// ffdc.js - Simple config accessor\n// No base64 decoding, just returns the config object directly\n\nconst {\n componentsConfigSchema,\n isCSSClassString,\n} = require('./lib/core/components-config-schema');\n\nfunction ffdc(config) {\n if (!config) {\n return {};\n }\n\n if (typeof config !== 'object') {\n throw new Error('Config must be an object');\n }\n\n return config;\n}\n\nffdc.componentsConfigSchema = componentsConfigSchema;\nffdc.isCSSClassString = isCSSClassString;\n\nmodule.exports = ffdc;\n"],
|
|
5
|
+
"mappings": "8DAAA,IAAAA,EAAAC,EAAA,CAAAC,EAAAC,IAAA,CASA,IAAMC,EAAmB,CACvB,MAAO,CACL,CAAE,KAAM,QAAS,EACjB,CAAE,KAAM,QAAS,EACjB,CAAE,KAAM,SAAU,EAClB,CAAE,KAAM,MAAO,CACjB,CACF,EAEMC,EAAyB,CAC7B,IAAK,gEACL,KAAM,SACN,qBAAsB,CAAE,KAAM,2BAA4B,EAC1D,MAAO,CACL,WAAYD,EACZ,SAAU,CACR,KAAM,SACN,qBAAsB,CACpB,MAAO,CACL,CAAE,KAAM,oBAAqB,EAC7B,CAAE,KAAM,kBAAmB,CAC7B,CACF,CACF,EACA,QAAS,CACP,KAAM,SACN,qBAAsB,CACpB,MAAO,CACL,CAAE,KAAM,oBAAqB,EAC7B,CAAE,KAAM,iBAAkB,CAC5B,CACF,CACF,EACA,kBAAmB,CACjB,KAAM,SACN,qBAAsB,GACtB,WAAY,CAEV,KAAM,CAAE,MAAO,CAAC,CAAE,KAAM,QAAS,EAAG,CAAE,KAAM,MAAO,CAAC,CAAE,EAKtD,SAAU,CACR,KAAM,SACN,qBAAsB,CAAE,KAAM,kBAAmB,CACnD,EAIA,QAAS,CAAE,KAAM,kBAAmB,EACpC,KAAM,CAAE,KAAM,kBAAmB,EACjC,MAAO,CAAE,KAAM,kBAAmB,EAClC,aAAc,CAAE,KAAM,kBAAmB,EACzC,MAAO,CAAE,KAAM,kBAAmB,EAClC,UAAW,CAAE,KAAM,kBAAmB,EAItC,MAAO,CACL,KAAM,SACN,qBAAsB,CAAE,KAAM,2BAA4B,CAC5D,EAGA,KAAM,CAAE,KAAM,2BAA4B,EAE1C,OAAQ,CAAE,KAAM,kBAAmB,EACnC,iBAAkB,CAAE,KAAM,kBAAmB,EAC7C,gBAAiB,CAAE,KAAM,iBAAkB,EAC3C,MAAO,CAAE,KAAM,iBAAkB,CACnC,CACF,CACF,CACF,EAEA,SAASE,EAAiBC,EAAO,CAO/B,GANI,OAAOA,GAAU,UAEjBA,EAAM,KAAK,EAAE,SAAW,GAExB,WAAW,KAAKA,CAAK,GAErBA,IAAU,QAAUA,IAAU,QAAS,MAAO,GAElD,IAAMC,EAAY,KAAK,KAAKD,CAAK,EAC3BE,EAAoB,aAAa,KAAKF,CAAK,EAEjD,MAA2C,EAG7C,CAEAJ,EAAO,QAAU,CACf,uBAAAE,EACA,iBAAAC,CACF,ICtGA,GAAM,CACJ,uBAAAI,EACA,iBAAAC,CACF,EAAI,IAEJ,SAASC,EAAKC,EAAQ,CACpB,GAAI,CAACA,EACH,MAAO,CAAC,EAGV,GAAI,OAAOA,GAAW,SACpB,MAAM,IAAI,MAAM,0BAA0B,EAG5C,OAAOA,CACT,CAEAD,EAAK,uBAAyBF,EAC9BE,EAAK,iBAAmBD,EAExB,OAAO,QAAUC",
|
|
6
|
+
"names": ["require_components_config_schema", "__commonJSMin", "exports", "module", "classValueSchema", "componentsConfigSchema", "isCSSClassString", "value", "hasSpaces", "hasTailwindSyntax", "componentsConfigSchema", "isCSSClassString", "ffdc", "config"]
|
|
7
7
|
}
|