@vettvangur/design-system 0.0.21 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -1,8 +1,1111 @@
1
1
  #!/usr/bin/env node
2
- import process from 'node:process';
2
+ import process$1 from 'node:process';
3
3
  import chalk from 'chalk';
4
4
  import boxen from 'boxen';
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 path from 'node:path';
11
+ import { pathToFileURL } from 'node:url';
5
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
+ // ---------- component + component-set discovery ----------
809
+ function walk(node, fn) {
810
+ if (!node || typeof node !== 'object') return;
811
+ fn(node);
812
+ const children = Array.isArray(node.children) ? node.children : [];
813
+ for (const c of children) walk(c, fn);
814
+ }
815
+ async function fetchComponentAndSetIds(fileKey) {
816
+ console.log(`${tag$1} scanning for components...`);
817
+ const file = await jget(`https://api.figma.com/v1/files/${fileKey}`);
818
+ const ids = new Set();
819
+ walk(file?.document, n => {
820
+ if (n?.type === 'COMPONENT' || n?.type === 'COMPONENT_SET') {
821
+ ids.add(n.id);
822
+ }
823
+ });
824
+ return Array.from(ids);
825
+ }
826
+
827
+ // ---------- nodes (batched) ----------
828
+ async function fetchNodesByIds(fileKey, allIds, type) {
829
+ console.log(`${tag$1} reading ${type} nodes by id...`);
830
+ const ids = Array.from(new Set((allIds || []).filter(Boolean).map(String).map(s => s.trim()).filter(s => s.length > 0)));
831
+ const out = [];
832
+ const CHUNK = 50;
833
+ for (let i = 0; i < ids.length; i += CHUNK) {
834
+ const slice = ids.slice(i, i + CHUNK);
835
+ const qs = slice.map(encodeURIComponent).join(",");
836
+ const url = `https://api.figma.com/v1/files/${fileKey}/nodes?ids=${qs}`;
837
+ const res = await jget(url);
838
+ const nodes = res?.nodes || {};
839
+ for (const k of Object.keys(nodes)) {
840
+ const doc = nodes[k]?.document;
841
+ if (doc) out.push(doc);
842
+ }
843
+ }
844
+ return out;
845
+ }
846
+
847
+ // ---------- text props derivation ----------
848
+ function weightFromStyleName(s) {
849
+ if (!s) return undefined;
850
+ const x = s.toLowerCase();
851
+ if (/\b(100|thin)\b/.test(x)) return 100;
852
+ if (/\b(200|extralight|ultralight)\b/.test(x)) return 200;
853
+ if (/\b(300|light)\b/.test(x)) return 300;
854
+ if (/\b(400|regular|book|normal)\b/.test(x)) return 400;
855
+ if (/\b(500|medium)\b/.test(x)) return 500;
856
+ if (/\b(600|semibold|demibold)\b/.test(x)) return 600;
857
+ if (/\b(700|bold)\b/.test(x)) return 700;
858
+ if (/\b(800|extrabold|ultrabold|heavy)\b/.test(x)) return 800;
859
+ if (/\b(900|black|heavy)\b/.test(x)) return 900;
860
+ const m = s.match(/\b(100|200|300|400|500|600|700|800|900)\b/);
861
+ return m ? Number(m[1]) : undefined;
862
+ }
863
+ function deriveFontStyle(style) {
864
+ if (typeof style?.fontStyle === "string" && style.fontStyle.trim()) return style.fontStyle;
865
+ const ps = style?.fontPostScriptName || "";
866
+ const tail = ps.includes("-") ? ps.split("-")[1] : ps;
867
+ if (/italic/i.test(tail)) return "Italic";
868
+ if (/oblique/i.test(tail)) return "Oblique";
869
+ if (/regular|book|normal/i.test(tail)) return "Regular";
870
+ const token = (tail.match(/[A-Za-z]+$/) || [])[0];
871
+ return token ? token[0].toUpperCase() + token.slice(1) : undefined;
872
+ }
873
+ function readTextProps(n) {
874
+ const s = n.style || n;
875
+ const styleName = s.fontStyle || s.fontPostScriptName || "";
876
+ return {
877
+ fontFamily: s.fontFamily || n.fontFamily,
878
+ fontStyle: deriveFontStyle(s),
879
+ fontWeight: typeof s.fontWeight === "number" ? s.fontWeight : weightFromStyleName(styleName),
880
+ fontSize: s.fontSize ?? n.fontSize,
881
+ lineHeight: readLineHeight(s),
882
+ letterSpacing: readLetterSpacing(s),
883
+ paragraphSpacing: typeof s.paragraphSpacing === "number" ? s.paragraphSpacing : typeof n.paragraphSpacing === "number" ? n.paragraphSpacing : 0,
884
+ textCase: s.textCase || n.textCase || "ORIGINAL",
885
+ textDecoration: s.textDecoration || n.textDecoration || "NONE"
886
+ };
887
+ }
888
+
889
+ // ---------- normalizers: styles ----------
890
+ function emitPaintStylesJson(styleMetas, nodes) {
891
+ console.log(`${tag$1} reading paint styles...`);
892
+ const byId = {};
893
+ for (const n of nodes) {
894
+ const paints = Array.isArray(n.paints) ? n.paints : Array.isArray(n.fills) ? n.fills : null;
895
+ if (!paints) continue;
896
+ const meta = Object.values(styleMetas).find(m => m.node_id === n.id && m.style_type === "FILL");
897
+ if (!meta) continue;
898
+ const solids = [];
899
+ for (const p of paints) {
900
+ if (p?.type !== "SOLID" || p?.visible === false) continue;
901
+ const a = typeof p.opacity === "number" ? p.opacity : 1;
902
+ solids.push({
903
+ hex: colorToHexRgba({
904
+ ...p.color,
905
+ a
906
+ }),
907
+ rgba: rgba255({
908
+ ...p.color,
909
+ a
910
+ })
911
+ });
912
+ }
913
+ if (solids.length) {
914
+ const key = toKebab(meta.name);
915
+ byId[key] = {
916
+ id: meta.key || meta.node_id,
917
+ name: meta.name,
918
+ paints: solids
919
+ };
920
+ }
921
+ }
922
+ console.log(`${tag$1} finished reading paint styles`);
923
+ return byId;
924
+ }
925
+ function emitTextStylesJson(styleMetas, nodes) {
926
+ console.log(`${tag$1} reading text styles...`);
927
+ const out = {};
928
+ for (const n of nodes) {
929
+ const meta = Object.values(styleMetas).find(m => m.node_id === n.id && m.style_type === "TEXT");
930
+ if (!meta) continue;
931
+ const key = toKebab(meta.name);
932
+ const p = readTextProps(n);
933
+ out[key] = {
934
+ id: meta.key || meta.node_id,
935
+ name: meta.name,
936
+ fontFamily: p.fontFamily,
937
+ fontStyle: p.fontStyle,
938
+ fontWeight: p.fontWeight,
939
+ fontSize: p.fontSize,
940
+ lineHeight: p.lineHeight,
941
+ letterSpacing: p.letterSpacing,
942
+ paragraphSpacing: p.paragraphSpacing,
943
+ textCase: p.textCase,
944
+ textDecoration: p.textDecoration
945
+ };
946
+ }
947
+ console.log(`${tag$1} finished reading text styles`);
948
+ return out;
949
+ }
950
+ function effectToJson(e) {
951
+ if (e?.visible === false) return null;
952
+ if (e?.type === "DROP_SHADOW" || e?.type === "INNER_SHADOW") {
953
+ const spread = typeof e.spread === "number" ? Math.round(e.spread) : 0;
954
+ return {
955
+ kind: e.type,
956
+ offset: {
957
+ x: Math.round(e.offset?.x ?? 0),
958
+ y: Math.round(e.offset?.y ?? 0)
959
+ },
960
+ radius: Math.round(e.radius ?? 0),
961
+ spread,
962
+ color: {
963
+ hex: colorToHexRgba(e.color),
964
+ rgba: rgba255(e.color)
965
+ }
966
+ };
967
+ }
968
+ if (e?.type === "LAYER_BLUR" || e?.type === "BACKGROUND_BLUR") {
969
+ return {
970
+ kind: e.type,
971
+ radius: Math.round(e.radius ?? 0)
972
+ };
973
+ }
974
+ return null;
975
+ }
976
+ function emitEffectStylesJson(styleMetas, nodes) {
977
+ console.log(`${tag$1} reading effect styles...`);
978
+ const out = {};
979
+ for (const n of nodes) {
980
+ const meta = Object.values(styleMetas).find(m => m.node_id === n.id && m.style_type === "EFFECT");
981
+ if (!meta) continue;
982
+ const list = [];
983
+ const effects = Array.isArray(n.effects) ? n.effects : [];
984
+ for (const e of effects) {
985
+ const obj = effectToJson(e);
986
+ if (obj) list.push(obj);
987
+ }
988
+ if (list.length) {
989
+ const key = toKebab(meta.name);
990
+ out[key] = {
991
+ id: meta.key || meta.node_id,
992
+ name: meta.name,
993
+ effects: list
994
+ };
995
+ }
996
+ }
997
+ console.log(`${tag$1} finished reading effect styles`);
998
+ return out;
999
+ }
1000
+
1001
+ // ---------- normalizers: component properties ----------
1002
+ function normalizeComponentPropDef(def) {
1003
+ // Figma REST: { name, type, defaultValue, variantOptions?, preferredValues? ... }
1004
+ const base = {
1005
+ name: def?.name ?? '',
1006
+ type: def?.type ?? 'TEXT'
1007
+ };
1008
+ if (def?.type === 'TEXT') {
1009
+ return {
1010
+ ...base,
1011
+ default: def?.defaultValue ?? ''
1012
+ };
1013
+ }
1014
+ if (def?.type === 'BOOLEAN') {
1015
+ return {
1016
+ ...base,
1017
+ default: !!def?.defaultValue
1018
+ };
1019
+ }
1020
+ if (def?.type === 'VARIANT') {
1021
+ const opts = Array.isArray(def?.variantOptions) ? def.variantOptions : [];
1022
+ const defVal = typeof def?.defaultValue === 'string' ? def.defaultValue : opts[0] ?? '';
1023
+ return {
1024
+ ...base,
1025
+ options: opts,
1026
+ default: defVal
1027
+ };
1028
+ }
1029
+ if (def?.type === 'INSTANCE_SWAP') {
1030
+ // preferredValues is an array of component IDs/keys; Figma may return "preferredValues"
1031
+ const prefs = Array.isArray(def?.preferredValues) ? def.preferredValues : [];
1032
+ const defVal = def?.defaultValue ?? null;
1033
+ return {
1034
+ ...base,
1035
+ preferredValues: prefs,
1036
+ default: defVal
1037
+ };
1038
+ }
1039
+ return {
1040
+ ...base,
1041
+ default: def?.defaultValue ?? null
1042
+ };
1043
+ }
1044
+ function emitComponentPropsJson(nodes) {
1045
+ console.log(`${tag$1} reading component properties...`);
1046
+ const out = {};
1047
+ for (const n of nodes) {
1048
+ if (n?.type !== 'COMPONENT' && n?.type !== 'COMPONENT_SET') continue;
1049
+ const defs = n.componentPropertyDefinitions || {};
1050
+ const props = {};
1051
+ for (const propId of Object.keys(defs)) {
1052
+ const def = defs[propId];
1053
+ const key = toKebab(def?.name ?? propId);
1054
+ props[key] = {
1055
+ id: propId,
1056
+ ...normalizeComponentPropDef(def)
1057
+ };
1058
+ }
1059
+ if (Object.keys(props).length > 0) {
1060
+ const key = toKebab(n.name || n.id);
1061
+ out[key] = {
1062
+ id: n.id,
1063
+ name: n.name,
1064
+ kind: n.type,
1065
+ // COMPONENT or COMPONENT_SET
1066
+ props
1067
+ };
1068
+ }
1069
+ }
1070
+ console.log(`${tag$1} finished reading component properties`);
1071
+ return out;
1072
+ }
1073
+
1074
+ // ---------- exporter ----------
1075
+ const exporter = async function (fileKey) {
1076
+ if (!fileKey) throw new Error("Missing FILE_KEY");
1077
+ console.log(`${tag$1} reading from figma`);
1078
+ const variablesByCollection = await fetchVariablesLocal(fileKey);
1079
+ const stylesMap = await fetchMergedStyleNodeIds(fileKey);
1080
+ 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);
1081
+ const componentIds = await fetchComponentAndSetIds(fileKey);
1082
+
1083
+ // Early-return case when no styles and no components
1084
+ if (styleNodeIds.length === 0 && componentIds.length === 0) {
1085
+ return {
1086
+ paint: {},
1087
+ text: {},
1088
+ effect: {},
1089
+ variables: variablesByCollection,
1090
+ components: {}
1091
+ };
1092
+ }
1093
+ const styleNodes = styleNodeIds.length ? await fetchNodesByIds(fileKey, styleNodeIds, 'style') : [];
1094
+ const componentNodes = componentIds.length ? await fetchNodesByIds(fileKey, componentIds, 'component') : [];
1095
+ const paint = emitPaintStylesJson(stylesMap, styleNodes);
1096
+ const text = emitTextStylesJson(stylesMap, styleNodes);
1097
+ const effect = emitEffectStylesJson(stylesMap, styleNodes);
1098
+ const components = emitComponentPropsJson(componentNodes);
1099
+ return {
1100
+ paint,
1101
+ text,
1102
+ effect,
1103
+ variables: variablesByCollection,
1104
+ components
1105
+ };
1106
+ };
1107
+
1108
+ const tag = chalk.cyan('[design-system]');
6
1109
  function printBanner() {
7
1110
  const lines = ['@vettvangur/design-system', '', 'Syncing with Figma… and your imposter syndrome'].join("\n");
8
1111
  console.log(boxen(lines, {
@@ -17,57 +1120,59 @@ function printBanner() {
17
1120
  borderColor: "cyan"
18
1121
  }));
19
1122
  }
20
- console.clear();
21
- printBanner();
22
- const [cmd, ...rest] = process.argv.slice(2);
23
- try {
24
- switch (cmd) {
25
- case 'astro':
26
- {
27
- const {
28
- default: generateAstro
29
- } = await import('./generate-astro-BDPRsAax.js');
30
- await generateAstro();
31
- break;
32
- }
33
- case 'razor':
34
- {
35
- const flag = rest.find(a => typeof a === 'string' && a.startsWith('--n='));
36
- if (!flag) {
37
- console.error('Usage: styleguide razor --n=YourNamespace');
38
- process.exit(1);
1123
+ async function readFigma() {
1124
+ const figmaStyles = await exporter(paths.figma.file);
1125
+ return figmaStyles;
1126
+ }
1127
+ (async () => {
1128
+ console.clear();
1129
+ printBanner();
1130
+ const [cmd, ...rest] = process$1.argv.slice(2);
1131
+ try {
1132
+ const {
1133
+ default: generateTailwind
1134
+ } = await import('./generate-tailwind-xFSVHE9T.js');
1135
+ const figma = await readFigma();
1136
+ switch (cmd) {
1137
+ case 'astro':
1138
+ {
1139
+ const {
1140
+ default: generateAstro
1141
+ } = await import('./generate-astro-BDPRsAax.js');
1142
+ await generateTailwind('astro', figma);
1143
+ await generateAstro(figma);
1144
+ break;
39
1145
  }
40
- const ns = flag.slice('--n='.length).trim();
41
- if (!ns) {
42
- console.error('Error: --n= flag provided without a value.\nUsage: styleguide razor --n=YourNamespace');
43
- process.exit(1);
1146
+ case 'razor':
1147
+ {
1148
+ const flag = rest.find(a => typeof a === 'string' && a.startsWith('--n='));
1149
+ if (!flag) {
1150
+ console.error('Usage: styleguide razor --n=YourNamespace');
1151
+ process$1.exit(1);
1152
+ }
1153
+ const ns = flag.slice('--n='.length).trim();
1154
+ if (!ns) {
1155
+ console.error('Error: --n= flag provided without a value.\nUsage: styleguide razor --n=YourNamespace');
1156
+ process$1.exit(1);
1157
+ }
1158
+ const {
1159
+ default: generateRazor
1160
+ } = await import('./generate-razor-BGJZZWfp.js');
1161
+ await generateTailwind('razor', figma);
1162
+ await generateRazor(ns, figma);
1163
+ break;
44
1164
  }
45
- const {
46
- default: generateRazor
47
- } = await import('./generate-razor-THJQBjpk.js');
48
- const {
49
- default: generateTailwind
50
- } = await import('./generate-tailwind-Dri2RfD_.js');
51
- await generateTailwind();
52
- await generateRazor(ns);
53
- break;
54
- }
55
- case 'tailwind':
56
- {
57
- const {
58
- default: generateTailwind
59
- } = await import('./generate-tailwind-Dri2RfD_.js');
60
- await generateTailwind();
61
- break;
62
- }
63
- default:
64
- {
65
- console.log(`${chalk.cyan(tag)} Unknown command. Use: razor, astro or tailwind`);
66
- process.exit(1);
67
- }
1165
+ default:
1166
+ {
1167
+ console.log(`${tag} unknown command. use: razor or astro`);
1168
+ process$1.exit(1);
1169
+ }
1170
+ }
1171
+ console.log('\nHappy coding! ✨\n');
1172
+ } catch (err) {
1173
+ console.error(err?.stack || err);
1174
+ process$1.exit(1);
68
1175
  }
69
- console.log('\nHappy coding! ✨\n');
70
- } catch (err) {
71
- console.error(err?.stack || err);
72
- process.exit(1);
73
- }
1176
+ })();
1177
+
1178
+ export { paths as p };