@vettvangur/design-system 0.0.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.
Files changed (37) hide show
  1. package/LICENSE +11 -0
  2. package/README.md +18 -0
  3. package/dist/generate-astro-AcegHzWG.js +476 -0
  4. package/dist/generate-astro-B225JTMY.js +534 -0
  5. package/dist/generate-astro-Btr2jiyr.js +389 -0
  6. package/dist/generate-astro-CpNCOn_3.js +475 -0
  7. package/dist/generate-astro-CqXZcTfi.js +820 -0
  8. package/dist/generate-astro-D-FcwrAx.js +819 -0
  9. package/dist/generate-astro-DfaWTEF_.js +539 -0
  10. package/dist/generate-astro-DwBkft4p.js +820 -0
  11. package/dist/generate-razor--hJCWG13.js +454 -0
  12. package/dist/generate-razor-B0Pid10L.js +441 -0
  13. package/dist/generate-razor-B0n9FUR8.js +441 -0
  14. package/dist/generate-razor-B3a_-uDf.js +441 -0
  15. package/dist/generate-razor-BLNML3CC.js +441 -0
  16. package/dist/generate-razor-CABSDQok.js +454 -0
  17. package/dist/generate-razor-CGhqwpRL.js +413 -0
  18. package/dist/generate-razor-DcAkNKya.js +9 -0
  19. package/dist/generate-razor-DwXDvSTP.js +332 -0
  20. package/dist/generate-razor-IGs8o9fx.js +441 -0
  21. package/dist/generate-razor-nGTa0EVW.js +335 -0
  22. package/dist/generate-tailwind-16Mayr2z.js +1050 -0
  23. package/dist/generate-tailwind-BR1-fpNI.js +1056 -0
  24. package/dist/generate-tailwind-BVpI-hUx.js +1057 -0
  25. package/dist/generate-tailwind-Bd3ltCQR.js +1084 -0
  26. package/dist/generate-tailwind-BljcUYMa.js +1051 -0
  27. package/dist/generate-tailwind-BqZCEvj5.js +1049 -0
  28. package/dist/generate-tailwind-CGwQarzK.js +1059 -0
  29. package/dist/generate-tailwind-Cpb8YNNz.js +1050 -0
  30. package/dist/generate-tailwind-CtUsHnBd.js +1060 -0
  31. package/dist/generate-tailwind-DL0GEqR6.js +1050 -0
  32. package/dist/generate-tailwind-Dr3KLBMD.js +1045 -0
  33. package/dist/generate-tailwind-KzRHwyMG.js +1049 -0
  34. package/dist/generate-tailwind-VnkcDTZP.js +1049 -0
  35. package/dist/index.esm.js +58 -0
  36. package/dist/inputs-CQVgm9Do.js +439 -0
  37. package/package.json +35 -0
@@ -0,0 +1,1057 @@
1
+ #!/usr/bin/env node
2
+ import fs$1 from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import chalk from 'chalk';
5
+ import require$$0 from 'fs';
6
+ import require$$1 from 'path';
7
+ import require$$2 from 'os';
8
+ import require$$3 from 'crypto';
9
+ import fs from 'node:fs';
10
+ import { pathToFileURL } from 'node:url';
11
+ import process$1 from 'node:process';
12
+
13
+ var config$1 = {};
14
+
15
+ var main$1 = {exports: {}};
16
+
17
+ var name = "dotenv";
18
+ var version = "16.4.5";
19
+ var description = "Loads environment variables from .env file";
20
+ var main = "lib/main.js";
21
+ var types = "lib/main.d.ts";
22
+ var exports = {
23
+ ".": {
24
+ types: "./lib/main.d.ts",
25
+ require: "./lib/main.js",
26
+ "default": "./lib/main.js"
27
+ },
28
+ "./config": "./config.js",
29
+ "./config.js": "./config.js",
30
+ "./lib/env-options": "./lib/env-options.js",
31
+ "./lib/env-options.js": "./lib/env-options.js",
32
+ "./lib/cli-options": "./lib/cli-options.js",
33
+ "./lib/cli-options.js": "./lib/cli-options.js",
34
+ "./package.json": "./package.json"
35
+ };
36
+ var scripts = {
37
+ "dts-check": "tsc --project tests/types/tsconfig.json",
38
+ lint: "standard",
39
+ "lint-readme": "standard-markdown",
40
+ pretest: "npm run lint && npm run dts-check",
41
+ test: "tap tests/*.js --100 -Rspec",
42
+ "test:coverage": "tap --coverage-report=lcov",
43
+ prerelease: "npm test",
44
+ release: "standard-version"
45
+ };
46
+ var repository = {
47
+ type: "git",
48
+ url: "git://github.com/motdotla/dotenv.git"
49
+ };
50
+ var funding = "https://dotenvx.com";
51
+ var keywords = [
52
+ "dotenv",
53
+ "env",
54
+ ".env",
55
+ "environment",
56
+ "variables",
57
+ "config",
58
+ "settings"
59
+ ];
60
+ var readmeFilename = "README.md";
61
+ var license = "BSD-2-Clause";
62
+ var devDependencies = {
63
+ "@definitelytyped/dtslint": "^0.0.133",
64
+ "@types/node": "^18.11.3",
65
+ decache: "^4.6.1",
66
+ sinon: "^14.0.1",
67
+ standard: "^17.0.0",
68
+ "standard-markdown": "^7.1.0",
69
+ "standard-version": "^9.5.0",
70
+ tap: "^16.3.0",
71
+ tar: "^6.1.11",
72
+ typescript: "^4.8.4"
73
+ };
74
+ var engines = {
75
+ node: ">=12"
76
+ };
77
+ var browser = {
78
+ fs: false
79
+ };
80
+ var require$$4 = {
81
+ name: name,
82
+ version: version,
83
+ description: description,
84
+ main: main,
85
+ types: types,
86
+ exports: exports,
87
+ scripts: scripts,
88
+ repository: repository,
89
+ funding: funding,
90
+ keywords: keywords,
91
+ readmeFilename: readmeFilename,
92
+ license: license,
93
+ devDependencies: devDependencies,
94
+ engines: engines,
95
+ browser: browser
96
+ };
97
+
98
+ var hasRequiredMain;
99
+ function requireMain() {
100
+ if (hasRequiredMain) return main$1.exports;
101
+ hasRequiredMain = 1;
102
+ const fs = require$$0;
103
+ const path = require$$1;
104
+ const os = require$$2;
105
+ const crypto = require$$3;
106
+ const packageJson = require$$4;
107
+ const version = packageJson.version;
108
+ const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg;
109
+
110
+ // Parse src into an Object
111
+ function parse(src) {
112
+ const obj = {};
113
+
114
+ // Convert buffer to string
115
+ let lines = src.toString();
116
+
117
+ // Convert line breaks to same format
118
+ lines = lines.replace(/\r\n?/mg, '\n');
119
+ let match;
120
+ while ((match = LINE.exec(lines)) != null) {
121
+ const key = match[1];
122
+
123
+ // Default undefined or null to empty string
124
+ let value = match[2] || '';
125
+
126
+ // Remove whitespace
127
+ value = value.trim();
128
+
129
+ // Check if double quoted
130
+ const maybeQuote = value[0];
131
+
132
+ // Remove surrounding quotes
133
+ value = value.replace(/^(['"`])([\s\S]*)\1$/mg, '$2');
134
+
135
+ // Expand newlines if double quoted
136
+ if (maybeQuote === '"') {
137
+ value = value.replace(/\\n/g, '\n');
138
+ value = value.replace(/\\r/g, '\r');
139
+ }
140
+
141
+ // Add to object
142
+ obj[key] = value;
143
+ }
144
+ return obj;
145
+ }
146
+ function _parseVault(options) {
147
+ const vaultPath = _vaultPath(options);
148
+
149
+ // Parse .env.vault
150
+ const result = DotenvModule.configDotenv({
151
+ path: vaultPath
152
+ });
153
+ if (!result.parsed) {
154
+ const err = new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`);
155
+ err.code = 'MISSING_DATA';
156
+ throw err;
157
+ }
158
+
159
+ // handle scenario for comma separated keys - for use with key rotation
160
+ // example: DOTENV_KEY="dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=prod,dotenv://:key_7890@dotenvx.com/vault/.env.vault?environment=prod"
161
+ const keys = _dotenvKey(options).split(',');
162
+ const length = keys.length;
163
+ let decrypted;
164
+ for (let i = 0; i < length; i++) {
165
+ try {
166
+ // Get full key
167
+ const key = keys[i].trim();
168
+
169
+ // Get instructions for decrypt
170
+ const attrs = _instructions(result, key);
171
+
172
+ // Decrypt
173
+ decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key);
174
+ break;
175
+ } catch (error) {
176
+ // last key
177
+ if (i + 1 >= length) {
178
+ throw error;
179
+ }
180
+ // try next key
181
+ }
182
+ }
183
+
184
+ // Parse decrypted .env string
185
+ return DotenvModule.parse(decrypted);
186
+ }
187
+ function _log(message) {
188
+ console.log(`[dotenv@${version}][INFO] ${message}`);
189
+ }
190
+ function _warn(message) {
191
+ console.log(`[dotenv@${version}][WARN] ${message}`);
192
+ }
193
+ function _debug(message) {
194
+ console.log(`[dotenv@${version}][DEBUG] ${message}`);
195
+ }
196
+ function _dotenvKey(options) {
197
+ // prioritize developer directly setting options.DOTENV_KEY
198
+ if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) {
199
+ return options.DOTENV_KEY;
200
+ }
201
+
202
+ // secondary infra already contains a DOTENV_KEY environment variable
203
+ if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) {
204
+ return process.env.DOTENV_KEY;
205
+ }
206
+
207
+ // fallback to empty string
208
+ return '';
209
+ }
210
+ function _instructions(result, dotenvKey) {
211
+ // Parse DOTENV_KEY. Format is a URI
212
+ let uri;
213
+ try {
214
+ uri = new URL(dotenvKey);
215
+ } catch (error) {
216
+ if (error.code === 'ERR_INVALID_URL') {
217
+ const err = new Error('INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development');
218
+ err.code = 'INVALID_DOTENV_KEY';
219
+ throw err;
220
+ }
221
+ throw error;
222
+ }
223
+
224
+ // Get decrypt key
225
+ const key = uri.password;
226
+ if (!key) {
227
+ const err = new Error('INVALID_DOTENV_KEY: Missing key part');
228
+ err.code = 'INVALID_DOTENV_KEY';
229
+ throw err;
230
+ }
231
+
232
+ // Get environment
233
+ const environment = uri.searchParams.get('environment');
234
+ if (!environment) {
235
+ const err = new Error('INVALID_DOTENV_KEY: Missing environment part');
236
+ err.code = 'INVALID_DOTENV_KEY';
237
+ throw err;
238
+ }
239
+
240
+ // Get ciphertext payload
241
+ const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`;
242
+ const ciphertext = result.parsed[environmentKey]; // DOTENV_VAULT_PRODUCTION
243
+ if (!ciphertext) {
244
+ const err = new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`);
245
+ err.code = 'NOT_FOUND_DOTENV_ENVIRONMENT';
246
+ throw err;
247
+ }
248
+ return {
249
+ ciphertext,
250
+ key
251
+ };
252
+ }
253
+ function _vaultPath(options) {
254
+ let possibleVaultPath = null;
255
+ if (options && options.path && options.path.length > 0) {
256
+ if (Array.isArray(options.path)) {
257
+ for (const filepath of options.path) {
258
+ if (fs.existsSync(filepath)) {
259
+ possibleVaultPath = filepath.endsWith('.vault') ? filepath : `${filepath}.vault`;
260
+ }
261
+ }
262
+ } else {
263
+ possibleVaultPath = options.path.endsWith('.vault') ? options.path : `${options.path}.vault`;
264
+ }
265
+ } else {
266
+ possibleVaultPath = path.resolve(process.cwd(), '.env.vault');
267
+ }
268
+ if (fs.existsSync(possibleVaultPath)) {
269
+ return possibleVaultPath;
270
+ }
271
+ return null;
272
+ }
273
+ function _resolveHome(envPath) {
274
+ return envPath[0] === '~' ? path.join(os.homedir(), envPath.slice(1)) : envPath;
275
+ }
276
+ function _configVault(options) {
277
+ _log('Loading env from encrypted .env.vault');
278
+ const parsed = DotenvModule._parseVault(options);
279
+ let processEnv = process.env;
280
+ if (options && options.processEnv != null) {
281
+ processEnv = options.processEnv;
282
+ }
283
+ DotenvModule.populate(processEnv, parsed, options);
284
+ return {
285
+ parsed
286
+ };
287
+ }
288
+ function configDotenv(options) {
289
+ const dotenvPath = path.resolve(process.cwd(), '.env');
290
+ let encoding = 'utf8';
291
+ const debug = Boolean(options && options.debug);
292
+ if (options && options.encoding) {
293
+ encoding = options.encoding;
294
+ } else {
295
+ if (debug) {
296
+ _debug('No encoding is specified. UTF-8 is used by default');
297
+ }
298
+ }
299
+ let optionPaths = [dotenvPath]; // default, look for .env
300
+ if (options && options.path) {
301
+ if (!Array.isArray(options.path)) {
302
+ optionPaths = [_resolveHome(options.path)];
303
+ } else {
304
+ optionPaths = []; // reset default
305
+ for (const filepath of options.path) {
306
+ optionPaths.push(_resolveHome(filepath));
307
+ }
308
+ }
309
+ }
310
+
311
+ // Build the parsed data in a temporary object (because we need to return it). Once we have the final
312
+ // parsed data, we will combine it with process.env (or options.processEnv if provided).
313
+ let lastError;
314
+ const parsedAll = {};
315
+ for (const path of optionPaths) {
316
+ try {
317
+ // Specifying an encoding returns a string instead of a buffer
318
+ const parsed = DotenvModule.parse(fs.readFileSync(path, {
319
+ encoding
320
+ }));
321
+ DotenvModule.populate(parsedAll, parsed, options);
322
+ } catch (e) {
323
+ if (debug) {
324
+ _debug(`Failed to load ${path} ${e.message}`);
325
+ }
326
+ lastError = e;
327
+ }
328
+ }
329
+ let processEnv = process.env;
330
+ if (options && options.processEnv != null) {
331
+ processEnv = options.processEnv;
332
+ }
333
+ DotenvModule.populate(processEnv, parsedAll, options);
334
+ if (lastError) {
335
+ return {
336
+ parsed: parsedAll,
337
+ error: lastError
338
+ };
339
+ } else {
340
+ return {
341
+ parsed: parsedAll
342
+ };
343
+ }
344
+ }
345
+
346
+ // Populates process.env from .env file
347
+ function config(options) {
348
+ // fallback to original dotenv if DOTENV_KEY is not set
349
+ if (_dotenvKey(options).length === 0) {
350
+ return DotenvModule.configDotenv(options);
351
+ }
352
+ const vaultPath = _vaultPath(options);
353
+
354
+ // dotenvKey exists but .env.vault file does not exist
355
+ if (!vaultPath) {
356
+ _warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`);
357
+ return DotenvModule.configDotenv(options);
358
+ }
359
+ return DotenvModule._configVault(options);
360
+ }
361
+ function decrypt(encrypted, keyStr) {
362
+ const key = Buffer.from(keyStr.slice(-64), 'hex');
363
+ let ciphertext = Buffer.from(encrypted, 'base64');
364
+ const nonce = ciphertext.subarray(0, 12);
365
+ const authTag = ciphertext.subarray(-16);
366
+ ciphertext = ciphertext.subarray(12, -16);
367
+ try {
368
+ const aesgcm = crypto.createDecipheriv('aes-256-gcm', key, nonce);
369
+ aesgcm.setAuthTag(authTag);
370
+ return `${aesgcm.update(ciphertext)}${aesgcm.final()}`;
371
+ } catch (error) {
372
+ const isRange = error instanceof RangeError;
373
+ const invalidKeyLength = error.message === 'Invalid key length';
374
+ const decryptionFailed = error.message === 'Unsupported state or unable to authenticate data';
375
+ if (isRange || invalidKeyLength) {
376
+ const err = new Error('INVALID_DOTENV_KEY: It must be 64 characters long (or more)');
377
+ err.code = 'INVALID_DOTENV_KEY';
378
+ throw err;
379
+ } else if (decryptionFailed) {
380
+ const err = new Error('DECRYPTION_FAILED: Please check your DOTENV_KEY');
381
+ err.code = 'DECRYPTION_FAILED';
382
+ throw err;
383
+ } else {
384
+ throw error;
385
+ }
386
+ }
387
+ }
388
+
389
+ // Populate process.env with parsed values
390
+ function populate(processEnv, parsed, options = {}) {
391
+ const debug = Boolean(options && options.debug);
392
+ const override = Boolean(options && options.override);
393
+ if (typeof parsed !== 'object') {
394
+ const err = new Error('OBJECT_REQUIRED: Please check the processEnv argument being passed to populate');
395
+ err.code = 'OBJECT_REQUIRED';
396
+ throw err;
397
+ }
398
+
399
+ // Set process.env
400
+ for (const key of Object.keys(parsed)) {
401
+ if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
402
+ if (override === true) {
403
+ processEnv[key] = parsed[key];
404
+ }
405
+ if (debug) {
406
+ if (override === true) {
407
+ _debug(`"${key}" is already defined and WAS overwritten`);
408
+ } else {
409
+ _debug(`"${key}" is already defined and was NOT overwritten`);
410
+ }
411
+ }
412
+ } else {
413
+ processEnv[key] = parsed[key];
414
+ }
415
+ }
416
+ }
417
+ const DotenvModule = {
418
+ configDotenv,
419
+ _configVault,
420
+ _parseVault,
421
+ config,
422
+ decrypt,
423
+ parse,
424
+ populate
425
+ };
426
+ main$1.exports.configDotenv = DotenvModule.configDotenv;
427
+ main$1.exports._configVault = DotenvModule._configVault;
428
+ main$1.exports._parseVault = DotenvModule._parseVault;
429
+ main$1.exports.config = DotenvModule.config;
430
+ main$1.exports.decrypt = DotenvModule.decrypt;
431
+ main$1.exports.parse = DotenvModule.parse;
432
+ main$1.exports.populate = DotenvModule.populate;
433
+ main$1.exports = DotenvModule;
434
+ return main$1.exports;
435
+ }
436
+
437
+ var envOptions;
438
+ var hasRequiredEnvOptions;
439
+ function requireEnvOptions() {
440
+ if (hasRequiredEnvOptions) return envOptions;
441
+ hasRequiredEnvOptions = 1;
442
+ // ../config.js accepts options via environment variables
443
+ const options = {};
444
+ if (process.env.DOTENV_CONFIG_ENCODING != null) {
445
+ options.encoding = process.env.DOTENV_CONFIG_ENCODING;
446
+ }
447
+ if (process.env.DOTENV_CONFIG_PATH != null) {
448
+ options.path = process.env.DOTENV_CONFIG_PATH;
449
+ }
450
+ if (process.env.DOTENV_CONFIG_DEBUG != null) {
451
+ options.debug = process.env.DOTENV_CONFIG_DEBUG;
452
+ }
453
+ if (process.env.DOTENV_CONFIG_OVERRIDE != null) {
454
+ options.override = process.env.DOTENV_CONFIG_OVERRIDE;
455
+ }
456
+ if (process.env.DOTENV_CONFIG_DOTENV_KEY != null) {
457
+ options.DOTENV_KEY = process.env.DOTENV_CONFIG_DOTENV_KEY;
458
+ }
459
+ envOptions = options;
460
+ return envOptions;
461
+ }
462
+
463
+ var cliOptions;
464
+ var hasRequiredCliOptions;
465
+ function requireCliOptions() {
466
+ if (hasRequiredCliOptions) return cliOptions;
467
+ hasRequiredCliOptions = 1;
468
+ const re = /^dotenv_config_(encoding|path|debug|override|DOTENV_KEY)=(.+)$/;
469
+ cliOptions = function optionMatcher(args) {
470
+ return args.reduce(function (acc, cur) {
471
+ const matches = cur.match(re);
472
+ if (matches) {
473
+ acc[matches[1]] = matches[2];
474
+ }
475
+ return acc;
476
+ }, {});
477
+ };
478
+ return cliOptions;
479
+ }
480
+
481
+ var hasRequiredConfig;
482
+ function requireConfig() {
483
+ if (hasRequiredConfig) return config$1;
484
+ hasRequiredConfig = 1;
485
+ (function () {
486
+ requireMain().config(Object.assign({}, requireEnvOptions(), requireCliOptions()(process.argv)));
487
+ })();
488
+ return config$1;
489
+ }
490
+
491
+ requireConfig();
492
+
493
+ // src/load-config.mjs
494
+ function req(name, val) {
495
+ if (val === undefined || val === null) {
496
+ throw new Error(`[vettvangur-styleguide :: load-config] Missing required config field: ${name}`);
497
+ }
498
+ return val;
499
+ }
500
+
501
+ // Also load .env.local if present (priority over .env)
502
+ const rootCwd = process$1.cwd();
503
+ const envLocal = path.join(rootCwd, ".env.local");
504
+ if (fs.existsSync(envLocal)) {
505
+ const dotenv = await import('dotenv');
506
+ dotenv.config({
507
+ path: envLocal,
508
+ override: true
509
+ });
510
+ }
511
+ const cfgFile = path.resolve(rootCwd, "vettvangur.config.mjs");
512
+ const mod = await import(pathToFileURL(cfgFile).href);
513
+ const config = mod.default ?? mod;
514
+ const root = rootCwd;
515
+
516
+ // Accept either config.figma.key or env (FIGMA_KEY/FIGMA_TOKEN); file from config or FIGMA_FILE
517
+ const figmaKey = config?.figma?.key ?? process$1.env.FIGMA_KEY ?? process$1.env.FIGMA_TOKEN ?? null;
518
+ const figmaFile = config?.figma?.file ?? process$1.env.FIGMA_FILE ?? null;
519
+ const paths = {
520
+ root,
521
+ dest: path.resolve(root, req("dest", config.dest)),
522
+ public: path.resolve(root, req("public", config.public)),
523
+ assets: path.resolve(root, req("assets", config.assets)),
524
+ styles: path.resolve(root, req("styles.path", config.styles.path)),
525
+ styleEntries: req("styles.entries", config.styles.entries).map(p => path.isAbsolute(p) ? p : path.resolve(root, p)),
526
+ scripts: path.resolve(root, req("scripts.path", config.scripts.path)),
527
+ scriptEntries: req("scripts.entries", config.scripts.entries).map(p => path.isAbsolute(p) ? p : path.resolve(root, p)),
528
+ views: path.resolve(root, req("views", config.views)),
529
+ watch: config.watch,
530
+ figma: {
531
+ key: req("figma.key", figmaKey),
532
+ file: req("figma.file", figmaFile)
533
+ }
534
+ };
535
+
536
+ // ---- HTTPS (safe, never throws) --------------------------------------------
537
+ function makeHttpsCfg(cfg) {
538
+ const ds = cfg?.devserver ?? {};
539
+ const h = ds.https;
540
+ if (h === false) return false;
541
+ if (h && typeof h === "object") {
542
+ if (h.key && h.cert) return {
543
+ key: h.key,
544
+ cert: h.cert
545
+ };
546
+ if (h.keyfile && h.certfile) {
547
+ try {
548
+ if (fs.existsSync(h.keyfile) && fs.existsSync(h.certfile)) {
549
+ return {
550
+ key: fs.readFileSync(h.keyfile),
551
+ cert: fs.readFileSync(h.certfile)
552
+ };
553
+ }
554
+ } catch {
555
+ /* swallow */
556
+ }
557
+ return false;
558
+ }
559
+ }
560
+ if (ds.keyfile && ds.certfile) {
561
+ try {
562
+ if (fs.existsSync(ds.keyfile) && fs.existsSync(ds.certfile)) {
563
+ return {
564
+ key: fs.readFileSync(ds.keyfile),
565
+ cert: fs.readFileSync(ds.certfile)
566
+ };
567
+ }
568
+ } catch {
569
+ /* swallow */
570
+ }
571
+ }
572
+ return false;
573
+ }
574
+ makeHttpsCfg(config);
575
+
576
+ // ---- flags -----------------------------------------------------------------
577
+ ({
578
+ useSass: config.styles?.type === "sass",
579
+ useTailwind: config.styles?.type === "tailwind"
580
+ });
581
+
582
+ // figma-exporter.mjs
583
+ // Usage: import exporter from './figma-exporter.mjs'; const json = await exporter('<FILE_KEY>');
584
+ // Requires: FIGMA_TOKEN env (or hardcode TOKEN below)
585
+ const tag$1 = chalk.cyan('[design-system]');
586
+
587
+ // ---------- utils ----------
588
+ const toKebab = s => s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
589
+ const hex2 = n => n.toString(16).padStart(2, "0");
590
+ function rgba255(c) {
591
+ const a = typeof c.a === "number" ? c.a : 1;
592
+ return {
593
+ r: Math.round(c.r * 255),
594
+ g: Math.round(c.g * 255),
595
+ b: Math.round(c.b * 255),
596
+ a
597
+ };
598
+ }
599
+ function colorToHexRgba(c) {
600
+ const r = Math.round(c.r * 255),
601
+ g = Math.round(c.g * 255),
602
+ b = Math.round(c.b * 255);
603
+ const a = typeof c.a === "number" ? c.a : 1;
604
+ return `#${hex2(r)}${hex2(g)}${hex2(b)}${a < 1 ? hex2(Math.round(a * 255)) : ""}`;
605
+ }
606
+
607
+ // ---------- text metric shims ----------
608
+ function readLineHeight(style) {
609
+ const s = style || {};
610
+ // 1) New object form
611
+ if (s.lineHeight && typeof s.lineHeight === "object") {
612
+ const u = s.lineHeight.unit || s.lineHeight.lineHeightUnit;
613
+ const v = s.lineHeight.value || s.lineHeight.lineHeightPx || s.lineHeight.percent;
614
+ if ((u === "PERCENT" || u === "FONT_SIZE_%") && typeof v === "number" && v > 0) {
615
+ return {
616
+ unit: "%",
617
+ value: v
618
+ };
619
+ }
620
+ if (u === "PIXELS" && typeof v === "number" && v > 0) {
621
+ return {
622
+ unit: "px",
623
+ value: v
624
+ };
625
+ }
626
+ }
627
+
628
+ // 2) Legacy percent fields (prefer percent over px)
629
+ if (typeof s.lineHeightPercentFontSize === "number" && s.lineHeightPercentFontSize > 0) {
630
+ return {
631
+ unit: "%",
632
+ value: s.lineHeightPercentFontSize
633
+ };
634
+ }
635
+ if (typeof s.lineHeightPercent === "number" && s.lineHeightPercent > 0) {
636
+ return {
637
+ unit: "%",
638
+ value: s.lineHeightPercent
639
+ };
640
+ }
641
+
642
+ // 3) If unit is percent but only px is present, derive percent
643
+ if ((s.lineHeightUnit === "FONT_SIZE_%" || s.lineHeightUnit === "PERCENT") && typeof s.lineHeightPx === "number" && typeof s.fontSize === "number" && s.fontSize > 0) {
644
+ const pct = s.lineHeightPx / s.fontSize * 100;
645
+ return {
646
+ unit: "%",
647
+ value: Math.round(pct * 100) / 100
648
+ };
649
+ }
650
+
651
+ // 4) Plain px
652
+ if (typeof s.lineHeightPx === "number" && s.lineHeightPx > 0) {
653
+ return {
654
+ unit: "px",
655
+ value: s.lineHeightPx
656
+ };
657
+ }
658
+
659
+ // 5) Last-resort derivation from numeric lineHeight + fontSize
660
+ if (typeof s.lineHeight === "number" && typeof s.fontSize === "number" && s.fontSize > 0) {
661
+ const pct = s.lineHeight / s.fontSize * 100;
662
+ return {
663
+ unit: "%",
664
+ value: Math.round(pct * 100) / 100
665
+ };
666
+ }
667
+ return null;
668
+ }
669
+ function readLetterSpacing(style) {
670
+ if (style?.letterSpacing && typeof style.letterSpacing === "object") {
671
+ const u = style.letterSpacing.unit || style.letterSpacingUnit;
672
+ const v = style.letterSpacing.value || style.letterSpacingPx || style.letterSpacing.percent;
673
+ if (u === "PIXELS" && typeof v === "number") return {
674
+ unit: "px",
675
+ value: v
676
+ };
677
+ if (u === "PERCENT" && typeof v === "number") return {
678
+ unit: "%",
679
+ value: v
680
+ };
681
+ }
682
+ if (typeof style?.letterSpacing === "number" && style?.letterSpacingUnit === "PIXELS") return {
683
+ unit: "px",
684
+ value: style.letterSpacing
685
+ };
686
+ if (typeof style?.letterSpacingPercent === "number") return {
687
+ unit: "%",
688
+ value: style.letterSpacingPercent
689
+ };
690
+ if (typeof style?.letterSpacingPercentFontSize === "number") return {
691
+ unit: "%",
692
+ value: style.letterSpacingPercentFontSize
693
+ };
694
+ return null;
695
+ }
696
+
697
+ // ---------- HTTP ----------
698
+ async function jget(url) {
699
+ const HEADERS = {
700
+ "X-Figma-Token": paths.figma.key,
701
+ "Content-Type": "application/json"
702
+ };
703
+ const r = await fetch(url, {
704
+ headers: HEADERS
705
+ });
706
+ if (!r.ok) {
707
+ const body = await r.text().catch(() => "");
708
+ throw new Error(`${r.status} ${r.statusText} – ${url}\n${body}`);
709
+ }
710
+ return r.json();
711
+ }
712
+
713
+ // ---------- variables (optional; Enterprise + file_variables:read) ----------
714
+ async function fetchVariablesLocal(fileKey) {
715
+ console.log(`${tag$1} fetching variables...`);
716
+ try {
717
+ const res = await jget(`https://api.figma.com/v1/files/${fileKey}/variables/local`);
718
+ const meta = res?.meta ?? {};
719
+ const vars = meta.variables ?? {};
720
+ const cols = meta.variableCollections ?? {};
721
+ const byCollection = {};
722
+ for (const colId of Object.keys(cols)) {
723
+ const c = cols[colId];
724
+ const colKey = toKebab(c.name);
725
+ byCollection[colKey] = {
726
+ id: c.id,
727
+ name: c.name,
728
+ modes: (c.modes || []).map(m => ({
729
+ id: m.modeId,
730
+ name: m.name,
731
+ key: toKebab(m.name)
732
+ })),
733
+ tokens: {}
734
+ };
735
+ }
736
+ function resolveAlias(value, depth = 0) {
737
+ if (!value || typeof value !== "object") return value;
738
+ if (value.type === "VARIABLE_ALIAS" && value.id && depth < 20) {
739
+ const aliased = vars[value.id];
740
+ if (!aliased) return value;
741
+ const coll = cols[aliased.variableCollectionId];
742
+ const modeOrder = (coll?.modes || []).map(m => m.modeId);
743
+ const pick = modeOrder[0] || Object.keys(aliased.valuesByMode || {})[0];
744
+ return resolveAlias(aliased.valuesByMode?.[pick], depth + 1);
745
+ }
746
+ return value;
747
+ }
748
+ for (const vId of Object.keys(vars)) {
749
+ const v = vars[vId];
750
+ const coll = cols[v.variableCollectionId];
751
+ if (!coll) continue;
752
+ const colKey = toKebab(coll.name);
753
+ const nameKey = toKebab(v.name);
754
+ const token = {
755
+ id: v.id,
756
+ name: v.name,
757
+ type: v.resolvedType,
758
+ values: {}
759
+ };
760
+ for (const [modeId, raw] of Object.entries(v.valuesByMode || {})) {
761
+ const mode = (coll.modes || []).find(m => m.modeId === modeId);
762
+ if (!mode) continue;
763
+ const val = resolveAlias(raw);
764
+ if (v.resolvedType === "FLOAT" || v.resolvedType === "BOOLEAN" || v.resolvedType === "STRING") {
765
+ token.values[mode.name] = val ?? null;
766
+ } else if (v.resolvedType === "COLOR" && val && typeof val === "object" && "r" in val) {
767
+ token.values[mode.name] = {
768
+ hex: colorToHexRgba(val),
769
+ rgba: rgba255(val)
770
+ };
771
+ } else {
772
+ token.values[mode.name] = null;
773
+ }
774
+ }
775
+ byCollection[colKey].tokens[nameKey] = token;
776
+ }
777
+ return byCollection;
778
+ } catch {
779
+ return {};
780
+ }
781
+ }
782
+
783
+ // ---------- styles (two sources) ----------
784
+ async function fetchStyleNodeIds_fromFile(fileKey) {
785
+ const file = await jget(`https://api.figma.com/v1/files/${fileKey}`);
786
+ return file?.styles || {};
787
+ }
788
+ async function fetchStyleNodeIds_published(fileKey) {
789
+ try {
790
+ const res = await jget(`https://api.figma.com/v1/files/${fileKey}/styles`);
791
+ const arr = Array.isArray(res?.meta?.styles) ? res.meta.styles : [];
792
+ const map = {};
793
+ for (const s of arr) map[s.key] = s;
794
+ return map;
795
+ } catch {
796
+ return {};
797
+ }
798
+ }
799
+ async function fetchMergedStyleNodeIds(fileKey) {
800
+ const a = await fetchStyleNodeIds_fromFile(fileKey);
801
+ const b = await fetchStyleNodeIds_published(fileKey);
802
+ return {
803
+ ...a,
804
+ ...b
805
+ };
806
+ }
807
+
808
+ // ---------- nodes (batched) ----------
809
+ async function fetchNodesByIds(fileKey, allIds) {
810
+ console.log(`${tag$1} reading nodes by id...`);
811
+ const ids = Array.from(new Set((allIds || []).filter(Boolean).map(String).map(s => s.trim()).filter(s => s.length > 0)));
812
+ const out = [];
813
+ const CHUNK = 50;
814
+ for (let i = 0; i < ids.length; i += CHUNK) {
815
+ const slice = ids.slice(i, i + CHUNK);
816
+ const qs = slice.map(encodeURIComponent).join(",");
817
+ const url = `https://api.figma.com/v1/files/${fileKey}/nodes?ids=${qs}`;
818
+ const res = await jget(url);
819
+ const nodes = res?.nodes || {};
820
+ for (const k of Object.keys(nodes)) {
821
+ const doc = nodes[k]?.document;
822
+ if (doc) out.push(doc);
823
+ }
824
+ }
825
+ return out;
826
+ }
827
+
828
+ // ---------- text props derivation ----------
829
+ function weightFromStyleName(s) {
830
+ if (!s) return undefined;
831
+ const x = s.toLowerCase();
832
+ if (/\b(100|thin)\b/.test(x)) return 100;
833
+ if (/\b(200|extralight|ultralight)\b/.test(x)) return 200;
834
+ if (/\b(300|light)\b/.test(x)) return 300;
835
+ if (/\b(400|regular|book|normal)\b/.test(x)) return 400;
836
+ if (/\b(500|medium)\b/.test(x)) return 500;
837
+ if (/\b(600|semibold|demibold)\b/.test(x)) return 600;
838
+ if (/\b(700|bold)\b/.test(x)) return 700;
839
+ if (/\b(800|extrabold|ultrabold|heavy)\b/.test(x)) return 800;
840
+ if (/\b(900|black|heavy)\b/.test(x)) return 900;
841
+ const m = s.match(/\b(100|200|300|400|500|600|700|800|900)\b/);
842
+ return m ? Number(m[1]) : undefined;
843
+ }
844
+ function deriveFontStyle(style) {
845
+ if (typeof style?.fontStyle === "string" && style.fontStyle.trim()) return style.fontStyle;
846
+ const ps = style?.fontPostScriptName || "";
847
+ const tail = ps.includes("-") ? ps.split("-")[1] : ps;
848
+ if (/italic/i.test(tail)) return "Italic";
849
+ if (/oblique/i.test(tail)) return "Oblique";
850
+ if (/regular|book|normal/i.test(tail)) return "Regular";
851
+ const token = (tail.match(/[A-Za-z]+$/) || [])[0];
852
+ return token ? token[0].toUpperCase() + token.slice(1) : undefined;
853
+ }
854
+ function readTextProps(n) {
855
+ const s = n.style || n;
856
+ const styleName = s.fontStyle || s.fontPostScriptName || "";
857
+ return {
858
+ fontFamily: s.fontFamily || n.fontFamily,
859
+ fontStyle: deriveFontStyle(s),
860
+ fontWeight: typeof s.fontWeight === "number" ? s.fontWeight : weightFromStyleName(styleName),
861
+ fontSize: s.fontSize ?? n.fontSize,
862
+ lineHeight: readLineHeight(s),
863
+ letterSpacing: readLetterSpacing(s),
864
+ paragraphSpacing: typeof s.paragraphSpacing === "number" ? s.paragraphSpacing : typeof n.paragraphSpacing === "number" ? n.paragraphSpacing : 0,
865
+ textCase: s.textCase || n.textCase || "ORIGINAL",
866
+ textDecoration: s.textDecoration || n.textDecoration || "NONE"
867
+ };
868
+ }
869
+
870
+ // ---------- normalizers ----------
871
+ function emitPaintStylesJson(styleMetas, nodes) {
872
+ const byId = {};
873
+ for (const n of nodes) {
874
+ const paints = Array.isArray(n.paints) ? n.paints : Array.isArray(n.fills) ? n.fills : null;
875
+ if (!paints) continue;
876
+ const meta = Object.values(styleMetas).find(m => m.node_id === n.id && m.style_type === "FILL");
877
+ if (!meta) continue;
878
+ const solids = [];
879
+ for (const p of paints) {
880
+ if (p?.type !== "SOLID" || p?.visible === false) continue;
881
+ const a = typeof p.opacity === "number" ? p.opacity : 1;
882
+ solids.push({
883
+ hex: colorToHexRgba({
884
+ ...p.color,
885
+ a
886
+ }),
887
+ rgba: rgba255({
888
+ ...p.color,
889
+ a
890
+ })
891
+ });
892
+ }
893
+ if (solids.length) {
894
+ const key = toKebab(meta.name);
895
+ byId[key] = {
896
+ id: meta.key || meta.node_id,
897
+ name: meta.name,
898
+ paints: solids
899
+ };
900
+ }
901
+ }
902
+ console.log(`${tag$1} finished reading paint styles`);
903
+ return byId;
904
+ }
905
+ function emitTextStylesJson(styleMetas, nodes) {
906
+ const out = {};
907
+ for (const n of nodes) {
908
+ const meta = Object.values(styleMetas).find(m => m.node_id === n.id && m.style_type === "TEXT");
909
+ if (!meta) continue;
910
+ const key = toKebab(meta.name);
911
+ const p = readTextProps(n);
912
+ out[key] = {
913
+ id: meta.key || meta.node_id,
914
+ name: meta.name,
915
+ fontFamily: p.fontFamily,
916
+ fontStyle: p.fontStyle,
917
+ fontWeight: p.fontWeight,
918
+ fontSize: p.fontSize,
919
+ lineHeight: p.lineHeight,
920
+ letterSpacing: p.letterSpacing,
921
+ paragraphSpacing: p.paragraphSpacing,
922
+ textCase: p.textCase,
923
+ textDecoration: p.textDecoration
924
+ };
925
+ }
926
+ console.log(`${tag$1} finished reading text styles`);
927
+ return out;
928
+ }
929
+ function effectToJson(e) {
930
+ if (e?.visible === false) return null;
931
+ if (e?.type === "DROP_SHADOW" || e?.type === "INNER_SHADOW") {
932
+ const spread = typeof e.spread === "number" ? Math.round(e.spread) : 0;
933
+ return {
934
+ kind: e.type,
935
+ offset: {
936
+ x: Math.round(e.offset?.x ?? 0),
937
+ y: Math.round(e.offset?.y ?? 0)
938
+ },
939
+ radius: Math.round(e.radius ?? 0),
940
+ spread,
941
+ color: {
942
+ hex: colorToHexRgba(e.color),
943
+ rgba: rgba255(e.color)
944
+ }
945
+ };
946
+ }
947
+ if (e?.type === "LAYER_BLUR" || e?.type === "BACKGROUND_BLUR") {
948
+ return {
949
+ kind: e.type,
950
+ radius: Math.round(e.radius ?? 0)
951
+ };
952
+ }
953
+ return null;
954
+ }
955
+ function emitEffectStylesJson(styleMetas, nodes) {
956
+ const out = {};
957
+ for (const n of nodes) {
958
+ const meta = Object.values(styleMetas).find(m => m.node_id === n.id && m.style_type === "EFFECT");
959
+ if (!meta) continue;
960
+ const list = [];
961
+ const effects = Array.isArray(n.effects) ? n.effects : [];
962
+ for (const e of effects) {
963
+ const obj = effectToJson(e);
964
+ if (obj) list.push(obj);
965
+ }
966
+ if (list.length) {
967
+ const key = toKebab(meta.name);
968
+ out[key] = {
969
+ id: meta.key || meta.node_id,
970
+ name: meta.name,
971
+ effects: list
972
+ };
973
+ }
974
+ }
975
+ console.log(`${tag$1} finished reading effect styles`);
976
+ return out;
977
+ }
978
+
979
+ // ---------- exporter ----------
980
+ const exporter = async function (fileKey) {
981
+ if (!fileKey) throw new Error("Missing FILE_KEY");
982
+ console.log(`${tag$1} reading from figma`);
983
+ const variablesByCollection = await fetchVariablesLocal(fileKey);
984
+ const stylesMap = await fetchMergedStyleNodeIds(fileKey);
985
+ const styleNodeIds = Object.values(stylesMap).filter(s => s && (s.style_type === "FILL" || s.style_type === "TEXT" || s.style_type === "EFFECT")).map(s => s.node_id);
986
+ if (styleNodeIds.length === 0) {
987
+ return {
988
+ paint: {},
989
+ text: {},
990
+ effect: {},
991
+ variables: variablesByCollection
992
+ };
993
+ }
994
+ const styleNodes = await fetchNodesByIds(fileKey, styleNodeIds);
995
+ const paint = emitPaintStylesJson(stylesMap, styleNodes);
996
+ const text = emitTextStylesJson(stylesMap, styleNodes);
997
+ const effect = emitEffectStylesJson(stylesMap, styleNodes);
998
+ console.log('text', text);
999
+ return {
1000
+ paint,
1001
+ text,
1002
+ effect,
1003
+ variables: variablesByCollection
1004
+ };
1005
+ };
1006
+
1007
+ const tag = chalk.cyan('[design-system]');
1008
+
1009
+ /*
1010
+ * This needs to genrate:
1011
+ * variables:
1012
+ * color.css
1013
+ * font.css
1014
+ * radius.css
1015
+ * shadow.css
1016
+ * typography.css
1017
+ * core:
1018
+ * body.css
1019
+ * headline.css
1020
+ */
1021
+
1022
+ async function generateTailwind() {
1023
+ console.log(`${chalk.cyan(tag)} starting tailwind...`);
1024
+ const figmaStyles = await exporter(paths.figma.file);
1025
+ await generateColors(figmaStyles.paint);
1026
+ console.log(`${tag} completed tailwind`);
1027
+ }
1028
+
1029
+ // ------- VARIABLES -------
1030
+ async function generateColors(data) {
1031
+ console.log(`${tag} generating colors...`);
1032
+ const outDir = path.join(paths.styles, 'config');
1033
+ await fs$1.mkdir(outDir, {
1034
+ recursive: true
1035
+ });
1036
+ const lines = [];
1037
+ for (const [key, entry] of Object.entries(data ?? {})) {
1038
+ const paints = entry?.paints ?? [];
1039
+ const p = paints[0] ?? {};
1040
+ // exporter already normalizes; prefer .hex/.value, else stringify fallback
1041
+ const value = (p.hex && String(p.hex)) ?? (p.value && String(p.value)) ?? (p.css && String(p.css)) ?? String(p);
1042
+ if (!value) {
1043
+ continue;
1044
+ }
1045
+ lines.push(` --color-${key}: ${value};`);
1046
+ }
1047
+ const css = `/* Auto-generated. Do not edit by hand. */
1048
+ @theme {
1049
+ ${lines.join('\n')}
1050
+ }
1051
+ `;
1052
+ const filePath = path.join(outDir, 'color.css');
1053
+ await fs$1.writeFile(filePath, css, 'utf8');
1054
+ console.log(`${tag} finished generating colors`);
1055
+ }
1056
+
1057
+ export { generateTailwind as default, generateTailwind };