@digdir/designsystemet 1.12.1 → 1.13.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.
Files changed (43) hide show
  1. package/dist/bin/config.d.ts +4 -4
  2. package/dist/bin/config.d.ts.map +1 -1
  3. package/dist/bin/config.js +133 -28
  4. package/dist/bin/designsystemet.d.ts.map +1 -1
  5. package/dist/bin/designsystemet.js +208 -149
  6. package/dist/src/config.d.ts +2 -2
  7. package/dist/src/config.d.ts.map +1 -1
  8. package/dist/src/config.js +4 -4
  9. package/dist/src/index.js +5 -5
  10. package/dist/src/migrations/beta-to-v1.js +122 -16
  11. package/dist/src/migrations/codemods/css/run.d.ts.map +1 -1
  12. package/dist/src/migrations/codemods/css/run.js +122 -16
  13. package/dist/src/migrations/color-rename-next49.js +122 -16
  14. package/dist/src/migrations/index.js +122 -16
  15. package/dist/src/scripts/update-preview-tokens.d.ts.map +1 -1
  16. package/dist/src/scripts/update-preview-tokens.js +122 -45
  17. package/dist/src/tokens/build.d.ts +2 -1
  18. package/dist/src/tokens/build.d.ts.map +1 -1
  19. package/dist/src/tokens/build.js +128 -62
  20. package/dist/src/tokens/create/files.d.ts +10 -0
  21. package/dist/src/tokens/create/files.d.ts.map +1 -0
  22. package/dist/src/tokens/create/{write.js → files.js} +143 -61
  23. package/dist/src/tokens/create/generators/$designsystemet.js +5 -5
  24. package/dist/src/tokens/format.js +5 -5
  25. package/dist/src/tokens/generate-config.d.ts +0 -1
  26. package/dist/src/tokens/generate-config.d.ts.map +1 -1
  27. package/dist/src/tokens/generate-config.js +145 -26
  28. package/dist/src/tokens/index.js +5 -5
  29. package/dist/src/tokens/process/output/declarations.js +5 -5
  30. package/dist/src/tokens/process/output/theme.js +5 -5
  31. package/dist/src/tokens/process/platform.d.ts +0 -4
  32. package/dist/src/tokens/process/platform.d.ts.map +1 -1
  33. package/dist/src/tokens/types.d.ts +2 -0
  34. package/dist/src/tokens/types.d.ts.map +1 -1
  35. package/dist/src/utils/filesystem.d.ts +40 -0
  36. package/dist/src/utils/filesystem.d.ts.map +1 -0
  37. package/dist/src/utils/filesystem.js +127 -0
  38. package/package.json +6 -6
  39. package/dist/src/tokens/create/write.d.ts +0 -12
  40. package/dist/src/tokens/create/write.d.ts.map +0 -1
  41. package/dist/src/utils.d.ts +0 -18
  42. package/dist/src/utils.d.ts.map +0 -1
  43. package/dist/src/utils.js +0 -70
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // bin/designsystemet.ts
4
+ import path5 from "path";
4
5
  import { Argument, createCommand, program } from "@commander-js/extra-typings";
5
6
  import pc12 from "picocolors";
6
7
  import * as R29 from "ramda";
@@ -415,58 +416,133 @@ var cssVarRename = (dictionary) => ({
415
416
  });
416
417
 
417
418
  // src/migrations/codemods/css/run.ts
418
- import fs2 from "fs";
419
419
  import glob from "fast-glob";
420
420
  import postcss from "postcss";
421
421
 
422
- // src/utils.ts
422
+ // src/utils/filesystem.ts
423
423
  import fs from "fs/promises";
424
+ import path from "path";
424
425
  import pc2 from "picocolors";
425
- var mkdir = async (dir, dry) => {
426
- if (dry) {
427
- console.log(`${pc2.blue("mkdir")} ${dir}`);
428
- return Promise.resolve();
429
- }
430
- const exists = await fs.access(dir, fs.constants.F_OK).then(() => true).catch(() => false);
431
- if (exists) {
432
- return Promise.resolve();
433
- }
434
- return fs.mkdir(dir, { recursive: true });
435
- };
436
- var writeFile = async (path6, data, dry) => {
437
- if (dry) {
438
- console.log(`${pc2.blue("writeFile")} ${path6}`);
439
- return Promise.resolve();
440
- }
441
- return fs.writeFile(path6, data, { encoding: "utf-8" }).catch((error) => {
442
- console.error(pc2.red(`Error writing file: ${path6}`));
443
- console.error(pc2.red(error));
444
- throw error;
445
- });
446
- };
447
- var cleanDir = async (dir, dry) => {
448
- if (dry) {
449
- console.log(`${pc2.blue("cleanDir")} ${dir}`);
450
- return Promise.resolve();
426
+ var FileSystem = class {
427
+ isInitialized = false;
428
+ dry = false;
429
+ verbose = false;
430
+ /** Default working directory is where the process was started */
431
+ workingDir = process.cwd();
432
+ outDir = this.workingDir;
433
+ /** Initialize the file system */
434
+ init({ dry, outdir, verbose }) {
435
+ if (this.isInitialized) {
436
+ console.warn(pc2.yellow("FileSystem is already initialized. Ignoring subsequent init call."));
437
+ return;
438
+ }
439
+ if (dry) {
440
+ console.log(pc2.blue("Initializing FileSystem in dry-run mode. No files will be written."));
441
+ }
442
+ this.dry = dry ?? false;
443
+ this.verbose = verbose ?? false;
444
+ this.outDir = outdir ? path.isAbsolute(outdir) ? outdir : path.join(this.workingDir, outdir) : this.workingDir;
445
+ if (this.verbose) {
446
+ console.log(
447
+ `FileSystem initialized with workingDir: ${pc2.green(this.workingDir)}, outDir: ${pc2.green(this.outDir)}`
448
+ );
449
+ }
450
+ this.isInitialized = true;
451
451
  }
452
- console.log(`
452
+ /**
453
+ * Creates a directory if it does not already exist.
454
+ *
455
+ * @param dir - The path of the directory to create.
456
+ *
457
+ * @returns A promise that resolves when the operation is complete.
458
+ * If the directory already exists or `dry` is `true`, the promise resolves immediately.
459
+ */
460
+ mkdir = async (dir) => {
461
+ if (this.dry) {
462
+ console.log(`${pc2.blue("mkdir")} ${dir}`);
463
+ return Promise.resolve();
464
+ }
465
+ const exists = await fs.access(dir, fs.constants.F_OK).then(() => true).catch(() => false);
466
+ if (exists) {
467
+ return Promise.resolve();
468
+ }
469
+ return fs.mkdir(dir, { recursive: true });
470
+ };
471
+ writeFile = async (path6, data) => {
472
+ if (this.dry) {
473
+ console.log(`${pc2.blue("writeFile")} ${path6}`);
474
+ return Promise.resolve();
475
+ }
476
+ return fs.writeFile(path6, data, { encoding: "utf-8" }).catch((error) => {
477
+ console.error(pc2.red(`Error writing file: ${path6}`));
478
+ console.error(pc2.red(error));
479
+ throw error;
480
+ });
481
+ };
482
+ cp = async (src, dest, filter2) => {
483
+ if (this.dry) {
484
+ console.log(`${pc2.blue("cp")} ${src} ${dest}`);
485
+ return Promise.resolve();
486
+ }
487
+ return fs.cp(src, dest, { recursive: true, filter: filter2 });
488
+ };
489
+ copyFile = async (src, dest) => {
490
+ if (this.dry) {
491
+ console.log(`${pc2.blue("copyFile")} ${src} to ${dest}`);
492
+ return Promise.resolve();
493
+ }
494
+ return fs.copyFile(src, dest);
495
+ };
496
+ cleanDir = async (dir) => {
497
+ if (this.dry) {
498
+ console.log(`${pc2.blue("cleanDir")} ${dir}`);
499
+ return Promise.resolve();
500
+ }
501
+ console.log(`
453
502
  \u{1F525} Cleaning dir ${pc2.red(`${dir.trim()}`)} `);
454
- return fs.rm(dir, { recursive: true, force: true });
455
- };
456
- var readFile = async (path6, dry, allowFileNotFound) => {
457
- if (dry) {
458
- console.log(`${pc2.blue("readFile")} ${path6}`);
459
- return Promise.resolve("");
460
- }
461
- try {
462
- return await fs.readFile(path6, "utf-8");
463
- } catch (error) {
464
- if (allowFileNotFound && error.code === "ENOENT") {
465
- return "";
503
+ return fs.rm(dir, { recursive: true, force: true });
504
+ };
505
+ readFile = async (path6, allowFileNotFound) => {
506
+ if (this.dry) {
507
+ console.log(`${pc2.blue("readFile")} ${path6}`);
466
508
  }
467
- throw error;
468
- }
509
+ try {
510
+ return await fs.readFile(path6, "utf-8");
511
+ } catch (error) {
512
+ if (allowFileNotFound && error.code === "ENOENT") {
513
+ return "";
514
+ }
515
+ throw error;
516
+ }
517
+ };
518
+ readdir = async (path6) => {
519
+ if (this.dry) {
520
+ console.log(`${pc2.blue("readdir")} ${path6}`);
521
+ }
522
+ try {
523
+ return await fs.readdir(path6);
524
+ } catch (error) {
525
+ if (error.code === "ENOENT") {
526
+ return [];
527
+ }
528
+ throw error;
529
+ }
530
+ };
531
+ writeFiles = async (files, outDir, log) => {
532
+ for (const { destination: filename, output } of files) {
533
+ if (filename) {
534
+ const filePath = path.join(outDir, filename);
535
+ const fileDir = path.dirname(filePath);
536
+ if (log) {
537
+ console.log(filename);
538
+ }
539
+ await this.mkdir(fileDir);
540
+ await this.writeFile(filePath, output);
541
+ }
542
+ }
543
+ };
469
544
  };
545
+ var dsfs = new FileSystem();
470
546
 
471
547
  // src/migrations/codemods/css/run.ts
472
548
  var runCssCodemod = async ({ plugins = [], globPattern = "./**/*.css" }) => {
@@ -482,9 +558,9 @@ var runCssCodemod = async ({ plugins = [], globPattern = "./**/*.css" }) => {
482
558
  if (file.includes("node_modules") || file.includes("dist")) {
483
559
  return;
484
560
  }
485
- const contents = readFile(file).toString();
486
- const result = await processor.process(contents, { from: file });
487
- fs2.writeFileSync(file, result.css);
561
+ const contents = await dsfs.readFile(file);
562
+ const result = await processor.process(contents.toString(), { from: file });
563
+ await dsfs.writeFile(file, result.css);
488
564
  });
489
565
  await Promise.all(filePromises);
490
566
  };
@@ -874,7 +950,6 @@ var migrations_default = {
874
950
  };
875
951
 
876
952
  // src/tokens/build.ts
877
- import path from "path";
878
953
  import pc7 from "picocolors";
879
954
  import * as R22 from "ramda";
880
955
 
@@ -884,11 +959,11 @@ import pc5 from "picocolors";
884
959
  // package.json
885
960
  var package_default = {
886
961
  name: "@digdir/designsystemet",
887
- version: "1.12.1",
962
+ version: "1.13.0",
888
963
  description: "CLI for Designsystemet",
889
964
  author: "Designsystemet team",
890
965
  engines: {
891
- node: ">=20 <25"
966
+ node: ">=20.20.1"
892
967
  },
893
968
  repository: {
894
969
  type: "git",
@@ -952,16 +1027,16 @@ var package_default = {
952
1027
  hsluv: "^1.0.1",
953
1028
  "object-hash": "^3.0.0",
954
1029
  picocolors: "^1.1.1",
955
- postcss: "^8.5.6",
1030
+ postcss: "^8.5.8",
956
1031
  ramda: "^0.32.0",
957
- "style-dictionary": "^5.3.0",
1032
+ "style-dictionary": "^5.3.3",
958
1033
  zod: "^4.3.6",
959
1034
  "zod-validation-error": "^5.0.0"
960
1035
  },
961
1036
  devDependencies: {
962
1037
  "@tokens-studio/types": "0.5.2",
963
1038
  "@types/chroma-js": "^3.1.2",
964
- "@types/node": "^24.10.13",
1039
+ "@types/node": "^24.12.0",
965
1040
  "@types/object-hash": "^3.0.6",
966
1041
  "@types/ramda": "^0.31.1",
967
1042
  tsup: "^8.5.1",
@@ -2530,25 +2605,13 @@ ${fileHeader}
2530
2605
  };
2531
2606
 
2532
2607
  // src/tokens/build.ts
2533
- async function write(files, outDir, dry) {
2534
- for (const { destination, output } of files) {
2535
- if (destination) {
2536
- const filePath = path.join(outDir, destination);
2537
- const fileDir = path.dirname(filePath);
2538
- console.log(destination);
2539
- await mkdir(fileDir, dry);
2540
- await writeFile(filePath, output, dry);
2541
- }
2542
- }
2543
- }
2544
2608
  var buildTokens = async (options) => {
2545
- const outDir = path.resolve(options.outDir);
2546
- const tokensDir = path.resolve(options.tokensDir);
2547
- const $themes = JSON.parse(await readFile(`${tokensDir}/$themes.json`));
2609
+ const tokensDir = options.tokensDir;
2610
+ const $themes = JSON.parse(await dsfs.readFile(`${tokensDir}/$themes.json`));
2548
2611
  const processed$themes = $themes.map(processThemeObject);
2549
2612
  let $designsystemet;
2550
2613
  try {
2551
- const $designsystemetContent = await readFile(`${tokensDir}/$designsystemet.jsonc`);
2614
+ const $designsystemetContent = await dsfs.readFile(`${tokensDir}/$designsystemet.jsonc`);
2552
2615
  $designsystemet = JSON.parse($designsystemetContent);
2553
2616
  } catch (_error) {
2554
2617
  }
@@ -2556,7 +2619,6 @@ var buildTokens = async (options) => {
2556
2619
  \u{1F3D7}\uFE0F Start building tokens in ${pc7.green(tokensDir)}`);
2557
2620
  const processedBuilds = await processPlatform({
2558
2621
  ...options,
2559
- outDir,
2560
2622
  tokensDir,
2561
2623
  type: "build",
2562
2624
  processed$themes,
@@ -2575,15 +2637,10 @@ design-tokens: v${$designsystemet.version}` : ""
2575
2637
  const tailwindFiles = createTailwindCSSFiles(cssFiles);
2576
2638
  files = files.concat(tailwindFiles.filter(Boolean));
2577
2639
  }
2578
- console.log(`
2579
- \u{1F4BE} Writing build to ${pc7.green(outDir)}`);
2580
- await write(files, outDir, options.dry);
2581
- console.log(`
2582
- \u2705 Finished building tokens!`);
2583
- return processedBuilds;
2640
+ return files;
2584
2641
  };
2585
2642
 
2586
- // src/tokens/create/write.ts
2643
+ // src/tokens/create/files.ts
2587
2644
  import path2 from "path";
2588
2645
  import pc8 from "picocolors";
2589
2646
  import * as R23 from "ramda";
@@ -2783,24 +2840,22 @@ function generateTypographyGroup(themes) {
2783
2840
  ];
2784
2841
  }
2785
2842
 
2786
- // src/tokens/create/write.ts
2843
+ // src/tokens/create/files.ts
2787
2844
  var stringify = (data) => JSON.stringify(data, null, 2);
2788
- var writeTokens = async (options) => {
2845
+ var createTokenFiles = async (options) => {
2789
2846
  const {
2790
2847
  outDir,
2791
2848
  tokenSets,
2792
- theme: { name: themeName, colors: colors2 },
2793
- dry
2849
+ theme: { name: themeName, colors: colors2 }
2794
2850
  } = options;
2795
- const targetDir = path2.resolve(process.cwd(), String(outDir));
2796
- const $themesPath = path2.join(targetDir, "$themes.json");
2797
- const $metadataPath = path2.join(targetDir, "$metadata.json");
2798
- const $designsystemetPath = path2.join(targetDir, "$designsystemet.jsonc");
2851
+ const $themesPath = "$themes.json";
2852
+ const $metadataPath = "$metadata.json";
2853
+ const $designsystemetPath = "$designsystemet.jsonc";
2799
2854
  let themeObjects = [];
2800
2855
  const sizeModes2 = ["small", "medium", "large"];
2801
- await mkdir(targetDir, dry);
2856
+ await dsfs.mkdir(outDir);
2802
2857
  try {
2803
- const $themes2 = await readFile($themesPath);
2858
+ const $themes2 = await dsfs.readFile(path2.join(outDir, $themesPath));
2804
2859
  if ($themes2) {
2805
2860
  themeObjects = JSON.parse($themes2);
2806
2861
  }
@@ -2819,16 +2874,15 @@ Themes: ${pc8.blue(themes.join(", "))}`);
2819
2874
  const $themes = await generate$Themes(["dark", "light"], themes, colors2, sizeModes2);
2820
2875
  const $metadata = generate$Metadata(["dark", "light"], themes, colors2, sizeModes2);
2821
2876
  const $designsystemet = generate$Designsystemet();
2822
- await writeFile($themesPath, stringify($themes), dry);
2823
- await writeFile($metadataPath, stringify($metadata), dry);
2824
- await writeFile($designsystemetPath, stringify($designsystemet), dry);
2877
+ const files = [];
2878
+ files.push({ destination: $themesPath, output: stringify($themes) });
2879
+ files.push({ destination: $metadataPath, output: stringify($metadata) });
2880
+ files.push({ destination: $designsystemetPath, output: stringify($designsystemet) });
2825
2881
  for (const [set4, tokens] of tokenSets) {
2826
- const fileDir = path2.join(targetDir, path2.dirname(set4));
2827
- await mkdir(fileDir, dry);
2828
- const filePath = path2.join(targetDir, `${set4}.json`);
2829
- await writeFile(filePath, stringify(tokens), dry);
2882
+ const filePath = `${set4}.json`;
2883
+ files.push({ destination: filePath, output: stringify(tokens) });
2830
2884
  }
2831
- console.log(`Finished creating Designsystem design tokens in ${pc8.green(outDir)} for theme ${pc8.blue(themeName)}`);
2885
+ return files;
2832
2886
  };
2833
2887
 
2834
2888
  // src/tokens/create/generators/primitives/color-scheme.ts
@@ -4062,12 +4116,11 @@ var createTokens = async (theme) => {
4062
4116
  };
4063
4117
 
4064
4118
  // src/tokens/generate-config.ts
4065
- import fs3 from "fs/promises";
4066
4119
  import path3 from "path";
4067
4120
  import pc9 from "picocolors";
4068
4121
  async function readJsonFile(filePath) {
4069
4122
  try {
4070
- const content = await fs3.readFile(filePath, "utf-8");
4123
+ const content = await dsfs.readFile(filePath);
4071
4124
  return JSON.parse(content);
4072
4125
  } catch (err) {
4073
4126
  throw new Error(`Failed to read token file at ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
@@ -4085,7 +4138,7 @@ function extractBaseColor(colorScale) {
4085
4138
  async function discoverThemes(tokensDir) {
4086
4139
  const lightModePath = path3.join(tokensDir, "themes");
4087
4140
  try {
4088
- const files = await fs3.readdir(lightModePath);
4141
+ const files = await dsfs.readdir(lightModePath);
4089
4142
  const themes = files.filter((file) => file.endsWith(".json")).map((file) => file.replace(".json", ""));
4090
4143
  return themes;
4091
4144
  } catch {
@@ -4191,7 +4244,7 @@ function categorizeColors(themeTokens, themeName) {
4191
4244
  return { main, support, neutral };
4192
4245
  }
4193
4246
  async function generateConfigFromTokens(options) {
4194
- const { tokensDir, dry = false } = options;
4247
+ const { tokensDir } = options;
4195
4248
  console.log(`
4196
4249
  Reading tokens from ${pc9.blue(tokensDir)}`);
4197
4250
  const themes = await discoverThemes(tokensDir);
@@ -4251,18 +4304,10 @@ Warning: No neutral color found for theme ${themeName}`));
4251
4304
  \u2705 Font family: ${pc9.cyan(fontFamily)}`);
4252
4305
  }
4253
4306
  }
4254
- if (!dry && options.outFile) {
4255
- const configJson = JSON.stringify(config, null, 2);
4256
- await fs3.writeFile(options.outFile, configJson, "utf-8");
4257
- console.log();
4258
- console.log(`
4259
- \u2705 Config file written to ${pc9.blue(options.outFile)}`);
4260
- }
4261
4307
  return config;
4262
4308
  }
4263
4309
 
4264
4310
  // bin/config.ts
4265
- import path5 from "path";
4266
4311
  import pc11 from "picocolors";
4267
4312
  import * as R28 from "ramda";
4268
4313
 
@@ -4296,24 +4341,24 @@ function makeFriendlyError(err) {
4296
4341
  console.error(err instanceof Error ? err.stack : "No stack trace available");
4297
4342
  }
4298
4343
  }
4299
- function validateConfig(schema, unvalidatedConfig, configPath) {
4344
+ function validateConfig(schema, unvalidatedConfig, configFilePath) {
4300
4345
  try {
4301
4346
  return schema.parse(unvalidatedConfig);
4302
4347
  } catch (err) {
4303
- console.error(pc10.redBright(`Invalid config file at ${pc10.red(configPath)}`));
4348
+ console.error(pc10.redBright(`Invalid config file at ${pc10.red(configFilePath)}`));
4304
4349
  const validationError = makeFriendlyError(err);
4305
4350
  console.error(validationError?.toString());
4306
4351
  process.exit(1);
4307
4352
  }
4308
4353
  }
4309
- function parseConfig(configFile, configPath) {
4354
+ function parseConfig(configFile, configFilePath) {
4310
4355
  if (!configFile) {
4311
4356
  return {};
4312
4357
  }
4313
4358
  try {
4314
4359
  return JSON.parse(configFile);
4315
4360
  } catch (err) {
4316
- console.error(pc10.redBright(`Failed parsing config file at ${pc10.red(configPath)}`));
4361
+ console.error(pc10.redBright(`Failed parsing config file at ${pc10.red(configFilePath)}`));
4317
4362
  const validationError = makeFriendlyError(err);
4318
4363
  console.error(validationError?.toString());
4319
4364
  process.exit(1);
@@ -4404,26 +4449,25 @@ var getDefaultCliOption = getOptionIfMatchingSource("default");
4404
4449
  var getCliOption = getOptionIfMatchingSource("cli", "default");
4405
4450
 
4406
4451
  // bin/config.ts
4407
- async function readConfigFile(configPath, allowFileNotFound = true) {
4408
- const resolvedPath = path5.resolve(process.cwd(), configPath);
4452
+ async function readConfigFile(configFilePath, allowFileNotFound = true) {
4409
4453
  let configFile;
4410
4454
  try {
4411
- configFile = await readFile(resolvedPath, false, allowFileNotFound);
4455
+ configFile = await dsfs.readFile(configFilePath, allowFileNotFound);
4412
4456
  } catch (err) {
4413
4457
  if (allowFileNotFound) {
4414
4458
  return "";
4415
4459
  }
4416
- console.error(pc11.redBright(`Could not read config file at ${pc11.blue(resolvedPath)}`));
4460
+ console.error(pc11.redBright(`Could not read config file at ${pc11.blue(configFilePath)}`));
4417
4461
  throw err;
4418
4462
  }
4419
4463
  if (configFile) {
4420
- console.log(`Found config file: ${pc11.green(resolvedPath)}`);
4464
+ console.log(`Found config file: ${pc11.green(configFilePath)}`);
4421
4465
  }
4422
4466
  return configFile;
4423
4467
  }
4424
4468
  async function parseCreateConfig(configFile, options) {
4425
- const { cmd, theme = "theme", configPath } = options;
4426
- const configParsed = parseConfig(configFile, configPath);
4469
+ const { cmd, theme = "theme", configFilePath } = options;
4470
+ const configParsed = parseConfig(configFile, configFilePath);
4427
4471
  const themeColors = Object.values(configParsed?.themes ?? {}).map(
4428
4472
  (x) => /* @__PURE__ */ new Set([...R28.keys(x.colors.main), ...R28.keys(x.colors.support)])
4429
4473
  );
@@ -4466,11 +4510,7 @@ async function parseCreateConfig(configFile, options) {
4466
4510
  }
4467
4511
  )
4468
4512
  });
4469
- return validateConfig(configFileCreateSchema, unvalidatedConfig, configPath);
4470
- }
4471
- async function parseBuildConfig(configFile, { configPath }) {
4472
- const configParsed = parseConfig(configFile, configPath);
4473
- return validateConfig(commonConfig, configParsed, configPath);
4513
+ return validateConfig(configFileCreateSchema, unvalidatedConfig, configFilePath);
4474
4514
  }
4475
4515
 
4476
4516
  // bin/designsystemet.ts
@@ -4489,27 +4529,31 @@ var DEFAULT_TOKENS_CREATE_DIR = "./design-tokens";
4489
4529
  var DEFAULT_TOKENS_BUILD_DIR = "./design-tokens-build";
4490
4530
  var DEFAULT_FONT = "Inter";
4491
4531
  var DEFAULT_THEME_NAME = "theme";
4492
- var DEFAULT_CONFIG_FILE = "designsystemet.config.json";
4532
+ var DEFAULT_CONFIG_FILEPATH = "designsystemet.config.json";
4493
4533
  function makeTokenCommands() {
4494
4534
  const tokenCmd = createCommand("tokens");
4495
4535
  tokenCmd.command("build").description("Build Designsystemet tokens").option("-t, --tokens <string>", `Path to ${pc12.blue("design-tokens")}`, DEFAULT_TOKENS_CREATE_DIR).option(
4496
4536
  "-o, --out-dir <string>",
4497
4537
  `Output directory for built ${pc12.blue("design-tokens")}`,
4498
4538
  DEFAULT_TOKENS_BUILD_DIR
4499
- ).option(`--${cliOptions.clean} [boolean]`, "Clean output directory before building tokens", parseBoolean, false).option("--dry [boolean]", `Dry run for built ${pc12.blue("design-tokens")}`, parseBoolean, false).option("--verbose", "Enable verbose output", false).option("--config <string>", `Path to config file (default: "${DEFAULT_CONFIG_FILE}")`).option("--experimental-tailwind", "Generate Tailwind CSS classes for tokens", false).action(async (opts) => {
4539
+ ).option(`--${cliOptions.clean} [boolean]`, "Clean output directory before building tokens", parseBoolean, false).option("--dry [boolean]", `Dry run for built ${pc12.blue("design-tokens")}`, parseBoolean, false).option("--verbose", "Enable verbose output", false).option("--config <string>", `Path to config file (default: "${DEFAULT_CONFIG_FILEPATH}")`).option("--experimental-tailwind", "Generate Tailwind CSS classes for tokens", false).action(async (opts) => {
4500
4540
  console.log(figletAscii);
4501
- const { verbose, clean, dry, experimentalTailwind } = opts;
4502
- const tokensDir = typeof opts.tokens === "string" ? opts.tokens : DEFAULT_TOKENS_CREATE_DIR;
4503
- const outDir = typeof opts.outDir === "string" ? opts.outDir : "./dist/tokens";
4504
- const { configFile, configPath } = await getConfigFile(opts.config);
4505
- const config = await parseBuildConfig(configFile, { configPath });
4506
- if (dry) {
4507
- console.log(`Performing dry run, no files will be written`);
4508
- }
4541
+ const { verbose, clean, dry, experimentalTailwind, tokens } = opts;
4542
+ dsfs.init({ dry, outdir: opts.outDir, verbose });
4543
+ const outDir = dsfs.outDir;
4509
4544
  if (clean) {
4510
- await cleanDir(outDir, dry);
4545
+ await dsfs.cleanDir(outDir);
4511
4546
  }
4512
- await buildTokens({ tokensDir, outDir, verbose, dry, tailwind: experimentalTailwind, ...config });
4547
+ const files = await buildTokens({
4548
+ tokensDir: tokens,
4549
+ verbose,
4550
+ tailwind: experimentalTailwind
4551
+ });
4552
+ console.log(`
4553
+ \u{1F4BE} Writing build to ${pc12.green(outDir)}`);
4554
+ await dsfs.writeFiles(files, outDir, true);
4555
+ console.log(`
4556
+ \u2705 Finished building tokens in ${pc12.green(outDir)}`);
4513
4557
  return Promise.resolve();
4514
4558
  });
4515
4559
  tokenCmd.command("create").description("Create Designsystemet tokens").option(`-m, --${cliOptions.theme.colors.main} <name:hex...>`, `Main colors`, parseColorValues).option(`-s, --${cliOptions.theme.colors.support} <name:hex...>`, `Support colors`, parseColorValues).option(`-n, --${cliOptions.theme.colors.neutral} <hex>`, `Neutral hex color`, convertToHex).option(
@@ -4521,47 +4565,62 @@ function makeTokenCommands() {
4521
4565
  `Unitless base border-radius in px`,
4522
4566
  (radiusAsString) => Number(radiusAsString),
4523
4567
  4
4524
- ).option("--theme <string>", "Theme name (ignored when using JSON config file)", DEFAULT_THEME_NAME).option("--config <string>", `Path to config file (default: "${DEFAULT_CONFIG_FILE}")`).action(async (opts, cmd) => {
4568
+ ).option("--theme <string>", "Theme name (ignored when using JSON config file)", DEFAULT_THEME_NAME).option("--config <string>", `Path to config file (default: "${DEFAULT_CONFIG_FILEPATH}")`).action(async (opts, cmd) => {
4525
4569
  console.log(figletAscii);
4526
4570
  if (opts.dry) {
4527
4571
  console.log(`Performing dry run, no files will be written`);
4528
4572
  }
4529
- const { configFile, configPath } = await getConfigFile(opts.config);
4573
+ const themeName = opts.theme;
4574
+ const { configFile, configFilePath } = await getConfigFile(opts.config);
4530
4575
  const config = await parseCreateConfig(configFile, {
4531
- theme: opts.theme,
4576
+ theme: themeName,
4532
4577
  cmd,
4533
- configPath
4578
+ configFilePath
4534
4579
  });
4580
+ dsfs.init({ dry: opts.dry, outdir: config.outDir });
4581
+ const outDir = dsfs.outDir;
4535
4582
  if (config.clean) {
4536
- await cleanDir(config.outDir, opts.dry);
4583
+ await dsfs.cleanDir(outDir);
4537
4584
  }
4585
+ let files = [];
4538
4586
  if (config.themes) {
4539
4587
  for (const [name, themeWithoutName] of Object.entries(config.themes)) {
4540
4588
  const theme = { name, ...themeWithoutName };
4541
4589
  const { tokenSets } = await createTokens(theme);
4542
- await writeTokens({ outDir: config.outDir, theme, dry: opts.dry, tokenSets });
4590
+ files = files.concat(await createTokenFiles({ outDir, theme, tokenSets }));
4543
4591
  }
4544
4592
  }
4593
+ await dsfs.writeFiles(files, outDir);
4594
+ console.log(`
4595
+ \u2705 Finished creating tokens in ${pc12.green(outDir)} for theme: ${pc12.blue(themeName)}`);
4596
+ return Promise.resolve();
4545
4597
  });
4546
4598
  return tokenCmd;
4547
4599
  }
4548
4600
  program.addCommand(makeTokenCommands());
4549
- program.command("generate-config-from-tokens").description("Generate a config file from existing design tokens. Will not include overrides.").option("-d, --dir <string>", "Path to design tokens directory", DEFAULT_TOKENS_CREATE_DIR).option("-o, --out <string>", "Output path for config file", DEFAULT_CONFIG_FILE).option("--dry [boolean]", "Dry run - show config without writing file", parseBoolean, false).action(async (opts) => {
4601
+ program.command("generate-config-from-tokens").description("Generate a config file from existing design tokens. Will not include overrides.").option("-d, --dir <string>", "Path to design tokens directory", DEFAULT_TOKENS_CREATE_DIR).option("-o, --out <string>", "Output path for config file", DEFAULT_CONFIG_FILEPATH).option("--dry [boolean]", "Dry run - show config without writing file", parseBoolean, false).action(async (opts) => {
4550
4602
  console.log(figletAscii);
4551
4603
  const { dry } = opts;
4552
- const tokensDir = typeof opts.dir === "string" ? opts.dir : DEFAULT_TOKENS_CREATE_DIR;
4553
- const outFile = typeof opts.out === "string" ? opts.out : DEFAULT_CONFIG_FILE;
4604
+ const tokensDir = path5.resolve(opts.dir);
4605
+ const configFilePath = path5.resolve(opts.out);
4606
+ dsfs.init({ dry, outdir: path5.dirname(configFilePath) });
4554
4607
  try {
4555
4608
  const config = await generateConfigFromTokens({
4556
4609
  tokensDir,
4557
- outFile: dry ? void 0 : outFile,
4558
- dry
4610
+ outFile: configFilePath
4559
4611
  });
4560
4612
  if (dry) {
4561
4613
  console.log();
4562
4614
  console.log("Generated config (dry run):");
4563
4615
  console.log(JSON.stringify(config, null, 2));
4564
4616
  }
4617
+ if (configFilePath) {
4618
+ const configJson = JSON.stringify(config, null, 2);
4619
+ await dsfs.writeFile(configFilePath, configJson);
4620
+ console.log();
4621
+ console.log(`
4622
+ \u2705 Config file written to ${pc12.blue(configFilePath)}`);
4623
+ }
4565
4624
  } catch (error) {
4566
4625
  console.error(pc12.redBright("Error generating config:"));
4567
4626
  console.error(error instanceof Error ? error.message : String(error));
@@ -4596,11 +4655,11 @@ function parseColorValues(value, previous = {}) {
4596
4655
  function parseBoolean(value) {
4597
4656
  return value === "true" || value === true;
4598
4657
  }
4599
- async function getConfigFile(config) {
4600
- const allowFileNotFound = R29.isNil(config) || config === DEFAULT_CONFIG_FILE;
4601
- const configPath = config ?? DEFAULT_CONFIG_FILE;
4602
- const configFile = await readConfigFile(configPath, allowFileNotFound);
4603
- return { configFile, configPath };
4658
+ async function getConfigFile(userConfigFilePath) {
4659
+ const allowFileNotFound = R29.isNil(userConfigFilePath) || userConfigFilePath === DEFAULT_CONFIG_FILEPATH;
4660
+ const configFilePath = userConfigFilePath ?? DEFAULT_CONFIG_FILEPATH;
4661
+ const configFile = await readConfigFile(configFilePath, allowFileNotFound);
4662
+ return { configFile, configFilePath };
4604
4663
  }
4605
4664
  export {
4606
4665
  figletAscii
@@ -8,8 +8,8 @@ import { z } from 'zod';
8
8
  * @returns The validated configuration object, typed as T.
9
9
  * @throws Exits the process with code 1 if validation fails, after logging a friendly error message.
10
10
  */
11
- export declare function validateConfig<T>(schema: z.ZodType<T>, unvalidatedConfig: Record<string, unknown>, configPath: string): T;
12
- export declare function parseConfig<T>(configFile: string, configPath: string): T;
11
+ export declare function validateConfig<T>(schema: z.ZodType<T>, unvalidatedConfig: Record<string, unknown>, configFilePath: string): T;
12
+ export declare function parseConfig<T>(configFile: string, configFilePath: string): T;
13
13
  export declare const colorRegex: RegExp;
14
14
  declare const overridesSchema: z.ZodOptional<z.ZodObject<{
15
15
  colors: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodEnum<{
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAuCxB;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EACpB,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1C,UAAU,EAAE,MAAM,GACjB,CAAC,CAUH;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,CAAC,CAexE;AAWD,eAAO,MAAM,UAAU,QAA2C,CAAC;AAuEnE,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAQR,CAAC;AAEd,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkBmG,CAAC;AAErH,eAAO,MAAM,YAAY;;iBAEvB,CAAC;AAYH;;GAEG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAqD,CAAC;AACzF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAC9D,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAC7D,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACxE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAC5D,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAuCxB;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EACpB,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1C,cAAc,EAAE,MAAM,GACrB,CAAC,CAUH;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,CAAC,CAe5E;AAWD,eAAO,MAAM,UAAU,QAA2C,CAAC;AAuEnE,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAQR,CAAC;AAEd,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkBmG,CAAC;AAErH,eAAO,MAAM,YAAY;;iBAEvB,CAAC;AAYH;;GAEG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAqD,CAAC;AACzF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAC9D,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAC7D,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACxE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAC5D,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC"}
@@ -355,24 +355,24 @@ function makeFriendlyError(err) {
355
355
  console.error(err instanceof Error ? err.stack : "No stack trace available");
356
356
  }
357
357
  }
358
- function validateConfig(schema, unvalidatedConfig, configPath) {
358
+ function validateConfig(schema, unvalidatedConfig, configFilePath) {
359
359
  try {
360
360
  return schema.parse(unvalidatedConfig);
361
361
  } catch (err) {
362
- console.error(pc.redBright(`Invalid config file at ${pc.red(configPath)}`));
362
+ console.error(pc.redBright(`Invalid config file at ${pc.red(configFilePath)}`));
363
363
  const validationError = makeFriendlyError(err);
364
364
  console.error(validationError?.toString());
365
365
  process.exit(1);
366
366
  }
367
367
  }
368
- function parseConfig(configFile, configPath) {
368
+ function parseConfig(configFile, configFilePath) {
369
369
  if (!configFile) {
370
370
  return {};
371
371
  }
372
372
  try {
373
373
  return JSON.parse(configFile);
374
374
  } catch (err) {
375
- console.error(pc.redBright(`Failed parsing config file at ${pc.red(configPath)}`));
375
+ console.error(pc.redBright(`Failed parsing config file at ${pc.red(configFilePath)}`));
376
376
  const validationError = makeFriendlyError(err);
377
377
  console.error(validationError?.toString());
378
378
  process.exit(1);