cuenti-dna 0.1.0-beta.1

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/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "cuenti-dna",
3
+ "version": "0.1.0-beta.1",
4
+ "private": false,
5
+ "description": "Cuenti design system components for React",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ },
16
+ "./styles.css": "./dist/styles.css"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "scripts",
21
+ "README.md"
22
+ ],
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "sideEffects": [
27
+ "**/*.css"
28
+ ],
29
+ "scripts": {
30
+ "build": "pnpm run build:lib && pnpm run build:styles",
31
+ "dev": "pnpm run storybook",
32
+ "build:lib": "tsup src/lib/index.ts --format esm,cjs --dts --sourcemap --clean --external react --external react-dom",
33
+ "build:styles": "postcss src/styles/globals.css -o dist/styles.css",
34
+ "postinstall": "node scripts/postinstall.cjs",
35
+ "prepack": "pnpm run build",
36
+ "lint": "biome lint",
37
+ "format": "biome format --write",
38
+ "check": "biome check --write",
39
+ "lint:fix": "biome lint --write",
40
+ "check:unsafe": "biome check --write --unsafe",
41
+ "storybook": "storybook dev -p 6006",
42
+ "build-storybook": "storybook build",
43
+ "prepublishOnly": "pnpm run lint",
44
+ "publish:beta": "npm publish --access public --tag beta"
45
+ },
46
+ "peerDependencies": {
47
+ "react": ">=18.2.0 <20",
48
+ "react-dom": ">=18.2.0 <20"
49
+ },
50
+ "dependencies": {
51
+ "class-variance-authority": "0.7.1",
52
+ "clsx": "2.1.1",
53
+ "lucide-react": "0.576.0",
54
+ "tailwind-merge": "3.5.0"
55
+ },
56
+ "devDependencies": {
57
+ "@biomejs/biome": "2.4.5",
58
+ "@chromatic-com/storybook": "5.1.1",
59
+ "@rsbuild/core": "1.7.3",
60
+ "@rsbuild/plugin-react": "1.4.5",
61
+ "@storybook/addon-a11y": "10.2.14",
62
+ "@storybook/addon-docs": "10.2.14",
63
+ "@storybook/react": "10.2.14",
64
+ "@tailwindcss/postcss": "4.2.1",
65
+ "postcss-cli": "11.0.1",
66
+ "react": "19.2.4",
67
+ "react-dom": "19.2.4",
68
+ "@types/react": "19.2.14",
69
+ "@types/react-dom": "19.2.3",
70
+ "storybook": "10.2.14",
71
+ "storybook-react-rsbuild": "3.3.0",
72
+ "tailwindcss": "4.2.1",
73
+ "tsup": "8.5.1",
74
+ "typescript": "5.9.3"
75
+ }
76
+ }
@@ -0,0 +1,569 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('node:fs')
4
+ const path = require('node:path')
5
+ const readline = require('node:readline')
6
+
7
+ const MANAGED_BLOCK_START = '/* cuenti-dna:variables:start */'
8
+ const MANAGED_BLOCK_END = '/* cuenti-dna:variables:end */'
9
+ const STYLE_IMPORT = "@import 'cuenti-dna/styles.css';"
10
+
11
+ const FONT_IMPORTS = [
12
+ '@import url("https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300..900;1,300..900&display=swap");',
13
+ '@import url("https://fonts.googleapis.com/css2?family=Quicksand:wght@300..700&family=Rubik:ital,wght@0,300..900;1,300..900&display=swap");'
14
+ ]
15
+
16
+ const ROOT_VARIABLES_BLOCK = `:root {
17
+ --background: oklch(0.9672 0 0);
18
+ --foreground: oklch(0.2178 0 0);
19
+ --neutral-50: #ffffff;
20
+ --neutral-100: #fcfcfc;
21
+ --neutral-200: #f4f4f4;
22
+ --neutral-300: #ececec;
23
+ --neutral-400: #e8e8e8;
24
+ --neutral-500: #dadada;
25
+ --neutral-600: #cdcdcd;
26
+ --neutral-700: #797979;
27
+ --neutral-800: #515151;
28
+ --neutral-900: #1a1a1a;
29
+ --neutral-1000: #141414;
30
+ --neutral-1100: #000000;
31
+ --primary-50: #cedcf5;
32
+ --primary-100: #9eb8eb;
33
+ --primary-200: #6d95e1;
34
+ --primary-300: #3c72d7;
35
+ --primary-400: #2556b4;
36
+ --primary-500: #1b3f83;
37
+ --primary-600: #17356d;
38
+ --primary-700: #122a57;
39
+ --primary-800: #0d2042;
40
+ --primary-900: #09152c;
41
+ --primary: var(--primary-500);
42
+ --primary-foreground: var(--neutral-100);
43
+ --secondary-25: #e8f0ff;
44
+ --secondary-50: #cdd6ec;
45
+ --secondary-100: #9bacd9;
46
+ --secondary-200: #6983c5;
47
+ --secondary-300: #405ea8;
48
+ --secondary-400: #2d4276;
49
+ --secondary-500: #1a2644;
50
+ --secondary-600: #162039;
51
+ --secondary-700: #11192d;
52
+ --secondary-800: #0d1322;
53
+ --secondary-900: #090d17;
54
+ --secondary: var(--secondary-500);
55
+ --secondary-foreground: var(--neutral-100);
56
+ --accent-50: #fff6d5;
57
+ --accent-100: #ffecaa;
58
+ --accent-200: #ffe380;
59
+ --accent-300: #ffd955;
60
+ --accent-400: #ffd02b;
61
+ --accent-500: #ffc600;
62
+ --accent-600: #d5a500;
63
+ --accent-700: #aa8400;
64
+ --accent-800: #806300;
65
+ --accent-900: #554200;
66
+ --accent: var(--accent-500);
67
+ --accent-foreground: var(--secondary);
68
+ --destructive-25: #fff3f3;
69
+ --destructive-50: #fbdcdc;
70
+ --destructive-100: #f7b9b9;
71
+ --destructive-200: #f39696;
72
+ --destructive-300: #ee7373;
73
+ --destructive-400: #ea5050;
74
+ --destructive-500: #e62d2d;
75
+ --destructive-600: #cd1818;
76
+ --destructive-700: #a41414;
77
+ --destructive-800: #7b0f0f;
78
+ --destructive-900: #520a0a;
79
+ --destructive: var(--destructive-500);
80
+ --destructive-foreground: var(--neutral-100);
81
+ --success-25: #ebffed;
82
+ --success-50: #dbf5de;
83
+ --success-100: #b8ebbd;
84
+ --success-200: #94e19d;
85
+ --success-300: #71d77c;
86
+ --success-400: #4dcd5b;
87
+ --success-500: #34b942;
88
+ --success-600: #2b9a37;
89
+ --success-700: #237b2c;
90
+ --success-800: #1a5d21;
91
+ --success-900: #113e16;
92
+ --success: var(--success-500);
93
+ --success-foreground: var(--neutral-100);
94
+ --warning-25: #fffaf2;
95
+ --warning-50: #fdefd9;
96
+ --warning-100: #fbdeb2;
97
+ --warning-200: #face8c;
98
+ --warning-300: #f8be66;
99
+ --warning-400: #f6ad3f;
100
+ --warning-500: #f49d19;
101
+ --warning-600: #d6850a;
102
+ --warning-700: #ab6a08;
103
+ --warning-800: #805006;
104
+ --warning-900: #563504;
105
+ --warning: var(--warning-500);
106
+ --warning-foreground: var(--neutral-100);
107
+ --info-25: #f3f6fd;
108
+ --info-50: #dae5f8;
109
+ --info-100: #b4cbf1;
110
+ --info-200: #8fb1ea;
111
+ --info-300: #6a96e4;
112
+ --info-400: #447cdd;
113
+ --info-500: #2664cf;
114
+ --info-600: #2053ac;
115
+ --info-700: #19438a;
116
+ --info-800: #133267;
117
+ --info-900: #0d2145;
118
+ --info: var(--info-500);
119
+ --info-foreground: var(--neutral-100);
120
+ --alt2-25: #f6f3fa;
121
+ --alt2-50: #e3daf0;
122
+ --alt2-100: #c7b4e1;
123
+ --alt2-200: #ac8fd2;
124
+ --alt2-300: #9069c3;
125
+ --alt2-400: #7547b1;
126
+ --alt2-500: #5c388c;
127
+ --alt2-600: #4d2f75;
128
+ --alt2-700: #3d255d;
129
+ --alt2-800: #2e1c46;
130
+ --alt2-900: #1f1132;
131
+ --alt2: var(--alt2-500);
132
+ --alt2-foreground: var(--neutral-100);
133
+ --font-paragraph: "Quicksand", ui-sans-serif, sans-serif, system-ui;
134
+ --font-title: "Rubik", ui-sans-serif, sans-serif, system-ui;
135
+ --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
136
+ --tracking-normal: 0em;
137
+ }`
138
+
139
+ const THEME_INLINE_BLOCK = `@theme inline {
140
+ --color-background: var(--background);
141
+ --color-foreground: var(--foreground);
142
+ --color-neutral-50: var(--neutral-50);
143
+ --color-neutral-100: var(--neutral-100);
144
+ --color-neutral-200: var(--neutral-200);
145
+ --color-neutral-300: var(--neutral-300);
146
+ --color-neutral-400: var(--neutral-400);
147
+ --color-neutral-500: var(--neutral-500);
148
+ --color-neutral-600: var(--neutral-600);
149
+ --color-neutral-700: var(--neutral-700);
150
+ --color-neutral-800: var(--neutral-800);
151
+ --color-neutral-900: var(--neutral-900);
152
+ --color-neutral-1000: var(--neutral-1000);
153
+ --color-neutral-1100: var(--neutral-1100);
154
+ --color-primary-50: var(--primary-50);
155
+ --color-primary-100: var(--primary-100);
156
+ --color-primary-200: var(--primary-200);
157
+ --color-primary-300: var(--primary-300);
158
+ --color-primary-400: var(--primary-400);
159
+ --color-primary-500: var(--primary-500);
160
+ --color-primary-600: var(--primary-600);
161
+ --color-primary-700: var(--primary-700);
162
+ --color-primary-800: var(--primary-800);
163
+ --color-primary-900: var(--primary-900);
164
+ --color-primary: var(--primary);
165
+ --color-primary-foreground: var(--primary-foreground);
166
+ --color-secondary-25: var(--secondary-25);
167
+ --color-secondary-50: var(--secondary-50);
168
+ --color-secondary-100: var(--secondary-100);
169
+ --color-secondary-200: var(--secondary-200);
170
+ --color-secondary-300: var(--secondary-300);
171
+ --color-secondary-400: var(--secondary-400);
172
+ --color-secondary-500: var(--secondary-500);
173
+ --color-secondary-600: var(--secondary-600);
174
+ --color-secondary-700: var(--secondary-700);
175
+ --color-secondary-800: var(--secondary-800);
176
+ --color-secondary-900: var(--secondary-900);
177
+ --color-secondary: var(--secondary);
178
+ --color-secondary-foreground: var(--secondary-foreground);
179
+ --color-accent-50: var(--accent-50);
180
+ --color-accent-100: var(--accent-100);
181
+ --color-accent-200: var(--accent-200);
182
+ --color-accent-300: var(--accent-300);
183
+ --color-accent-400: var(--accent-400);
184
+ --color-accent-500: var(--accent-500);
185
+ --color-accent-600: var(--accent-600);
186
+ --color-accent-700: var(--accent-700);
187
+ --color-accent-800: var(--accent-800);
188
+ --color-accent-900: var(--accent-900);
189
+ --color-accent: var(--accent);
190
+ --color-accent-foreground: var(--accent-foreground);
191
+ --color-destructive-25: var(--destructive-25);
192
+ --color-destructive-50: var(--destructive-50);
193
+ --color-destructive-100: var(--destructive-100);
194
+ --color-destructive-200: var(--destructive-200);
195
+ --color-destructive-300: var(--destructive-300);
196
+ --color-destructive-400: var(--destructive-400);
197
+ --color-destructive-500: var(--destructive-500);
198
+ --color-destructive-600: var(--destructive-600);
199
+ --color-destructive-700: var(--destructive-700);
200
+ --color-destructive-800: var(--destructive-800);
201
+ --color-destructive-900: var(--destructive-900);
202
+ --color-destructive: var(--destructive);
203
+ --color-destructive-foreground: var(--destructive-foreground);
204
+ --color-success-25: var(--success-25);
205
+ --color-success-50: var(--success-50);
206
+ --color-success-100: var(--success-100);
207
+ --color-success-200: var(--success-200);
208
+ --color-success-300: var(--success-300);
209
+ --color-success-400: var(--success-400);
210
+ --color-success-500: var(--success-500);
211
+ --color-success-600: var(--success-600);
212
+ --color-success-700: var(--success-700);
213
+ --color-success-800: var(--success-800);
214
+ --color-success-900: var(--success-900);
215
+ --color-success: var(--success);
216
+ --color-success-foreground: var(--success-foreground);
217
+ --color-warning-25: var(--warning-25);
218
+ --color-warning-50: var(--warning-50);
219
+ --color-warning-100: var(--warning-100);
220
+ --color-warning-200: var(--warning-200);
221
+ --color-warning-300: var(--warning-300);
222
+ --color-warning-400: var(--warning-400);
223
+ --color-warning-500: var(--warning-500);
224
+ --color-warning-600: var(--warning-600);
225
+ --color-warning-700: var(--warning-700);
226
+ --color-warning-800: var(--warning-800);
227
+ --color-warning-900: var(--warning-900);
228
+ --color-warning: var(--warning);
229
+ --color-warning-foreground: var(--warning-foreground);
230
+ --color-info-25: var(--info-25);
231
+ --color-info-50: var(--info-50);
232
+ --color-info-100: var(--info-100);
233
+ --color-info-200: var(--info-200);
234
+ --color-info-300: var(--info-300);
235
+ --color-info-400: var(--info-400);
236
+ --color-info-500: var(--info-500);
237
+ --color-info-600: var(--info-600);
238
+ --color-info-700: var(--info-700);
239
+ --color-info-800: var(--info-800);
240
+ --color-info-900: var(--info-900);
241
+ --color-info: var(--info);
242
+ --color-info-foreground: var(--info-foreground);
243
+ --color-alt2-25: var(--alt2-25);
244
+ --color-alt2-50: var(--alt2-50);
245
+ --color-alt2-100: var(--alt2-100);
246
+ --color-alt2-200: var(--alt2-200);
247
+ --color-alt2-300: var(--alt2-300);
248
+ --color-alt2-400: var(--alt2-400);
249
+ --color-alt2-500: var(--alt2-500);
250
+ --color-alt2-600: var(--alt2-600);
251
+ --color-alt2-700: var(--alt2-700);
252
+ --color-alt2-800: var(--alt2-800);
253
+ --color-alt2-900: var(--alt2-900);
254
+ --color-alt2: var(--alt2);
255
+ --color-alt2-foreground: var(--alt2-foreground);
256
+ --color-muted: var(--neutral-400);
257
+ --color-muted-foreground: var(--neutral-700);
258
+ --color-card: var(--neutral-50);
259
+ --color-card-foreground: var(--foreground);
260
+ --color-ring: var(--primary);
261
+ --color-border: var(--neutral-600);
262
+ --radius: 1rem;
263
+ --radius-xs: calc(var(--radius) - 6px);
264
+ --radius-sm: calc(var(--radius) - 4px);
265
+ --radius-md: var(--radius);
266
+ --radius-lg: calc(var(--radius) + 4px);
267
+ --radius-xl: calc(var(--radius) + 6px);
268
+ --font-title: var(--font-title);
269
+ --font-paragraph: var(--font-paragraph);
270
+ --font-mono: var(--font-mono);
271
+ --breakpoint-xs: 400px;
272
+ --shadow-xs: 0px 1.5px 3px 0px rgb(68 124 221 / 20%);
273
+ --shadow-sm: 0px 6px 12px 0px rgb(68 124 221 / 20%);
274
+ --shadow-md: 0px 9px 21px 0px rgb(68 124 221 / 20%);
275
+ --shadow-lg: 0px 12px 36px -6px rgb(68 124 221 / 20%);
276
+ --shadow-xl: 0px 24px 72px -12px rgb(68 124 221 / 20%);
277
+ }`
278
+
279
+ const CSS_CANDIDATES = [
280
+ 'src/styles/globals.css',
281
+ 'src/globals.css',
282
+ 'app/globals.css',
283
+ 'styles/globals.css',
284
+ 'src/index.css',
285
+ 'src/styles.css',
286
+ 'src/app.css'
287
+ ]
288
+
289
+ const DEFAULT_CSS_FILE = 'src/styles/globals.css'
290
+
291
+ const log = (message) => {
292
+ // biome-ignore lint/suspicious/noConsole: lifecycle scripts should print setup information.
293
+ console.log(`[cuenti-dna] ${message}`)
294
+ }
295
+
296
+ const toPosixPath = (value) => value.replace(/\\/g, '/')
297
+
298
+ const fileExists = (filePath) => {
299
+ try {
300
+ return fs.statSync(filePath).isFile()
301
+ } catch {
302
+ return false
303
+ }
304
+ }
305
+
306
+ const readTextFile = (filePath) => fs.readFileSync(filePath, 'utf8')
307
+
308
+ const writeTextFile = (filePath, content) => {
309
+ fs.mkdirSync(path.dirname(filePath), { recursive: true })
310
+ fs.writeFileSync(filePath, content, 'utf8')
311
+ }
312
+
313
+ const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
314
+
315
+ const detectEol = (content) => (content.includes('\r\n') ? '\r\n' : '\n')
316
+
317
+ const parseVariableNames = (content) => {
318
+ const names = new Set()
319
+ const matcher = /--([a-z0-9-_]+)\s*:/gi
320
+
321
+ let match = matcher.exec(content)
322
+
323
+ while (match) {
324
+ names.add(match[1])
325
+ match = matcher.exec(content)
326
+ }
327
+
328
+ return names
329
+ }
330
+
331
+ const removeManagedBlock = (content) => {
332
+ const pattern = new RegExp(
333
+ `${escapeRegExp(MANAGED_BLOCK_START)}[\\s\\S]*?${escapeRegExp(MANAGED_BLOCK_END)}\\s*`,
334
+ 'g'
335
+ )
336
+
337
+ return content.replace(pattern, '')
338
+ }
339
+
340
+ const removeLegacyImports = (content) => {
341
+ const importPattern =
342
+ /^[ \t]*@import\s+['"]cuenti-dna\/styles\.css['"];\s*\r?\n?/gim
343
+
344
+ let next = content.replace(importPattern, '')
345
+
346
+ for (const importLine of FONT_IMPORTS) {
347
+ const exactLinePattern = new RegExp(
348
+ `^[ \\t]*${escapeRegExp(importLine)}\\s*\\r?\\n?`,
349
+ 'gim'
350
+ )
351
+
352
+ next = next.replace(exactLinePattern, '')
353
+ }
354
+
355
+ return next
356
+ }
357
+
358
+ const removeVariableDeclarations = (content, variableNames) => {
359
+ let next = content
360
+
361
+ for (const variableName of variableNames) {
362
+ const declarationPattern = new RegExp(
363
+ `^[ \\t]*--${escapeRegExp(variableName)}\\s*:[^;]*;\\s*\\r?\\n?`,
364
+ 'gim'
365
+ )
366
+
367
+ next = next.replace(declarationPattern, '')
368
+ }
369
+
370
+ return next
371
+ }
372
+
373
+ const extractCharsetHeader = (content) => {
374
+ const charsetMatch = content.match(/^\s*@charset\s+[^;]+;\s*/i)
375
+
376
+ if (!charsetMatch) {
377
+ return {
378
+ charset: '',
379
+ rest: content
380
+ }
381
+ }
382
+
383
+ return {
384
+ charset: charsetMatch[0].trim(),
385
+ rest: content.slice(charsetMatch[0].length)
386
+ }
387
+ }
388
+
389
+ const buildManagedBlock = (eol) => {
390
+ const rootBlock = ROOT_VARIABLES_BLOCK.trim().replace(/\n/g, eol)
391
+ const themeInlineBlock = THEME_INLINE_BLOCK.trim().replace(/\n/g, eol)
392
+
393
+ return [
394
+ MANAGED_BLOCK_START,
395
+ STYLE_IMPORT,
396
+ ...FONT_IMPORTS,
397
+ '',
398
+ rootBlock,
399
+ '',
400
+ themeInlineBlock,
401
+ MANAGED_BLOCK_END
402
+ ].join(eol)
403
+ }
404
+
405
+ const askForOverwrite = (conflicts) =>
406
+ new Promise((resolve) => {
407
+ const rl = readline.createInterface({
408
+ input: process.stdin,
409
+ output: process.stdout
410
+ })
411
+
412
+ rl.question(
413
+ `[cuenti-dna] Found ${conflicts.length} CSS variable collisions. Overwrite existing variables with Cuenti defaults? (y/N): `,
414
+ (answer) => {
415
+ rl.close()
416
+
417
+ const normalized = answer.trim().toLowerCase()
418
+ resolve(normalized === 'y' || normalized === 'yes')
419
+ }
420
+ )
421
+ })
422
+
423
+ const parseOverwriteFlag = (value) => {
424
+ const normalized = String(value).trim().toLowerCase()
425
+
426
+ if (['1', 'true', 'yes', 'y'].includes(normalized)) {
427
+ return true
428
+ }
429
+
430
+ if (['0', 'false', 'no', 'n'].includes(normalized)) {
431
+ return false
432
+ }
433
+
434
+ return null
435
+ }
436
+
437
+ const resolveOverwriteDecision = async (conflicts) => {
438
+ if (conflicts.length === 0) {
439
+ return false
440
+ }
441
+
442
+ if (process.env.CUENTI_DNA_OVERWRITE_VARS) {
443
+ const parsed = parseOverwriteFlag(process.env.CUENTI_DNA_OVERWRITE_VARS)
444
+
445
+ if (parsed !== null) {
446
+ return parsed
447
+ }
448
+ }
449
+
450
+ const isInteractive =
451
+ Boolean(process.stdin.isTTY) &&
452
+ Boolean(process.stdout.isTTY) &&
453
+ !process.env.CI
454
+
455
+ if (!isInteractive) {
456
+ log(
457
+ 'Detected CSS variable collisions. Keeping consumer variables. Set CUENTI_DNA_OVERWRITE_VARS=true to force overwrite in non-interactive installs.'
458
+ )
459
+ return false
460
+ }
461
+
462
+ return askForOverwrite(conflicts)
463
+ }
464
+
465
+ const resolveTargetCssFile = (projectRoot) => {
466
+ for (const relativeFile of CSS_CANDIDATES) {
467
+ const absolutePath = path.join(projectRoot, relativeFile)
468
+
469
+ if (fileExists(absolutePath)) {
470
+ return {
471
+ absolutePath,
472
+ relativeFile,
473
+ existed: true
474
+ }
475
+ }
476
+ }
477
+
478
+ return {
479
+ absolutePath: path.join(projectRoot, DEFAULT_CSS_FILE),
480
+ relativeFile: DEFAULT_CSS_FILE,
481
+ existed: false
482
+ }
483
+ }
484
+
485
+ const prependVariablesAndFonts = async (projectRoot) => {
486
+ const target = resolveTargetCssFile(projectRoot)
487
+ const current = target.existed ? readTextFile(target.absolutePath) : ''
488
+ const eol = detectEol(current)
489
+
490
+ const withoutManagedBlock = removeManagedBlock(current)
491
+ const cleanedContent = removeLegacyImports(withoutManagedBlock)
492
+ const cuentiVariables = parseVariableNames(
493
+ `${ROOT_VARIABLES_BLOCK}\n${THEME_INLINE_BLOCK}`
494
+ )
495
+ const existingVariables = parseVariableNames(cleanedContent)
496
+ const conflicts = [...existingVariables].filter((name) =>
497
+ cuentiVariables.has(name)
498
+ )
499
+
500
+ const shouldOverwrite = await resolveOverwriteDecision(conflicts)
501
+
502
+ const preservedContent = shouldOverwrite
503
+ ? removeVariableDeclarations(cleanedContent, conflicts)
504
+ : cleanedContent
505
+
506
+ const { charset, rest } = extractCharsetHeader(preservedContent)
507
+ const normalizedRest = rest.trimStart()
508
+ const managedBlock = buildManagedBlock(eol)
509
+
510
+ const chunks = []
511
+
512
+ if (charset) {
513
+ chunks.push(charset)
514
+ }
515
+
516
+ chunks.push(managedBlock)
517
+
518
+ if (normalizedRest.trim().length > 0) {
519
+ chunks.push(normalizedRest)
520
+ }
521
+
522
+ const nextContent = `${chunks.join(`${eol}${eol}`)}${eol}`
523
+ writeTextFile(target.absolutePath, nextContent)
524
+
525
+ if (conflicts.length === 0) {
526
+ log(
527
+ `Injected fonts and variables into ${toPosixPath(target.relativeFile)}.`
528
+ )
529
+ } else if (shouldOverwrite) {
530
+ log(
531
+ `Injected fonts and variables into ${toPosixPath(target.relativeFile)} and overwrote ${conflicts.length} colliding variables.`
532
+ )
533
+ } else {
534
+ log(
535
+ `Injected fonts and variables into ${toPosixPath(target.relativeFile)} and preserved ${conflicts.length} consumer variables.`
536
+ )
537
+ }
538
+
539
+ if (!target.existed) {
540
+ log(
541
+ `Created ${toPosixPath(target.relativeFile)}. Ensure your app entry imports this CSS file.`
542
+ )
543
+ }
544
+ }
545
+
546
+ const main = async () => {
547
+ const projectRoot = process.env.INIT_CWD
548
+ ? path.resolve(process.env.INIT_CWD)
549
+ : null
550
+
551
+ if (!projectRoot) {
552
+ log('Skipped auto setup because INIT_CWD is not available.')
553
+ return
554
+ }
555
+
556
+ if (path.resolve(process.cwd()) === projectRoot) {
557
+ log('Skipped auto setup in the package workspace.')
558
+ return
559
+ }
560
+
561
+ try {
562
+ await prependVariablesAndFonts(projectRoot)
563
+ } catch (error) {
564
+ const details = error instanceof Error ? error.message : String(error)
565
+ log(`Auto setup failed: ${details}`)
566
+ }
567
+ }
568
+
569
+ void main()