@fragments-sdk/cli 0.15.0 → 0.15.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/{ai-client-I6MDWNYA.js → ai-client-LSLQGOMM.js} +1 -2
- package/dist/bin.js +565 -548
- package/dist/bin.js.map +1 -1
- package/dist/chunk-5JF26E55.js +1255 -0
- package/dist/chunk-5JF26E55.js.map +1 -0
- package/dist/{chunk-XJQ5BIWI.js → chunk-6SQPP47U.js} +30 -314
- package/dist/chunk-6SQPP47U.js.map +1 -0
- package/dist/{chunk-65WSVDV5.js → chunk-HQ6A6DTV.js} +1386 -1097
- package/dist/chunk-HQ6A6DTV.js.map +1 -0
- package/dist/chunk-MHIBEEW4.js +511 -0
- package/dist/chunk-MHIBEEW4.js.map +1 -0
- package/dist/{chunk-CZD3AD4Q.js → chunk-ONUP6Z4W.js} +17 -6
- package/dist/chunk-ONUP6Z4W.js.map +1 -0
- package/dist/{codebase-scanner-VOTPXRYW.js → codebase-scanner-MQHUZC2G.js} +1 -2
- package/dist/{converter-JLINP7CJ.js → converter-7XM3Y6NJ.js} +1 -2
- package/dist/{converter-JLINP7CJ.js.map → converter-7XM3Y6NJ.js.map} +1 -1
- package/dist/core/index.js +0 -1
- package/dist/create-JVAU3YKN.js +852 -0
- package/dist/create-JVAU3YKN.js.map +1 -0
- package/dist/doctor-BDPMYYE6.js +385 -0
- package/dist/doctor-BDPMYYE6.js.map +1 -0
- package/dist/{generate-A4FP5426.js → generate-PVOLUAAC.js} +3 -4
- package/dist/{generate-A4FP5426.js.map → generate-PVOLUAAC.js.map} +1 -1
- package/dist/{govern-scan-UCBZR6D6.js → govern-scan-OYFZYOQW.js} +142 -9
- package/dist/govern-scan-OYFZYOQW.js.map +1 -0
- package/dist/index.d.ts +2 -22
- package/dist/index.js +8 -7
- package/dist/index.js.map +1 -1
- package/dist/{init-HGSM35XA.js → init-SSGUSP7Z.js} +3 -4
- package/dist/{init-HGSM35XA.js.map → init-SSGUSP7Z.js.map} +1 -1
- package/dist/{init-cloud-MQ6GRJAZ.js → init-cloud-3DNKPWFB.js} +29 -4
- package/dist/{init-cloud-MQ6GRJAZ.js.map → init-cloud-3DNKPWFB.js.map} +1 -1
- package/dist/mcp-bin.js +1 -2
- package/dist/mcp-bin.js.map +1 -1
- package/dist/node-37AUE74M.js +65 -0
- package/dist/push-contracts-WY32TFP6.js +84 -0
- package/dist/push-contracts-WY32TFP6.js.map +1 -0
- package/dist/{scan-VNNKACG2.js → scan-PKSYSTRR.js} +5 -5
- package/dist/{scan-generate-TWRHNU5M.js → scan-generate-VY27PIOX.js} +8 -9
- package/dist/scan-generate-VY27PIOX.js.map +1 -0
- package/dist/{scanner-7LAZYPWZ.js → scanner-4KZNOXAK.js} +1 -2
- package/dist/{service-FHQU7YS7.js → service-QJGWUIVL.js} +16 -9
- package/dist/{snapshot-KQEQ6XHL.js → snapshot-WIJMEIFT.js} +1 -2
- package/dist/{snapshot-KQEQ6XHL.js.map → snapshot-WIJMEIFT.js.map} +1 -1
- package/dist/{static-viewer-63PG6FWY.js → static-viewer-7QIBQZRC.js} +1 -2
- package/dist/{test-UQYUCZIS.js → test-64Z5BKBA.js} +2 -3
- package/dist/{test-UQYUCZIS.js.map → test-64Z5BKBA.js.map} +1 -1
- package/dist/token-normalizer-TEPOVBPV.js +312 -0
- package/dist/token-normalizer-TEPOVBPV.js.map +1 -0
- package/dist/token-parser-32KOIOFN.js +22 -0
- package/dist/token-parser-32KOIOFN.js.map +1 -0
- package/dist/{tokens-6GYKDV6U.js → tokens-NZWFQIAB.js} +7 -7
- package/dist/{tokens-generate-VTZV5EEW.js → tokens-generate-5JQSJ27E.js} +1 -2
- package/dist/{tokens-generate-VTZV5EEW.js.map → tokens-generate-5JQSJ27E.js.map} +1 -1
- package/dist/tokens-push-HY3KO36V.js +148 -0
- package/dist/tokens-push-HY3KO36V.js.map +1 -0
- package/package.json +18 -16
- package/src/bin.ts +94 -1
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.contract.json +1 -1
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.contract.json +1 -1
- package/src/commands/__tests__/build-freshness.test.ts +231 -0
- package/src/commands/__tests__/create.test.ts +71 -0
- package/src/commands/__tests__/drift-sync.test.ts +1 -1
- package/src/commands/__tests__/govern.test.ts +258 -0
- package/src/commands/__tests__/init.test.ts +9 -1
- package/src/commands/__tests__/scan-generate.test.ts +1 -1
- package/src/commands/build.ts +54 -1
- package/src/commands/context.ts +1 -1
- package/src/commands/create.ts +590 -0
- package/src/commands/doctor.ts +3 -2
- package/src/commands/govern-scan.ts +187 -8
- package/src/commands/govern.ts +65 -2
- package/src/commands/init-cloud.ts +32 -4
- package/src/commands/push-contracts.ts +112 -0
- package/src/commands/scan-generate.ts +1 -1
- package/src/commands/scan.ts +13 -0
- package/src/commands/sync.ts +2 -2
- package/src/commands/tokens-push.ts +199 -0
- package/src/core/__tests__/token-resolver.test.ts +1 -1
- package/src/core/component-extractor.test.ts +1 -1
- package/src/core/drift-verifier.ts +1 -1
- package/src/core/extractor-adapter.ts +1 -1
- package/src/index.ts +3 -3
- package/src/migrate/fragment-to-contract.ts +2 -2
- package/src/service/index.ts +8 -0
- package/src/service/tailwind-v4-parser.ts +314 -0
- package/src/service/token-parser.ts +56 -0
- package/src/setup.ts +10 -39
- package/src/theme/__tests__/component-contrast.test.ts +2 -2
- package/src/theme/__tests__/serializer.test.ts +1 -1
- package/src/theme/generator.ts +30 -1
- package/src/theme/schema.ts +8 -0
- package/src/theme/serializer.ts +13 -9
- package/src/theme/types.ts +8 -0
- package/src/validators.ts +1 -2
- package/dist/chunk-65WSVDV5.js.map +0 -1
- package/dist/chunk-7WHVW72L.js +0 -2664
- package/dist/chunk-7WHVW72L.js.map +0 -1
- package/dist/chunk-CZD3AD4Q.js.map +0 -1
- package/dist/chunk-MN3TJ3D5.js +0 -695
- package/dist/chunk-MN3TJ3D5.js.map +0 -1
- package/dist/chunk-XJQ5BIWI.js.map +0 -1
- package/dist/chunk-Z7EY4VHE.js +0 -50
- package/dist/govern-scan-UCBZR6D6.js.map +0 -1
- package/dist/sass.node-4XJK6YBF.js +0 -130708
- package/dist/sass.node-4XJK6YBF.js.map +0 -1
- package/dist/scan-generate-TWRHNU5M.js.map +0 -1
- package/src/build.ts +0 -736
- package/src/core/auto-props.ts +0 -464
- package/src/core/component-extractor.ts +0 -1121
- package/src/core/token-resolver.ts +0 -155
- package/src/viewer/preview-adapter.ts +0 -116
- /package/dist/{ai-client-I6MDWNYA.js.map → ai-client-LSLQGOMM.js.map} +0 -0
- /package/dist/{chunk-Z7EY4VHE.js.map → codebase-scanner-MQHUZC2G.js.map} +0 -0
- /package/dist/{codebase-scanner-VOTPXRYW.js.map → node-37AUE74M.js.map} +0 -0
- /package/dist/{scan-VNNKACG2.js.map → scan-PKSYSTRR.js.map} +0 -0
- /package/dist/{scanner-7LAZYPWZ.js.map → scanner-4KZNOXAK.js.map} +0 -0
- /package/dist/{service-FHQU7YS7.js.map → service-QJGWUIVL.js.map} +0 -0
- /package/dist/{static-viewer-63PG6FWY.js.map → static-viewer-7QIBQZRC.js.map} +0 -0
- /package/dist/{tokens-6GYKDV6U.js.map → tokens-NZWFQIAB.js.map} +0 -0
|
@@ -0,0 +1,852 @@
|
|
|
1
|
+
import { createRequire as __banner_createRequire } from 'module'; const require = __banner_createRequire(import.meta.url);
|
|
2
|
+
import "./chunk-D2CDBRNU.js";
|
|
3
|
+
import {
|
|
4
|
+
BRAND
|
|
5
|
+
} from "./chunk-32LIWN2P.js";
|
|
6
|
+
|
|
7
|
+
// src/commands/create.ts
|
|
8
|
+
import { execSync } from "child_process";
|
|
9
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, unlinkSync } from "fs";
|
|
10
|
+
import { join as join2, resolve } from "path";
|
|
11
|
+
import pc from "picocolors";
|
|
12
|
+
|
|
13
|
+
// src/theme/serializer.ts
|
|
14
|
+
import { deflateSync, inflateSync } from "zlib";
|
|
15
|
+
|
|
16
|
+
// src/theme/schema.ts
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
var HEX_COLOR_REGEX = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
|
|
19
|
+
var RGB_COLOR_REGEX = /^rgba?\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*(,\s*(0|1|0?\.\d+))?\s*\)$/;
|
|
20
|
+
var HSL_COLOR_REGEX = /^hsla?\(\s*\d{1,3}\s*,\s*\d{1,3}%\s*,\s*\d{1,3}%\s*(,\s*(0|1|0?\.\d+))?\s*\)$/;
|
|
21
|
+
var colorSchema = z.string().refine(
|
|
22
|
+
(val) => {
|
|
23
|
+
return HEX_COLOR_REGEX.test(val) || RGB_COLOR_REGEX.test(val) || HSL_COLOR_REGEX.test(val);
|
|
24
|
+
},
|
|
25
|
+
{ message: "Invalid color format. Use hex (#rrggbb), rgb(), rgba(), hsl(), or hsla()" }
|
|
26
|
+
);
|
|
27
|
+
var sizeSchema = z.string().refine(
|
|
28
|
+
(val) => {
|
|
29
|
+
return /^-?\d*\.?\d+(px|rem|em|%|vw|vh)?$/.test(val);
|
|
30
|
+
},
|
|
31
|
+
{ message: "Invalid size format. Use px, rem, em, %, vw, or vh" }
|
|
32
|
+
);
|
|
33
|
+
var shadowSchema = z.string();
|
|
34
|
+
var fontStackSchema = z.string();
|
|
35
|
+
var fontWeightSchema = z.number().int().min(100).max(900);
|
|
36
|
+
var themeColorsSchema = z.object({
|
|
37
|
+
accent: colorSchema.optional(),
|
|
38
|
+
accentHover: colorSchema.optional(),
|
|
39
|
+
accentActive: colorSchema.optional(),
|
|
40
|
+
danger: colorSchema.optional(),
|
|
41
|
+
dangerHover: colorSchema.optional(),
|
|
42
|
+
success: colorSchema.optional(),
|
|
43
|
+
warning: colorSchema.optional(),
|
|
44
|
+
info: colorSchema.optional(),
|
|
45
|
+
dangerBg: colorSchema.optional(),
|
|
46
|
+
successBg: colorSchema.optional(),
|
|
47
|
+
warningBg: colorSchema.optional(),
|
|
48
|
+
infoBg: colorSchema.optional()
|
|
49
|
+
}).strict();
|
|
50
|
+
var themeSurfacesSchema = z.object({
|
|
51
|
+
bgPrimary: colorSchema.optional(),
|
|
52
|
+
bgSecondary: colorSchema.optional(),
|
|
53
|
+
bgTertiary: colorSchema.optional(),
|
|
54
|
+
bgElevated: colorSchema.optional(),
|
|
55
|
+
bgHover: colorSchema.optional(),
|
|
56
|
+
bgActive: colorSchema.optional()
|
|
57
|
+
}).strict();
|
|
58
|
+
var themeTextSchema = z.object({
|
|
59
|
+
primary: colorSchema.optional(),
|
|
60
|
+
secondary: colorSchema.optional(),
|
|
61
|
+
tertiary: colorSchema.optional(),
|
|
62
|
+
inverse: colorSchema.optional()
|
|
63
|
+
}).strict();
|
|
64
|
+
var themeBordersSchema = z.object({
|
|
65
|
+
default: colorSchema.optional(),
|
|
66
|
+
strong: colorSchema.optional()
|
|
67
|
+
}).strict();
|
|
68
|
+
var themeTypographySchema = z.object({
|
|
69
|
+
fontSans: fontStackSchema.optional(),
|
|
70
|
+
fontMono: fontStackSchema.optional(),
|
|
71
|
+
fontWeightNormal: fontWeightSchema.optional(),
|
|
72
|
+
fontWeightMedium: fontWeightSchema.optional(),
|
|
73
|
+
fontWeightSemibold: fontWeightSchema.optional()
|
|
74
|
+
}).strict();
|
|
75
|
+
var themeRadiusSchema = z.object({
|
|
76
|
+
sm: sizeSchema.optional(),
|
|
77
|
+
md: sizeSchema.optional(),
|
|
78
|
+
lg: sizeSchema.optional(),
|
|
79
|
+
full: sizeSchema.optional()
|
|
80
|
+
}).strict();
|
|
81
|
+
var themeShadowsSchema = z.object({
|
|
82
|
+
sm: shadowSchema.optional(),
|
|
83
|
+
md: shadowSchema.optional()
|
|
84
|
+
}).strict();
|
|
85
|
+
var themeDarkModeSchema = z.object({
|
|
86
|
+
surfaces: themeSurfacesSchema.optional(),
|
|
87
|
+
text: themeTextSchema.optional(),
|
|
88
|
+
borders: themeBordersSchema.optional(),
|
|
89
|
+
shadows: themeShadowsSchema.optional(),
|
|
90
|
+
accent: colorSchema.optional(),
|
|
91
|
+
accentHover: colorSchema.optional(),
|
|
92
|
+
accentActive: colorSchema.optional(),
|
|
93
|
+
dangerBg: colorSchema.optional(),
|
|
94
|
+
successBg: colorSchema.optional(),
|
|
95
|
+
warningBg: colorSchema.optional(),
|
|
96
|
+
infoBg: colorSchema.optional(),
|
|
97
|
+
backdrop: colorSchema.optional(),
|
|
98
|
+
dangerText: colorSchema.optional(),
|
|
99
|
+
successText: colorSchema.optional(),
|
|
100
|
+
warningText: colorSchema.optional(),
|
|
101
|
+
infoText: colorSchema.optional()
|
|
102
|
+
}).strict();
|
|
103
|
+
var themeConfigSchema = z.object({
|
|
104
|
+
name: z.string().min(1, "Theme name is required"),
|
|
105
|
+
version: z.string().optional(),
|
|
106
|
+
extends: z.string().optional(),
|
|
107
|
+
colors: themeColorsSchema.optional(),
|
|
108
|
+
surfaces: themeSurfacesSchema.optional(),
|
|
109
|
+
text: themeTextSchema.optional(),
|
|
110
|
+
borders: themeBordersSchema.optional(),
|
|
111
|
+
typography: themeTypographySchema.optional(),
|
|
112
|
+
radius: themeRadiusSchema.optional(),
|
|
113
|
+
shadows: themeShadowsSchema.optional(),
|
|
114
|
+
dark: themeDarkModeSchema.optional(),
|
|
115
|
+
density: z.enum(["compact", "default", "relaxed"]).optional()
|
|
116
|
+
}).strict();
|
|
117
|
+
function validateThemeConfig(config) {
|
|
118
|
+
const result = themeConfigSchema.safeParse(config);
|
|
119
|
+
if (result.success) {
|
|
120
|
+
return { success: true, data: result.data };
|
|
121
|
+
}
|
|
122
|
+
return { success: false, error: result.error };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/theme/serializer.ts
|
|
126
|
+
function fromBase64Url(str) {
|
|
127
|
+
let base64 = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
128
|
+
const pad = base64.length % 4;
|
|
129
|
+
if (pad) {
|
|
130
|
+
base64 += "=".repeat(4 - pad);
|
|
131
|
+
}
|
|
132
|
+
return Buffer.from(base64, "base64");
|
|
133
|
+
}
|
|
134
|
+
function decompressTheme(encoded) {
|
|
135
|
+
try {
|
|
136
|
+
const buffer = fromBase64Url(encoded);
|
|
137
|
+
const decompressed = inflateSync(buffer);
|
|
138
|
+
const json = decompressed.toString("utf-8");
|
|
139
|
+
const parsed = JSON.parse(json);
|
|
140
|
+
const result = validateThemeConfig(parsed);
|
|
141
|
+
if (result.success) return result.data;
|
|
142
|
+
} catch {
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
const buffer = fromBase64Url(encoded);
|
|
146
|
+
const json = buffer.toString("utf-8");
|
|
147
|
+
const parsed = JSON.parse(json);
|
|
148
|
+
const result = validateThemeConfig(parsed);
|
|
149
|
+
if (result.success) return result.data;
|
|
150
|
+
} catch {
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// src/theme/generator.ts
|
|
156
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
157
|
+
import { join } from "path";
|
|
158
|
+
var TOKEN_MAPPINGS = {
|
|
159
|
+
colors: {
|
|
160
|
+
accent: "fui-color-accent",
|
|
161
|
+
accentHover: "fui-color-accent-hover",
|
|
162
|
+
accentActive: "fui-color-accent-active",
|
|
163
|
+
danger: "fui-color-danger",
|
|
164
|
+
dangerHover: "fui-color-danger-hover",
|
|
165
|
+
success: "fui-color-success",
|
|
166
|
+
warning: "fui-color-warning",
|
|
167
|
+
info: "fui-color-info",
|
|
168
|
+
dangerBg: "fui-color-danger-bg",
|
|
169
|
+
successBg: "fui-color-success-bg",
|
|
170
|
+
warningBg: "fui-color-warning-bg",
|
|
171
|
+
infoBg: "fui-color-info-bg",
|
|
172
|
+
dangerText: "fui-color-danger-text",
|
|
173
|
+
successText: "fui-color-success-text",
|
|
174
|
+
warningText: "fui-color-warning-text",
|
|
175
|
+
infoText: "fui-color-info-text"
|
|
176
|
+
},
|
|
177
|
+
surfaces: {
|
|
178
|
+
bgPrimary: "fui-bg-primary",
|
|
179
|
+
bgSecondary: "fui-bg-secondary",
|
|
180
|
+
bgTertiary: "fui-bg-tertiary",
|
|
181
|
+
bgElevated: "fui-bg-elevated",
|
|
182
|
+
bgHover: "fui-bg-hover",
|
|
183
|
+
bgActive: "fui-bg-active"
|
|
184
|
+
},
|
|
185
|
+
text: {
|
|
186
|
+
primary: "fui-text-primary",
|
|
187
|
+
secondary: "fui-text-secondary",
|
|
188
|
+
tertiary: "fui-text-tertiary",
|
|
189
|
+
inverse: "fui-text-inverse"
|
|
190
|
+
},
|
|
191
|
+
borders: {
|
|
192
|
+
default: "fui-border",
|
|
193
|
+
strong: "fui-border-strong"
|
|
194
|
+
},
|
|
195
|
+
typography: {
|
|
196
|
+
fontSans: "fui-font-sans",
|
|
197
|
+
fontMono: "fui-font-mono",
|
|
198
|
+
fontWeightNormal: "fui-font-weight-normal",
|
|
199
|
+
fontWeightMedium: "fui-font-weight-medium",
|
|
200
|
+
fontWeightSemibold: "fui-font-weight-semibold"
|
|
201
|
+
},
|
|
202
|
+
radius: {
|
|
203
|
+
sm: "fui-radius-sm",
|
|
204
|
+
md: "fui-radius-md",
|
|
205
|
+
lg: "fui-radius-lg",
|
|
206
|
+
full: "fui-radius-full"
|
|
207
|
+
},
|
|
208
|
+
shadows: {
|
|
209
|
+
sm: "fui-shadow-sm",
|
|
210
|
+
md: "fui-shadow-md"
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
var DARK_TOKEN_MAPPINGS = {
|
|
214
|
+
surfaces: {
|
|
215
|
+
bgPrimary: "fui-dark-bg-primary",
|
|
216
|
+
bgSecondary: "fui-dark-bg-secondary",
|
|
217
|
+
bgTertiary: "fui-dark-bg-tertiary",
|
|
218
|
+
bgElevated: "fui-dark-bg-elevated",
|
|
219
|
+
bgHover: "fui-dark-bg-hover",
|
|
220
|
+
bgActive: "fui-dark-bg-active"
|
|
221
|
+
},
|
|
222
|
+
text: {
|
|
223
|
+
primary: "fui-dark-text-primary",
|
|
224
|
+
secondary: "fui-dark-text-secondary",
|
|
225
|
+
tertiary: "fui-dark-text-tertiary",
|
|
226
|
+
inverse: "fui-dark-text-inverse"
|
|
227
|
+
},
|
|
228
|
+
borders: {
|
|
229
|
+
default: "fui-dark-border",
|
|
230
|
+
strong: "fui-dark-border-strong"
|
|
231
|
+
},
|
|
232
|
+
shadows: {
|
|
233
|
+
sm: "fui-dark-shadow-sm",
|
|
234
|
+
md: "fui-dark-shadow-md"
|
|
235
|
+
},
|
|
236
|
+
// Direct dark mode properties
|
|
237
|
+
accent: "fui-dark-color-accent",
|
|
238
|
+
accentHover: "fui-dark-color-accent-hover",
|
|
239
|
+
accentActive: "fui-dark-color-accent-active",
|
|
240
|
+
dangerBg: "fui-dark-color-danger-bg",
|
|
241
|
+
successBg: "fui-dark-color-success-bg",
|
|
242
|
+
warningBg: "fui-dark-color-warning-bg",
|
|
243
|
+
infoBg: "fui-dark-color-info-bg",
|
|
244
|
+
dangerText: "fui-dark-color-danger-text",
|
|
245
|
+
successText: "fui-dark-color-success-text",
|
|
246
|
+
warningText: "fui-dark-color-warning-text",
|
|
247
|
+
infoText: "fui-dark-color-info-text",
|
|
248
|
+
backdrop: "fui-dark-backdrop"
|
|
249
|
+
};
|
|
250
|
+
function generateCategoryTokens(config, categoryKey, format) {
|
|
251
|
+
const category = config[categoryKey];
|
|
252
|
+
if (!category) return [];
|
|
253
|
+
const mappings = TOKEN_MAPPINGS[categoryKey];
|
|
254
|
+
const tokens = [];
|
|
255
|
+
for (const [key, varName] of Object.entries(mappings)) {
|
|
256
|
+
const value = category[key];
|
|
257
|
+
if (value !== void 0) {
|
|
258
|
+
if (format === "scss") {
|
|
259
|
+
tokens.push(`$${varName}: ${value} !default;`);
|
|
260
|
+
} else {
|
|
261
|
+
tokens.push(` --${varName}: ${value};`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return tokens;
|
|
266
|
+
}
|
|
267
|
+
function generateDarkTokens(config, format) {
|
|
268
|
+
if (!config.dark) return [];
|
|
269
|
+
const tokens = [];
|
|
270
|
+
const nestedCategories = ["surfaces", "text", "borders", "shadows"];
|
|
271
|
+
for (const category of nestedCategories) {
|
|
272
|
+
const categoryData = config.dark[category];
|
|
273
|
+
if (!categoryData) continue;
|
|
274
|
+
const mappings = DARK_TOKEN_MAPPINGS[category];
|
|
275
|
+
for (const [key, varName] of Object.entries(mappings)) {
|
|
276
|
+
const value = categoryData[key];
|
|
277
|
+
if (value !== void 0) {
|
|
278
|
+
if (format === "scss") {
|
|
279
|
+
tokens.push(`$${varName}: ${value} !default;`);
|
|
280
|
+
} else {
|
|
281
|
+
tokens.push(` --${varName.replace("fui-dark-", "fui-")}: ${value};`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const directProps = ["accent", "accentHover", "accentActive", "dangerBg", "successBg", "warningBg", "infoBg", "dangerText", "successText", "warningText", "infoText", "backdrop"];
|
|
287
|
+
for (const prop of directProps) {
|
|
288
|
+
const value = config.dark[prop];
|
|
289
|
+
if (value !== void 0) {
|
|
290
|
+
const varName = DARK_TOKEN_MAPPINGS[prop];
|
|
291
|
+
if (format === "scss") {
|
|
292
|
+
tokens.push(`$${varName}: ${value} !default;`);
|
|
293
|
+
} else {
|
|
294
|
+
const cssVarName = varName.replace("fui-dark-color-", "fui-color-").replace("fui-dark-", "fui-");
|
|
295
|
+
tokens.push(` --${cssVarName}: ${value};`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return tokens;
|
|
300
|
+
}
|
|
301
|
+
function generateScssTokens(config) {
|
|
302
|
+
const lines = [];
|
|
303
|
+
lines.push("// Auto-generated by @fragments-sdk/cli");
|
|
304
|
+
lines.push(`// Theme: ${config.name}`);
|
|
305
|
+
if (config.version) {
|
|
306
|
+
lines.push(`// Version: ${config.version}`);
|
|
307
|
+
}
|
|
308
|
+
lines.push("");
|
|
309
|
+
const colorTokens = generateCategoryTokens(config, "colors", "scss");
|
|
310
|
+
if (colorTokens.length > 0) {
|
|
311
|
+
lines.push("// Colors");
|
|
312
|
+
lines.push(...colorTokens);
|
|
313
|
+
lines.push("");
|
|
314
|
+
}
|
|
315
|
+
const surfaceTokens = generateCategoryTokens(config, "surfaces", "scss");
|
|
316
|
+
if (surfaceTokens.length > 0) {
|
|
317
|
+
lines.push("// Surfaces");
|
|
318
|
+
lines.push(...surfaceTokens);
|
|
319
|
+
lines.push("");
|
|
320
|
+
}
|
|
321
|
+
const textTokens = generateCategoryTokens(config, "text", "scss");
|
|
322
|
+
if (textTokens.length > 0) {
|
|
323
|
+
lines.push("// Text");
|
|
324
|
+
lines.push(...textTokens);
|
|
325
|
+
lines.push("");
|
|
326
|
+
}
|
|
327
|
+
const borderTokens = generateCategoryTokens(config, "borders", "scss");
|
|
328
|
+
if (borderTokens.length > 0) {
|
|
329
|
+
lines.push("// Borders");
|
|
330
|
+
lines.push(...borderTokens);
|
|
331
|
+
if (config.borders?.default) {
|
|
332
|
+
lines.push(`$fui-border-default: ${config.borders.default} !default;`);
|
|
333
|
+
}
|
|
334
|
+
lines.push("");
|
|
335
|
+
}
|
|
336
|
+
const typographyTokens = generateCategoryTokens(config, "typography", "scss");
|
|
337
|
+
if (typographyTokens.length > 0) {
|
|
338
|
+
lines.push("// Typography");
|
|
339
|
+
lines.push(...typographyTokens);
|
|
340
|
+
lines.push("");
|
|
341
|
+
}
|
|
342
|
+
const radiusTokens = generateCategoryTokens(config, "radius", "scss");
|
|
343
|
+
if (radiusTokens.length > 0) {
|
|
344
|
+
lines.push("// Border Radius");
|
|
345
|
+
lines.push(...radiusTokens);
|
|
346
|
+
lines.push("");
|
|
347
|
+
}
|
|
348
|
+
const shadowTokens = generateCategoryTokens(config, "shadows", "scss");
|
|
349
|
+
if (shadowTokens.length > 0) {
|
|
350
|
+
lines.push("// Shadows");
|
|
351
|
+
lines.push(...shadowTokens);
|
|
352
|
+
lines.push("");
|
|
353
|
+
}
|
|
354
|
+
if (config.density) {
|
|
355
|
+
lines.push("// Density");
|
|
356
|
+
lines.push(`$fui-density: "${config.density}" !default;`);
|
|
357
|
+
lines.push("");
|
|
358
|
+
}
|
|
359
|
+
const darkTokens = generateDarkTokens(config, "scss");
|
|
360
|
+
if (darkTokens.length > 0) {
|
|
361
|
+
lines.push("// Dark Mode");
|
|
362
|
+
lines.push(...darkTokens);
|
|
363
|
+
lines.push("");
|
|
364
|
+
}
|
|
365
|
+
return lines.join("\n");
|
|
366
|
+
}
|
|
367
|
+
function generateCssTokens(config) {
|
|
368
|
+
const lines = [];
|
|
369
|
+
lines.push("/* Auto-generated by @fragments-sdk/cli */");
|
|
370
|
+
lines.push(`/* Theme: ${config.name} */`);
|
|
371
|
+
if (config.version) {
|
|
372
|
+
lines.push(`/* Version: ${config.version} */`);
|
|
373
|
+
}
|
|
374
|
+
lines.push("");
|
|
375
|
+
const lightTokens = [];
|
|
376
|
+
const categories = ["colors", "surfaces", "text", "borders", "typography", "radius", "shadows"];
|
|
377
|
+
for (const category of categories) {
|
|
378
|
+
const tokens = generateCategoryTokens(config, category, "css");
|
|
379
|
+
lightTokens.push(...tokens);
|
|
380
|
+
}
|
|
381
|
+
if (config.borders?.default) {
|
|
382
|
+
lightTokens.push(` --fui-border-default: ${config.borders.default};`);
|
|
383
|
+
}
|
|
384
|
+
if (config.density) {
|
|
385
|
+
lightTokens.push(` --fui-density: ${config.density};`);
|
|
386
|
+
}
|
|
387
|
+
if (lightTokens.length > 0) {
|
|
388
|
+
lines.push(":root {");
|
|
389
|
+
lines.push(...lightTokens);
|
|
390
|
+
lines.push("}");
|
|
391
|
+
lines.push("");
|
|
392
|
+
}
|
|
393
|
+
const darkTokens = generateDarkTokens(config, "css");
|
|
394
|
+
if (config.dark?.borders?.default) {
|
|
395
|
+
darkTokens.push(` --fui-border-default: ${config.dark.borders.default};`);
|
|
396
|
+
}
|
|
397
|
+
if (darkTokens.length > 0) {
|
|
398
|
+
lines.push(":root.dark,");
|
|
399
|
+
lines.push(':root[data-theme="dark"] {');
|
|
400
|
+
lines.push(...darkTokens);
|
|
401
|
+
lines.push("}");
|
|
402
|
+
lines.push("");
|
|
403
|
+
}
|
|
404
|
+
return lines.join("\n");
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// src/commands/create.ts
|
|
408
|
+
function extractFontFamily(cssFontStack) {
|
|
409
|
+
const match = cssFontStack.match(/^["']?([^"',]+)["']?/);
|
|
410
|
+
if (!match) return null;
|
|
411
|
+
const name = match[1].trim();
|
|
412
|
+
const generics = ["sans-serif", "serif", "monospace", "cursive", "fantasy", "system-ui"];
|
|
413
|
+
if (generics.includes(name.toLowerCase())) return null;
|
|
414
|
+
if (name.toLowerCase() === "inter") return null;
|
|
415
|
+
return name;
|
|
416
|
+
}
|
|
417
|
+
function googleFontsUrl(familyName) {
|
|
418
|
+
return `https://fonts.googleapis.com/css2?family=${encodeURIComponent(familyName)}:wght@400;500;600;700&display=swap`;
|
|
419
|
+
}
|
|
420
|
+
function detectPackageManager() {
|
|
421
|
+
const agent = process.env.npm_config_user_agent || "";
|
|
422
|
+
if (agent.startsWith("pnpm")) return "pnpm";
|
|
423
|
+
if (agent.startsWith("yarn")) return "yarn";
|
|
424
|
+
if (agent.startsWith("bun")) return "bun";
|
|
425
|
+
return "npm";
|
|
426
|
+
}
|
|
427
|
+
function getInstallCommand(pm) {
|
|
428
|
+
return pm === "yarn" ? "yarn add" : `${pm} add`;
|
|
429
|
+
}
|
|
430
|
+
function getDevInstallCommand(pm) {
|
|
431
|
+
return pm === "yarn" ? "yarn add -D" : `${pm} add -D`;
|
|
432
|
+
}
|
|
433
|
+
function getRunCommand(pm) {
|
|
434
|
+
return pm === "npm" ? "npm run" : pm;
|
|
435
|
+
}
|
|
436
|
+
function isValidProjectName(name) {
|
|
437
|
+
return /^[a-z0-9][a-z0-9._-]*$/.test(name);
|
|
438
|
+
}
|
|
439
|
+
var PRESET_API_URL = "https://canny-otter-874.convex.site/api/theme-presets";
|
|
440
|
+
async function fetchPreset(presetId) {
|
|
441
|
+
try {
|
|
442
|
+
const res = await fetch(`${PRESET_API_URL}?id=${encodeURIComponent(presetId)}`);
|
|
443
|
+
if (!res.ok) return null;
|
|
444
|
+
const theme = await res.json();
|
|
445
|
+
if (!theme || !theme.name) return null;
|
|
446
|
+
return theme;
|
|
447
|
+
} catch {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
async function resolveTheme(options) {
|
|
452
|
+
if (options.preset) {
|
|
453
|
+
console.log(pc.dim(` Fetching theme preset ${options.preset}...`));
|
|
454
|
+
const theme = await fetchPreset(options.preset);
|
|
455
|
+
if (!theme) {
|
|
456
|
+
console.error(pc.red(`Error: Could not fetch preset "${options.preset}". It may have expired or the ID is invalid.`));
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
return theme;
|
|
460
|
+
}
|
|
461
|
+
if (options.theme) {
|
|
462
|
+
const decoded = decompressTheme(options.theme);
|
|
463
|
+
if (!decoded) {
|
|
464
|
+
console.error(pc.red("Error: Could not decode theme string. Make sure you copied it from usefragments.com/create"));
|
|
465
|
+
return null;
|
|
466
|
+
}
|
|
467
|
+
return decoded;
|
|
468
|
+
}
|
|
469
|
+
if (options.brand) {
|
|
470
|
+
return {
|
|
471
|
+
name: "custom",
|
|
472
|
+
colors: {
|
|
473
|
+
accent: options.brand
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
return {
|
|
478
|
+
name: "default",
|
|
479
|
+
colors: {
|
|
480
|
+
accent: "#6366f1"
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
function generateNextjsLayout(themePath, theme) {
|
|
485
|
+
const fontName = theme?.typography?.fontSans ? extractFontFamily(theme.typography.fontSans) : null;
|
|
486
|
+
const fontUrl = fontName ? googleFontsUrl(fontName) : null;
|
|
487
|
+
const htmlOpen = fontUrl ? ` <html lang="en" suppressHydrationWarning>
|
|
488
|
+
<head>
|
|
489
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
490
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="" />
|
|
491
|
+
<link href="${fontUrl}" rel="stylesheet" />
|
|
492
|
+
</head>
|
|
493
|
+
<body>` : ` <html lang="en" suppressHydrationWarning>
|
|
494
|
+
<body>`;
|
|
495
|
+
return `import type { Metadata } from 'next';
|
|
496
|
+
import '@fragments-sdk/ui/styles';
|
|
497
|
+
import '${themePath}';
|
|
498
|
+
import { Providers } from './providers';
|
|
499
|
+
|
|
500
|
+
export const metadata: Metadata = {
|
|
501
|
+
title: 'My App',
|
|
502
|
+
description: 'Built with Fragments UI',
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
export default function RootLayout({
|
|
506
|
+
children,
|
|
507
|
+
}: {
|
|
508
|
+
children: React.ReactNode;
|
|
509
|
+
}) {
|
|
510
|
+
return (
|
|
511
|
+
${htmlOpen}
|
|
512
|
+
<Providers>{children}</Providers>
|
|
513
|
+
</body>
|
|
514
|
+
</html>
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
`;
|
|
518
|
+
}
|
|
519
|
+
function generateNextjsProviders() {
|
|
520
|
+
return `'use client';
|
|
521
|
+
|
|
522
|
+
import type { ReactNode } from 'react';
|
|
523
|
+
import { ThemeProvider, TooltipProvider, ToastProvider } from '@fragments-sdk/ui';
|
|
524
|
+
|
|
525
|
+
export function Providers({ children }: { children: ReactNode }) {
|
|
526
|
+
return (
|
|
527
|
+
<ThemeProvider defaultMode="system">
|
|
528
|
+
<TooltipProvider>
|
|
529
|
+
<ToastProvider>{children}</ToastProvider>
|
|
530
|
+
</TooltipProvider>
|
|
531
|
+
</ThemeProvider>
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
`;
|
|
535
|
+
}
|
|
536
|
+
function generateNextjsPage() {
|
|
537
|
+
return `'use client';
|
|
538
|
+
|
|
539
|
+
import { Button, Card, Stack, Text, Input } from '@fragments-sdk/ui';
|
|
540
|
+
|
|
541
|
+
export default function Home() {
|
|
542
|
+
return (
|
|
543
|
+
<main style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
544
|
+
<Card style={{ maxWidth: 480, width: '100%' }}>
|
|
545
|
+
<Card.Header>
|
|
546
|
+
<Card.Title>Welcome to Fragments</Card.Title>
|
|
547
|
+
<Card.Description>Your app is ready. Start building something great.</Card.Description>
|
|
548
|
+
</Card.Header>
|
|
549
|
+
<Card.Body>
|
|
550
|
+
<Stack gap={3}>
|
|
551
|
+
<Input placeholder="Enter something..." />
|
|
552
|
+
<Stack direction="row" gap={2}>
|
|
553
|
+
<Button>Get Started</Button>
|
|
554
|
+
<Button variant="secondary">Learn More</Button>
|
|
555
|
+
</Stack>
|
|
556
|
+
</Stack>
|
|
557
|
+
</Card.Body>
|
|
558
|
+
</Card>
|
|
559
|
+
</main>
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
`;
|
|
563
|
+
}
|
|
564
|
+
function generateViteMain(themePath) {
|
|
565
|
+
return `import { StrictMode } from 'react';
|
|
566
|
+
import { createRoot } from 'react-dom/client';
|
|
567
|
+
import { ThemeProvider, TooltipProvider, ToastProvider } from '@fragments-sdk/ui';
|
|
568
|
+
import '@fragments-sdk/ui/styles';
|
|
569
|
+
import '${themePath}';
|
|
570
|
+
import App from './App';
|
|
571
|
+
|
|
572
|
+
createRoot(document.getElementById('root')!).render(
|
|
573
|
+
<StrictMode>
|
|
574
|
+
<ThemeProvider defaultMode="system">
|
|
575
|
+
<TooltipProvider>
|
|
576
|
+
<ToastProvider>
|
|
577
|
+
<App />
|
|
578
|
+
</ToastProvider>
|
|
579
|
+
</TooltipProvider>
|
|
580
|
+
</ThemeProvider>
|
|
581
|
+
</StrictMode>,
|
|
582
|
+
);
|
|
583
|
+
`;
|
|
584
|
+
}
|
|
585
|
+
function generateViteApp() {
|
|
586
|
+
return `import { Button, Card, Stack, Text, Input } from '@fragments-sdk/ui';
|
|
587
|
+
|
|
588
|
+
function App() {
|
|
589
|
+
return (
|
|
590
|
+
<main style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
591
|
+
<Card style={{ maxWidth: 480, width: '100%' }}>
|
|
592
|
+
<Card.Header>
|
|
593
|
+
<Card.Title>Welcome to Fragments</Card.Title>
|
|
594
|
+
<Card.Description>Your app is ready. Start building something great.</Card.Description>
|
|
595
|
+
</Card.Header>
|
|
596
|
+
<Card.Body>
|
|
597
|
+
<Stack gap={3}>
|
|
598
|
+
<Input placeholder="Enter something..." />
|
|
599
|
+
<Stack direction="row" gap={2}>
|
|
600
|
+
<Button>Get Started</Button>
|
|
601
|
+
<Button variant="secondary">Learn More</Button>
|
|
602
|
+
</Stack>
|
|
603
|
+
</Stack>
|
|
604
|
+
</Card.Body>
|
|
605
|
+
</Card>
|
|
606
|
+
</main>
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
export default App;
|
|
611
|
+
`;
|
|
612
|
+
}
|
|
613
|
+
function injectFontIntoViteHtml(projectDir, theme) {
|
|
614
|
+
const fontName = theme.typography?.fontSans ? extractFontFamily(theme.typography.fontSans) : null;
|
|
615
|
+
if (!fontName) return;
|
|
616
|
+
const indexPath = join2(projectDir, "index.html");
|
|
617
|
+
if (!existsSync(indexPath)) return;
|
|
618
|
+
let html = readFileSync(indexPath, "utf-8");
|
|
619
|
+
const fontLinks = [
|
|
620
|
+
' <link rel="preconnect" href="https://fonts.googleapis.com" />',
|
|
621
|
+
' <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />',
|
|
622
|
+
` <link href="${googleFontsUrl(fontName)}" rel="stylesheet" />`
|
|
623
|
+
].join("\n");
|
|
624
|
+
html = html.replace("</head>", `${fontLinks}
|
|
625
|
+
</head>`);
|
|
626
|
+
writeFileSync(indexPath, html, "utf-8");
|
|
627
|
+
}
|
|
628
|
+
async function promptIfMissing(options) {
|
|
629
|
+
const resolved = { ...options };
|
|
630
|
+
if (!resolved.name) {
|
|
631
|
+
if (resolved.yes) {
|
|
632
|
+
resolved.name = "my-app";
|
|
633
|
+
} else {
|
|
634
|
+
try {
|
|
635
|
+
const { input } = await import("@inquirer/prompts");
|
|
636
|
+
resolved.name = await input({
|
|
637
|
+
message: "Project name:",
|
|
638
|
+
default: "my-app",
|
|
639
|
+
validate: (val) => isValidProjectName(val) || "Use lowercase letters, numbers, hyphens, dots, underscores"
|
|
640
|
+
});
|
|
641
|
+
} catch {
|
|
642
|
+
resolved.name = "my-app";
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
if (!resolved.template && !resolved.yes) {
|
|
647
|
+
try {
|
|
648
|
+
const { select } = await import("@inquirer/prompts");
|
|
649
|
+
resolved.template = await select({
|
|
650
|
+
message: "Framework:",
|
|
651
|
+
choices: [
|
|
652
|
+
{ name: "Next.js (App Router)", value: "nextjs" },
|
|
653
|
+
{ name: "Vite + React", value: "vite" }
|
|
654
|
+
],
|
|
655
|
+
default: "nextjs"
|
|
656
|
+
});
|
|
657
|
+
} catch {
|
|
658
|
+
resolved.template = "nextjs";
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
resolved.template = resolved.template || "nextjs";
|
|
662
|
+
resolved.packageManager = resolved.packageManager || detectPackageManager();
|
|
663
|
+
return resolved;
|
|
664
|
+
}
|
|
665
|
+
function scaffoldFramework(name, template, pm) {
|
|
666
|
+
console.log(pc.cyan(`
|
|
667
|
+
Scaffolding ${template === "nextjs" ? "Next.js" : "Vite + React"} project...
|
|
668
|
+
`));
|
|
669
|
+
if (template === "nextjs") {
|
|
670
|
+
const cmd = `npx create-next-app@latest ${name} --ts --app --src-dir --no-tailwind --no-eslint --import-alias "@/*" --yes`;
|
|
671
|
+
execSync(cmd, { stdio: "inherit" });
|
|
672
|
+
} else {
|
|
673
|
+
if (pm === "bun") {
|
|
674
|
+
execSync(`bun create vite ${name} --template react-ts`, { stdio: "inherit" });
|
|
675
|
+
} else {
|
|
676
|
+
execSync(`npm create vite@latest ${name} -- --template react-ts`, { stdio: "inherit" });
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
function installDeps(projectDir, pm, scss) {
|
|
681
|
+
console.log(pc.cyan("\nInstalling Fragments UI...\n"));
|
|
682
|
+
const install = getInstallCommand(pm);
|
|
683
|
+
execSync(`${install} @fragments-sdk/ui`, { cwd: projectDir, stdio: "inherit" });
|
|
684
|
+
if (scss) {
|
|
685
|
+
const devInstall = getDevInstallCommand(pm);
|
|
686
|
+
execSync(`${devInstall} sass`, { cwd: projectDir, stdio: "inherit" });
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
function writeThemeFile(projectDir, theme, scss, template) {
|
|
690
|
+
const stylesDir = join2(projectDir, "src", "styles");
|
|
691
|
+
mkdirSync(stylesDir, { recursive: true });
|
|
692
|
+
const ext = scss ? "scss" : "css";
|
|
693
|
+
const content = scss ? generateScssTokens(theme) : generateCssTokens(theme);
|
|
694
|
+
const filePath = join2(stylesDir, `theme.${ext}`);
|
|
695
|
+
writeFileSync(filePath, content, "utf-8");
|
|
696
|
+
if (template === "nextjs") {
|
|
697
|
+
return `../styles/theme.${ext}`;
|
|
698
|
+
}
|
|
699
|
+
return `./styles/theme.${ext}`;
|
|
700
|
+
}
|
|
701
|
+
function addNextTranspilePackages(projectDir) {
|
|
702
|
+
const configCandidates = ["next.config.ts", "next.config.mjs", "next.config.js"];
|
|
703
|
+
for (const configFile of configCandidates) {
|
|
704
|
+
const fullPath = join2(projectDir, configFile);
|
|
705
|
+
if (!existsSync(fullPath)) continue;
|
|
706
|
+
const content = readFileSync(fullPath, "utf-8");
|
|
707
|
+
if (content.includes("transpilePackages") && content.includes("@fragments-sdk/ui")) {
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
if (content.includes("transpilePackages")) {
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
const patterns = [
|
|
714
|
+
{
|
|
715
|
+
search: /const\s+\w+\s*(?::\s*\w+)?\s*=\s*\{/,
|
|
716
|
+
replacement: (match) => `${match}
|
|
717
|
+
transpilePackages: ['@fragments-sdk/ui'],`
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
search: /module\.exports\s*=\s*\{/,
|
|
721
|
+
replacement: (match) => `${match}
|
|
722
|
+
transpilePackages: ['@fragments-sdk/ui'],`
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
search: /export\s+default\s*\{/,
|
|
726
|
+
replacement: (match) => `${match}
|
|
727
|
+
transpilePackages: ['@fragments-sdk/ui'],`
|
|
728
|
+
}
|
|
729
|
+
];
|
|
730
|
+
for (const pattern of patterns) {
|
|
731
|
+
if (!pattern.search.test(content)) continue;
|
|
732
|
+
writeFileSync(
|
|
733
|
+
fullPath,
|
|
734
|
+
content.replace(pattern.search, pattern.replacement),
|
|
735
|
+
"utf-8"
|
|
736
|
+
);
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
function rewriteAppFiles(projectDir, template, themePath, theme) {
|
|
743
|
+
if (template === "nextjs") {
|
|
744
|
+
const layoutPath = join2(projectDir, "src", "app", "layout.tsx");
|
|
745
|
+
writeFileSync(layoutPath, generateNextjsLayout(themePath, theme), "utf-8");
|
|
746
|
+
const providersPath = join2(projectDir, "src", "app", "providers.tsx");
|
|
747
|
+
writeFileSync(providersPath, generateNextjsProviders(), "utf-8");
|
|
748
|
+
const pagePath = join2(projectDir, "src", "app", "page.tsx");
|
|
749
|
+
writeFileSync(pagePath, generateNextjsPage(), "utf-8");
|
|
750
|
+
const moduleCssPath = join2(projectDir, "src", "app", "page.module.css");
|
|
751
|
+
try {
|
|
752
|
+
unlinkSync(moduleCssPath);
|
|
753
|
+
} catch {
|
|
754
|
+
}
|
|
755
|
+
const globalsCssPath = join2(projectDir, "src", "app", "globals.css");
|
|
756
|
+
try {
|
|
757
|
+
unlinkSync(globalsCssPath);
|
|
758
|
+
} catch {
|
|
759
|
+
}
|
|
760
|
+
addNextTranspilePackages(projectDir);
|
|
761
|
+
} else {
|
|
762
|
+
const mainPath = join2(projectDir, "src", "main.tsx");
|
|
763
|
+
writeFileSync(mainPath, generateViteMain(themePath), "utf-8");
|
|
764
|
+
const appPath = join2(projectDir, "src", "App.tsx");
|
|
765
|
+
writeFileSync(appPath, generateViteApp(), "utf-8");
|
|
766
|
+
for (const file of ["src/App.css", "src/index.css"]) {
|
|
767
|
+
try {
|
|
768
|
+
unlinkSync(join2(projectDir, file));
|
|
769
|
+
} catch {
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
injectFontIntoViteHtml(projectDir, theme);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
function initGit(projectDir) {
|
|
776
|
+
try {
|
|
777
|
+
if (!existsSync(join2(projectDir, ".git"))) {
|
|
778
|
+
execSync("git init", { cwd: projectDir, stdio: "ignore" });
|
|
779
|
+
execSync("git add -A", { cwd: projectDir, stdio: "ignore" });
|
|
780
|
+
execSync('git commit -m "Initial commit with Fragments UI"', { cwd: projectDir, stdio: "ignore" });
|
|
781
|
+
}
|
|
782
|
+
} catch {
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
function configureMcp(projectDir) {
|
|
786
|
+
const mcpConfig = {
|
|
787
|
+
mcpServers: {
|
|
788
|
+
fragments: {
|
|
789
|
+
command: "npx",
|
|
790
|
+
args: ["-y", "@fragments-sdk/cli", "mcp"]
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
const mcpDir = join2(projectDir, ".mcp");
|
|
795
|
+
mkdirSync(mcpDir, { recursive: true });
|
|
796
|
+
writeFileSync(join2(mcpDir, "config.json"), JSON.stringify(mcpConfig, null, 2), "utf-8");
|
|
797
|
+
}
|
|
798
|
+
async function create(options) {
|
|
799
|
+
console.log(pc.cyan(`
|
|
800
|
+
${BRAND.name} Create
|
|
801
|
+
`));
|
|
802
|
+
const resolved = await promptIfMissing(options);
|
|
803
|
+
const name = resolved.name;
|
|
804
|
+
const template = resolved.template;
|
|
805
|
+
const pm = resolved.packageManager;
|
|
806
|
+
if (!isValidProjectName(name)) {
|
|
807
|
+
return { success: false, error: `Invalid project name: ${name}. Use lowercase letters, numbers, hyphens, dots, underscores.` };
|
|
808
|
+
}
|
|
809
|
+
const projectDir = resolve(process.cwd(), name);
|
|
810
|
+
if (existsSync(projectDir)) {
|
|
811
|
+
return { success: false, error: `Directory "${name}" already exists.` };
|
|
812
|
+
}
|
|
813
|
+
const theme = await resolveTheme(resolved);
|
|
814
|
+
if (!theme) {
|
|
815
|
+
return { success: false, error: "Invalid theme configuration." };
|
|
816
|
+
}
|
|
817
|
+
scaffoldFramework(name, template, pm);
|
|
818
|
+
if (!existsSync(projectDir)) {
|
|
819
|
+
return { success: false, error: "Framework scaffolding failed \u2014 project directory was not created." };
|
|
820
|
+
}
|
|
821
|
+
installDeps(projectDir, pm, !!resolved.scss);
|
|
822
|
+
const themePath = writeThemeFile(projectDir, theme, !!resolved.scss, template);
|
|
823
|
+
rewriteAppFiles(projectDir, template, themePath, theme);
|
|
824
|
+
if (resolved.mcp) {
|
|
825
|
+
configureMcp(projectDir);
|
|
826
|
+
console.log(pc.dim(" Configured MCP server for AI tooling"));
|
|
827
|
+
}
|
|
828
|
+
if (!resolved.noGit) {
|
|
829
|
+
initGit(projectDir);
|
|
830
|
+
}
|
|
831
|
+
const run = getRunCommand(pm);
|
|
832
|
+
console.log("");
|
|
833
|
+
console.log(pc.green(" Project created successfully!"));
|
|
834
|
+
console.log("");
|
|
835
|
+
console.log(` ${pc.cyan("cd")} ${name}`);
|
|
836
|
+
console.log(` ${pc.cyan(run)} dev`);
|
|
837
|
+
console.log("");
|
|
838
|
+
if (theme.name !== "default") {
|
|
839
|
+
console.log(pc.dim(` Theme "${theme.name}" applied with full token set.`));
|
|
840
|
+
console.log(pc.dim(` Edit src/styles/theme.${resolved.scss ? "scss" : "css"} to customize.
|
|
841
|
+
`));
|
|
842
|
+
}
|
|
843
|
+
return { success: true, projectDir };
|
|
844
|
+
}
|
|
845
|
+
export {
|
|
846
|
+
addNextTranspilePackages,
|
|
847
|
+
create,
|
|
848
|
+
generateNextjsLayout,
|
|
849
|
+
generateNextjsPage,
|
|
850
|
+
generateNextjsProviders
|
|
851
|
+
};
|
|
852
|
+
//# sourceMappingURL=create-JVAU3YKN.js.map
|