@digdir/designsystemet 1.16.0 → 1.17.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 (96) hide show
  1. package/README.md +2 -15
  2. package/dist/bin/config.d.ts +2 -4
  3. package/dist/bin/config.d.ts.map +1 -1
  4. package/dist/bin/config.js +6 -8
  5. package/dist/bin/designsystemet.d.ts +1 -1
  6. package/dist/bin/designsystemet.d.ts.map +1 -1
  7. package/dist/bin/designsystemet.js +12 -13
  8. package/dist/config.schema.json +5 -33
  9. package/dist/package.js +1 -1
  10. package/dist/src/automigrate.d.ts +2 -0
  11. package/dist/src/automigrate.d.ts.map +1 -0
  12. package/dist/src/automigrate.js +33 -0
  13. package/dist/src/colors/types.d.ts +1 -1
  14. package/dist/src/index.d.ts +1 -1
  15. package/dist/src/index.d.ts.map +1 -1
  16. package/dist/src/index.js +4 -3
  17. package/dist/src/migrations/flatten-color-categories.d.ts +10 -0
  18. package/dist/src/migrations/flatten-color-categories.d.ts.map +1 -0
  19. package/dist/src/migrations/flatten-color-categories.js +51 -0
  20. package/dist/src/migrations/index.d.ts +9 -0
  21. package/dist/src/migrations/index.d.ts.map +1 -1
  22. package/dist/src/migrations/index.js +3 -1
  23. package/dist/src/schemas/helpers.d.ts +29 -0
  24. package/dist/src/schemas/helpers.d.ts.map +1 -0
  25. package/dist/src/schemas/helpers.js +71 -0
  26. package/dist/src/{config.d.ts → schemas/v1/schema.d.ts} +1 -12
  27. package/dist/src/schemas/v1/schema.d.ts.map +1 -0
  28. package/dist/src/{config.js → schemas/v1/schema.js} +6 -61
  29. package/dist/src/schemas/v1.1/schema.d.ts +162 -0
  30. package/dist/src/schemas/v1.1/schema.d.ts.map +1 -0
  31. package/dist/src/schemas/v1.1/schema.js +54 -0
  32. package/dist/src/tokens/build.js +1 -1
  33. package/dist/src/tokens/create/files.d.ts +2 -2
  34. package/dist/src/tokens/create/files.d.ts.map +1 -1
  35. package/dist/src/tokens/create/files.js +3 -3
  36. package/dist/src/tokens/create/generators/$metadata.d.ts +2 -2
  37. package/dist/src/tokens/create/generators/$metadata.d.ts.map +1 -1
  38. package/dist/src/tokens/create/generators/$metadata.js +2 -4
  39. package/dist/src/tokens/create/generators/$themes.d.ts +2 -2
  40. package/dist/src/tokens/create/generators/$themes.d.ts.map +1 -1
  41. package/dist/src/tokens/create/generators/$themes.js +7 -10
  42. package/dist/src/tokens/create/generators/primitives/color-scheme.d.ts +4 -4
  43. package/dist/src/tokens/create/generators/primitives/color-scheme.d.ts.map +1 -1
  44. package/dist/src/tokens/create/generators/primitives/color-scheme.js +9 -11
  45. package/dist/src/tokens/create/generators/semantic/color.d.ts +2 -4
  46. package/dist/src/tokens/create/generators/semantic/color.d.ts.map +1 -1
  47. package/dist/src/tokens/create/generators/semantic/color.js +9 -38
  48. package/dist/src/tokens/create/generators/semantic/style.d.ts +1 -1
  49. package/dist/src/tokens/create/generators/semantic/style.d.ts.map +1 -1
  50. package/dist/src/tokens/create/generators/semantic/style.js +78 -41
  51. package/dist/src/tokens/create/generators/themes/theme.d.ts +2 -2
  52. package/dist/src/tokens/create/generators/themes/theme.d.ts.map +1 -1
  53. package/dist/src/tokens/create/generators/themes/theme.js +2 -11
  54. package/dist/src/tokens/create.d.ts +0 -16
  55. package/dist/src/tokens/create.d.ts.map +1 -1
  56. package/dist/src/tokens/create.js +10 -22
  57. package/dist/src/tokens/format.js +6 -4
  58. package/dist/src/tokens/generate-config.d.ts +3 -2
  59. package/dist/src/tokens/generate-config.d.ts.map +1 -1
  60. package/dist/src/tokens/generate-config.js +10 -36
  61. package/dist/src/tokens/index.d.ts +2 -1
  62. package/dist/src/tokens/index.d.ts.map +1 -1
  63. package/dist/src/tokens/index.js +3 -2
  64. package/dist/src/tokens/process/configs/color.d.ts +1 -9
  65. package/dist/src/tokens/process/configs/color.d.ts.map +1 -1
  66. package/dist/src/tokens/process/configs/color.js +6 -9
  67. package/dist/src/tokens/process/configs.d.ts +1 -7
  68. package/dist/src/tokens/process/configs.d.ts.map +1 -1
  69. package/dist/src/tokens/process/configs.js +3 -24
  70. package/dist/src/tokens/process/formats/css/color.d.ts.map +1 -1
  71. package/dist/src/tokens/process/formats/css/color.js +3 -4
  72. package/dist/src/tokens/process/formats/css/size-mode.d.ts +0 -1
  73. package/dist/src/tokens/process/formats/css/size-mode.d.ts.map +1 -1
  74. package/dist/src/tokens/process/formats/css/size-mode.js +1 -1
  75. package/dist/src/tokens/process/formats/css/size.d.ts +0 -12
  76. package/dist/src/tokens/process/formats/css/size.d.ts.map +1 -1
  77. package/dist/src/tokens/process/formats/css/size.js +1 -1
  78. package/dist/src/tokens/process/output/declarations.d.ts.map +1 -1
  79. package/dist/src/tokens/process/output/declarations.js +6 -8
  80. package/dist/src/tokens/process/platform.d.ts +4 -36
  81. package/dist/src/tokens/process/platform.d.ts.map +1 -1
  82. package/dist/src/tokens/process/platform.js +8 -50
  83. package/dist/src/tokens/process/utils/getMultidimensionalThemes.d.ts +2 -3
  84. package/dist/src/tokens/process/utils/getMultidimensionalThemes.d.ts.map +1 -1
  85. package/dist/src/tokens/process/utils/getMultidimensionalThemes.js +4 -8
  86. package/dist/src/tokens/types.d.ts +3 -11
  87. package/dist/src/tokens/types.d.ts.map +1 -1
  88. package/dist/src/tokens/types.js +1 -7
  89. package/dist/src/tokens/utils.d.ts +5 -11
  90. package/dist/src/tokens/utils.d.ts.map +1 -1
  91. package/dist/src/tokens/utils.js +8 -13
  92. package/package.json +11 -7
  93. package/dist/src/config.d.ts.map +0 -1
  94. package/dist/src/tokens/create/generators/semantic/color-modes.d.ts +0 -8
  95. package/dist/src/tokens/create/generators/semantic/color-modes.d.ts.map +0 -1
  96. package/dist/src/tokens/create/generators/semantic/color-modes.js +0 -26
package/README.md CHANGED
@@ -14,7 +14,7 @@ Read the Designsystemet [README](https://github.com/digdir/designsystemet) to ge
14
14
  Use `npx @digdir/designsystemet tokens create <options>` to create design tokens for use with Designsystemet.
15
15
 
16
16
  This allows you to define themes including custom colors, font-family, and border-radius.
17
- We recommend using the [Designsystemet theme builder](https://theme.designsystemet.no/) for generating a valid command with correct options.
17
+ We recommend using the [Designsystemet theme builder](https://theme.designsystemet.no/) for generating a valid config.
18
18
 
19
19
  #### Update tokens
20
20
 
@@ -67,18 +67,6 @@ To get started, use this template for a `designsystemet.config.json` file:
67
67
  In editors which support JSON Schema, the `$schema` will then give you editor hints for the structure of the file.
68
68
 
69
69
  #### Minimal config example
70
- As a minimal example, the following CLI snippet from the theme builder
71
-
72
- ```
73
- npx @digdir/designsystemet tokens create \
74
- --main-colors "primary:#0062BA" "accent:#1E98F5" \
75
- --neutral-color "#1E2B3C" \
76
- --support-colors "extra1:#F45F63" "extra2:#E5AA20" \
77
- --border-radius 4 \
78
- --theme "theme"
79
- ```
80
-
81
- ...is equivalent to this `designsystemet.config.json` file
82
70
  ```jsonc
83
71
  {
84
72
  "$schema": "./node_modules/@digdir/designsystemet/dist/config.schema.json",
@@ -86,9 +74,8 @@ npx @digdir/designsystemet tokens create \
86
74
  "themes": {
87
75
  "theme": {
88
76
  "colors": {
89
- "main": { "primary": "#0062BA", "accent": "#1E98F5" },
77
+ "accent": "#1E98F5",
90
78
  "neutral": "#1E2B3C",
91
- "support": { "extra1": "#F45F63", "extra2": "#E5AA20" }
92
79
  },
93
80
  "borderRadius": 4
94
81
  }
@@ -1,12 +1,10 @@
1
1
  import type { Command, OptionValues } from '@commander-js/extra-typings';
2
- import { type BuildConfigSchema, type CreateConfigSchema } from '../src/config.js';
2
+ import { type CreateConfigSchema } from '../src/schemas/v1.1/schema.js';
3
+ export { deprecatedCLIOptions } from '../src/schemas/helpers.js';
3
4
  export declare function readConfigFile(configFilePath: string, allowFileNotFound?: boolean): Promise<string>;
4
5
  export declare function parseCreateConfig(configFile: string, options: {
5
6
  theme: string;
6
7
  cmd: Command<unknown[], OptionValues>;
7
8
  configFilePath: string;
8
9
  }): Promise<CreateConfigSchema>;
9
- export declare function parseBuildConfig(configFile: string, { configFilePath }: {
10
- configFilePath: string;
11
- }): Promise<BuildConfigSchema>;
12
10
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../bin/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAGzE,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EAKxB,MAAM,kBAAkB,CAAC;AAI1B,wBAAsB,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,iBAAiB,UAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBtG;AAED,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GACxF,OAAO,CAAC,kBAAkB,CAAC,CAiE7B;AAED,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,EAClB,EAAE,cAAc,EAAE,EAAE;IAAE,cAAc,EAAE,MAAM,CAAA;CAAE,GAC7C,OAAO,CAAC,iBAAiB,CAAC,CAI5B"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../bin/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAIzE,OAAO,EAAE,KAAK,kBAAkB,EAA0B,MAAM,+BAA+B,CAAC;AAIhG,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE,wBAAsB,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,iBAAiB,UAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBtG;AAED,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GACxF,OAAO,CAAC,kBAAkB,CAAC,CA+D7B"}
@@ -1,4 +1,5 @@
1
- import { commonConfig, configFileCreateSchema, parseConfig, validateConfig } from "../src/config.js";
1
+ import { deprecatedCLIOptions, parseConfig, validateConfig } from "../src/schemas/helpers.js";
2
+ import { configFileCreateSchema } from "../src/schemas/v1.1/schema.js";
2
3
  import { dsfs } from "../src/utils/filesystem.js";
3
4
  import { getCliOption, getDefaultCliOption, getSuppliedCliOption } from "./options.js";
4
5
  import pc from "picocolors";
@@ -19,7 +20,7 @@ async function readConfigFile(configFilePath, allowFileNotFound = true) {
19
20
  async function parseCreateConfig(configFile, options) {
20
21
  const { cmd, theme = "theme", configFilePath } = options;
21
22
  const configParsed = parseConfig(configFile, configFilePath);
22
- const themeColors = Object.values(configParsed?.themes ?? {}).map((x) => new Set([...R.keys(x.colors.main), ...R.keys(x.colors.support)]));
23
+ const themeColors = Object.values(configParsed?.themes ?? {}).map((x) => new Set(R.keys(x.colors)));
23
24
  if (!R.all(R.equals(R.__, themeColors[0]), themeColors)) {
24
25
  console.error(pc.redBright(`In config, all themes must have the same custom color names, but we found:`));
25
26
  const themeNames = R.keys(configParsed.themes ?? {});
@@ -33,8 +34,8 @@ async function parseCreateConfig(configFile, options) {
33
34
  const noUndefined = R.reject(R.isNil);
34
35
  const getThemeOptions = (optionGetter) => noUndefined({
35
36
  colors: noUndefined({
36
- main: optionGetter(cmd, "mainColors"),
37
- support: optionGetter(cmd, "supportColors"),
37
+ ...optionGetter(cmd, "mainColors"),
38
+ ...optionGetter(cmd, "supportColors"),
38
39
  neutral: optionGetter(cmd, "neutralColor")
39
40
  }),
40
41
  typography: noUndefined({ fontFamily: optionGetter(cmd, "fontFamily") }),
@@ -51,8 +52,5 @@ async function parseCreateConfig(configFile, options) {
51
52
  }, configParsed.themes) : { [theme]: getThemeOptions(getCliOption) }
52
53
  }), configFilePath);
53
54
  }
54
- async function parseBuildConfig(configFile, { configFilePath }) {
55
- return validateConfig(commonConfig, parseConfig(configFile, configFilePath), configFilePath);
56
- }
57
55
  //#endregion
58
- export { parseBuildConfig, parseCreateConfig, readConfigFile };
56
+ export { deprecatedCLIOptions, parseCreateConfig, readConfigFile };
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- export declare const figletAscii = "\n _____ _ _ _\n| __ \\ (_) | | | |\n| | | | ___ ___ _ __ _ _ __ ___ _ _ ___| |_ ___ _ __ ___ ___| |_\n| | | |/ _ \\/ __| |/ _` | '_ \\/ __| | | / __| __/ _ \\ '_ ` _ \\ / _ \\ __|\n| |__| | __/\\__ \\ | (_| | | | \\__ \\ |_| \\__ \\ || __/ | | | | | __/ |_\n|_____/ \\___||___/_|\\__, |_| |_|___/\\__, |___/\\__\\___|_| |_| |_|\\___|\\__|\n __/ | __/ |\n |___/ |___/\n";
2
+ export {};
3
3
  //# sourceMappingURL=designsystemet.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"designsystemet.d.ts","sourceRoot":"","sources":["../../bin/designsystemet.ts"],"names":[],"mappings":";AAiBA,eAAO,MAAM,WAAW,4iBASvB,CAAC"}
1
+ {"version":3,"file":"designsystemet.d.ts","sourceRoot":"","sources":["../../bin/designsystemet.ts"],"names":[],"mappings":""}
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env node
2
+ import { deprecatedCLIOptions } from "../src/schemas/helpers.js";
2
3
  import { convertToHex } from "../src/colors/utils.js";
3
4
  import "../src/colors/index.js";
4
- import { cliOptions, createTokens, tokenSetDimensions } from "../src/tokens/create.js";
5
5
  import { dsfs } from "../src/utils/filesystem.js";
6
6
  import { parseCreateConfig, readConfigFile } from "./config.js";
7
7
  import migrations_default from "../src/migrations/index.js";
8
- import { colorNamesByCategory } from "../src/tokens/utils.js";
8
+ import { checkAutomigrate } from "../src/automigrate.js";
9
+ import { toColorNames } from "../src/tokens/utils.js";
9
10
  import { buildTokens } from "../src/tokens/build.js";
10
11
  import { createSystemTokenFiles, tokenSetsToFiles } from "../src/tokens/create/files.js";
12
+ import { createTokens, tokenSetDimensions } from "../src/tokens/create.js";
11
13
  import { generateConfigFromTokens } from "../src/tokens/generate-config.js";
12
14
  import pc from "picocolors";
13
15
  import * as R from "ramda";
@@ -30,9 +32,9 @@ const DEFAULT_TOKENS_BUILD_DIR = "./design-tokens-build";
30
32
  const DEFAULT_FONT = "Inter";
31
33
  const DEFAULT_THEME_NAME = "theme";
32
34
  const DEFAULT_CONFIG_FILEPATH = "designsystemet.config.json";
33
- function makeTokenCommands() {
35
+ function _makeTokenCommands() {
34
36
  const tokenCmd = createCommand("tokens");
35
- tokenCmd.command("build").description("Build Designsystemet tokens").option("-t, --tokens <string>", `Path to ${pc.blue("design-tokens")}`, DEFAULT_TOKENS_CREATE_DIR).option("-o, --out-dir <string>", `Output directory for built ${pc.blue("design-tokens")}`, DEFAULT_TOKENS_BUILD_DIR).option(`--${cliOptions.clean} [boolean]`, "Clean output directory before building tokens", parseBoolean, false).option("--dry [boolean]", `Dry run for built ${pc.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) => {
37
+ tokenCmd.command("build").description("Build Designsystemet tokens").option("-t, --tokens <string>", `Path to ${pc.blue("design-tokens")}`, DEFAULT_TOKENS_CREATE_DIR).option("-o, --out-dir <string>", `Output directory for built ${pc.blue("design-tokens")}`, DEFAULT_TOKENS_BUILD_DIR).option(`--${deprecatedCLIOptions.clean} [boolean]`, "Clean output directory before building tokens", parseBoolean, false).option("--dry [boolean]", `Dry run for built ${pc.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) => {
36
38
  console.log(figletAscii);
37
39
  const { verbose, clean, dry, experimentalTailwind, tokens } = opts;
38
40
  dsfs.init({
@@ -52,14 +54,14 @@ function makeTokenCommands() {
52
54
  console.log(`\n✅ Finished building tokens in ${pc.green(outDir)}`);
53
55
  return Promise.resolve();
54
56
  });
55
- tokenCmd.command("create").description("Create Designsystemet tokens").option("--config <string>", `Path to config file (default: "${DEFAULT_CONFIG_FILEPATH}")`).option(`--${cliOptions.clean} [boolean]`, "Clean output directory before creating tokens", parseBoolean, false).option("--dry [boolean]", `Dry run for created ${pc.blue("design-tokens")}`, parseBoolean, false).option(`-m, --${cliOptions.theme.colors.main} <name:hex...>`, `Main colors (deprecated, use JSON config file instead)`, parseColorValues).option(`-s, --${cliOptions.theme.colors.support} <name:hex...>`, `Support colors (deprecated, use JSON config file instead)`, parseColorValues).option(`-n, --${cliOptions.theme.colors.neutral} <hex>`, `Neutral hex color (deprecated, use JSON config file instead)`, convertToHex).option(`-o, --${cliOptions.outDir} <string>`, `Output directory for created ${pc.blue("design-tokens")}`, DEFAULT_TOKENS_CREATE_DIR).option(`-f, --${cliOptions.theme.typography.fontFamily} <string>`, `Font family (experimental, deprecated, use JSON config file instead)`, DEFAULT_FONT).option(`-b, --${cliOptions.theme.borderRadius} <number>`, `Unitless base border-radius in px (deprecated, use JSON config file instead)`, (radiusAsString) => Number(radiusAsString), 4).option("--theme <string>", "Theme name (deprecated, use JSON config file instead)", DEFAULT_THEME_NAME).action(async (opts, cmd) => {
57
+ tokenCmd.command("create").description("Create Designsystemet tokens").option("--config <string>", `Path to config file (default: "${DEFAULT_CONFIG_FILEPATH}")`).option(`--${deprecatedCLIOptions.clean} [boolean]`, "Clean output directory before creating tokens", parseBoolean, false).option("--dry [boolean]", `Dry run for created ${pc.blue("design-tokens")}`, parseBoolean, false).option("--skip-check", "Skip migration check", false).option("-y, --yes", "Skip user prompts", false).option(`-m, --${deprecatedCLIOptions.theme.colors.main} <name:hex...>`, `Main colors (deprecated, use JSON config file instead)`, parseColorValues).option(`-s, --${deprecatedCLIOptions.theme.colors.support} <name:hex...>`, `Support colors (deprecated, use JSON config file instead)`, parseColorValues).option(`-n, --${deprecatedCLIOptions.theme.colors.neutral} <hex>`, `Neutral hex color (deprecated, use JSON config file instead)`, convertToHex).option(`-o, --${deprecatedCLIOptions.outDir} <string>`, `Output directory for created ${pc.blue("design-tokens")}`, DEFAULT_TOKENS_CREATE_DIR).option(`-f, --${deprecatedCLIOptions.theme.typography.fontFamily} <string>`, `Font family (experimental, deprecated, use JSON config file instead)`, DEFAULT_FONT).option(`-b, --${deprecatedCLIOptions.theme.borderRadius} <number>`, `Unitless base border-radius in px (deprecated, use JSON config file instead)`, (radiusAsString) => Number(radiusAsString), 4).option("--theme <string>", "Theme name (deprecated, use JSON config file instead)", DEFAULT_THEME_NAME).action(async (opts, cmd) => {
56
58
  if (opts.mainColors || opts.supportColors || opts.neutralColor || opts.borderRadius && opts.borderRadius !== 4 || opts.theme && opts.theme !== DEFAULT_THEME_NAME || opts.fontFamily && opts.fontFamily !== DEFAULT_FONT) console.warn(pc.yellow(`\n ⚠️ Using CLI options for ${pc.bold(`colors, border radius, theme, or font family is deprecated`)} and will be removed in a future release.
57
59
  \n ⚠️ Please use a JSON config file instead.`));
58
60
  console.log(figletAscii);
59
61
  if (opts.dry) console.log(`Performing dry run, no files will be written`);
60
62
  const themeName = opts.theme;
61
63
  const { configFile, configFilePath } = await getConfigFile(opts.config);
62
- const config = await parseCreateConfig(configFile, {
64
+ const config = await parseCreateConfig((opts.skipCheck ? configFile : await checkAutomigrate(configFile, configFilePath, opts.yes)) || configFile, {
63
65
  theme: themeName,
64
66
  cmd,
65
67
  configFilePath
@@ -72,6 +74,7 @@ function makeTokenCommands() {
72
74
  });
73
75
  const outDir = dsfs.outDir;
74
76
  const files = [];
77
+ const colorNames = toColorNames(config.themes?.[themeNames[0]]?.colors);
75
78
  for (const [name, themeConfig] of Object.entries(config.themes)) {
76
79
  const { tokenSets } = await createTokens({
77
80
  name,
@@ -79,14 +82,10 @@ function makeTokenCommands() {
79
82
  });
80
83
  files.push(...tokenSetsToFiles(tokenSets));
81
84
  }
82
- const colors = config.themes?.[themeNames[0]]?.colors ?? {
83
- main: {},
84
- support: {}
85
- };
86
85
  files.push(...await createSystemTokenFiles({
87
86
  tokenSetDimensions,
88
87
  themeNames,
89
- colors: colorNamesByCategory(colors)
88
+ colorNames
90
89
  }));
91
90
  if (config.clean) await dsfs.cleanDir(outDir);
92
91
  await dsfs.mkdir(outDir);
@@ -96,7 +95,7 @@ function makeTokenCommands() {
96
95
  });
97
96
  return tokenCmd;
98
97
  }
99
- program.addCommand(makeTokenCommands());
98
+ program.addCommand(_makeTokenCommands());
100
99
  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) => {
101
100
  console.log(figletAscii);
102
101
  const { dry } = opts;
@@ -160,4 +159,4 @@ async function getConfigFile(userConfigFilePath) {
160
159
  };
161
160
  }
162
161
  //#endregion
163
- export { figletAscii };
162
+ export {};
@@ -19,40 +19,12 @@
19
19
  "properties": {
20
20
  "colors": {
21
21
  "type": "object",
22
- "properties": {
23
- "main": {
24
- "type": "object",
25
- "propertyNames": {
26
- "type": "string",
27
- "pattern": "^(?!(?:neutral|success|warning|danger|info)$)"
28
- },
29
- "additionalProperties": {
30
- "description": "A hex color, which is used for creating a color scale. Invalid color names: neutral, success, warning, danger, info"
31
- },
32
- "description": "An object with one or more color definitions. The property name is used as the color name."
33
- },
34
- "support": {
35
- "default": {},
36
- "type": "object",
37
- "propertyNames": {
38
- "type": "string",
39
- "pattern": "^(?!(?:neutral|success|warning|danger|info)$)"
40
- },
41
- "additionalProperties": {
42
- "description": "A hex color, which is used for creating a color scale. Invalid color names: neutral, success, warning, danger, info"
43
- },
44
- "description": "An object with one or more color definitions. The property name is used as the color name."
45
- },
46
- "neutral": {
47
- "description": "A hex color, which is used for creating a color scale."
48
- }
22
+ "propertyNames": {
23
+ "type": "string"
24
+ },
25
+ "additionalProperties": {
26
+ "description": "A hex color, which is used for creating a color scale."
49
27
  },
50
- "required": [
51
- "main",
52
- "support",
53
- "neutral"
54
- ],
55
- "additionalProperties": false,
56
28
  "description": "Defines the colors for this theme"
57
29
  },
58
30
  "typography": {
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
2
  var name = "@digdir/designsystemet";
3
- var version = "1.16.0";
3
+ var version = "1.17.0";
4
4
  //#endregion
5
5
  export { name, version };
@@ -0,0 +1,2 @@
1
+ export declare const checkAutomigrate: (configFile: string, configFilePath: string, yes: boolean) => Promise<string | null>;
2
+ //# sourceMappingURL=automigrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automigrate.d.ts","sourceRoot":"","sources":["../../src/automigrate.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,gBAAgB,GAAU,YAAY,MAAM,EAAE,gBAAgB,MAAM,EAAE,KAAK,OAAO,2BAwC9F,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { dsfs } from "./utils/filesystem.js";
2
+ import { automigrations } from "./migrations/index.js";
3
+ import pc from "picocolors";
4
+ import confirm from "@inquirer/confirm";
5
+ //#region src/automigrate.ts
6
+ const checkAutomigrate = async (configFile, configFilePath, yes) => {
7
+ if (!configFile) return null;
8
+ let migratedConfigFile = null;
9
+ const eligibleMigrations = Object.values(automigrations).filter((migration) => {
10
+ try {
11
+ return migration.check(configFile);
12
+ } catch {
13
+ return false;
14
+ }
15
+ });
16
+ if (eligibleMigrations.length === 0) return null;
17
+ for (const migration of eligibleMigrations) {
18
+ console.log(pc.red(`\n ✋ Automigration detected \n`));
19
+ console.log(pc.yellow(`Config file ${pc.blue(configFilePath)} is eligible for migration: ${pc.blue(migration.name)}\n`));
20
+ console.log(`${migration.message}`);
21
+ let answer = true;
22
+ if (!yes) answer = await confirm({ message: `Do you want to migrate?` });
23
+ else console.log(pc.green(`Auto-confirming migration with --yes flag`));
24
+ if (!answer) migratedConfigFile = migration.no(migratedConfigFile ?? configFile);
25
+ else {
26
+ migratedConfigFile = migration.yes(migratedConfigFile ?? configFile);
27
+ await dsfs.writeFile(configFilePath, migratedConfigFile);
28
+ }
29
+ }
30
+ return migratedConfigFile;
31
+ };
32
+ //#endregion
33
+ export { checkAutomigrate };
@@ -23,7 +23,7 @@ export declare const semanticColorMap: {
23
23
  readonly 'base-contrast-default': 16;
24
24
  };
25
25
  export declare const semanticColorNames: ("background-default" | "background-tinted" | "surface-default" | "surface-tinted" | "surface-hover" | "surface-active" | "border-subtle" | "border-default" | "border-strong" | "text-subtle" | "text-default" | "base-default" | "base-hover" | "base-active" | "base-contrast-subtle" | "base-contrast-default")[];
26
- export declare const semanticColorNumbers: (1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16)[];
26
+ export declare const semanticColorNumbers: (2 | 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16)[];
27
27
  type SemanticColorMap = typeof semanticColorMap;
28
28
  type SemanticColorMapping = {
29
29
  [K in keyof SemanticColorMap]: {
@@ -1,4 +1,4 @@
1
1
  export * from './colors/index.js';
2
- export { type CreateConfigSchema as ConfigSchema, configFileCreateSchema as configSchema, } from './config.js';
2
+ export { type CreateConfigSchema as ConfigSchema, configFileCreateSchema as configSchema, } from './schemas/v1.1/schema.js';
3
3
  export * from './tokens/index.js';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,OAAO,EACL,KAAK,kBAAkB,IAAI,YAAY,EACvC,sBAAsB,IAAI,YAAY,GACvC,MAAM,aAAa,CAAC;AACrB,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,OAAO,EACL,KAAK,kBAAkB,IAAI,YAAY,EACvC,sBAAsB,IAAI,YAAY,GACvC,MAAM,0BAA0B,CAAC;AAClC,cAAc,mBAAmB,CAAC"}
package/dist/src/index.js CHANGED
@@ -1,10 +1,11 @@
1
+ import { deprecatedCLIOptions } from "./schemas/helpers.js";
1
2
  import { baseColorNames, baseColors, colorMetadata, colorNames, dsLinkColor, getColorMetadataByNumber } from "./colors/colorMetadata.js";
2
3
  import { HSLToHex, areColorsContrasting, canTextBeUsedOnColors, convertColor, convertToHex, getContrastFromHex, getContrastFromLightness, getLightnessFromHex, getLuminanceFromColor, getLuminanceFromLightness, hexToCssHsl, hexToHSL, hexToHsluv, hexToRgb, hslArrToCss, isHexColor, rgbToHex } from "./colors/utils.js";
3
4
  import { RESERVED_COLORS, generateColorContrast, generateColorScale, generateColorSchemes, getCssVariable } from "./colors/theme.js";
4
5
  import { semanticColorMap, semanticColorNames, semanticColorNumbers } from "./colors/types.js";
5
6
  import "./colors/index.js";
6
- import { cliOptions, createTokens } from "./tokens/create.js";
7
- import { configFileCreateSchema } from "./config.js";
7
+ import { configFileCreateSchema } from "./schemas/v1.1/schema.js";
8
+ import { createTokens } from "./tokens/create.js";
8
9
  import { formatThemeCSS, formatTokens } from "./tokens/format.js";
9
10
  import "./tokens/index.js";
10
- export { HSLToHex, RESERVED_COLORS, areColorsContrasting, baseColorNames, baseColors, canTextBeUsedOnColors, cliOptions, colorMetadata, colorNames, configFileCreateSchema as configSchema, convertColor, convertToHex, createTokens, dsLinkColor, formatThemeCSS, formatTokens, generateColorContrast, generateColorScale, generateColorSchemes, getColorMetadataByNumber, getContrastFromHex, getContrastFromLightness, getCssVariable, getLightnessFromHex, getLuminanceFromColor, getLuminanceFromLightness, hexToCssHsl, hexToHSL, hexToHsluv, hexToRgb, hslArrToCss, isHexColor, rgbToHex, semanticColorMap, semanticColorNames, semanticColorNumbers };
11
+ export { HSLToHex, RESERVED_COLORS, areColorsContrasting, baseColorNames, baseColors, canTextBeUsedOnColors, deprecatedCLIOptions as cliOptions, colorMetadata, colorNames, configFileCreateSchema as configSchema, convertColor, convertToHex, createTokens, dsLinkColor, formatThemeCSS, formatTokens, generateColorContrast, generateColorScale, generateColorSchemes, getColorMetadataByNumber, getContrastFromHex, getContrastFromLightness, getCssVariable, getLightnessFromHex, getLuminanceFromColor, getLuminanceFromLightness, hexToCssHsl, hexToHSL, hexToHsluv, hexToRgb, hslArrToCss, isHexColor, rgbToHex, semanticColorMap, semanticColorNames, semanticColorNumbers };
@@ -0,0 +1,10 @@
1
+ type Automigrate = {
2
+ name: string;
3
+ check: (config: any) => boolean;
4
+ message: string;
5
+ yes: (config: string) => string;
6
+ no: (config: string) => string;
7
+ };
8
+ declare const migration: Automigrate;
9
+ export default migration;
10
+ //# sourceMappingURL=flatten-color-categories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flatten-color-categories.d.ts","sourceRoot":"","sources":["../../../src/migrations/flatten-color-categories.ts"],"names":[],"mappings":"AAGA,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,OAAO,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IAChC,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;CAChC,CAAC;AAyDF,QAAA,MAAM,SAAS,EAAE,WAgBhB,CAAC;AAEF,eAAe,SAAS,CAAC"}
@@ -0,0 +1,51 @@
1
+ import pc from "picocolors";
2
+ //#region src/migrations/flatten-color-categories.ts
3
+ const isOldColorSchema = (theme) => {
4
+ return theme.colors && Object.keys(theme.colors).length === 3 && theme.colors.main && theme.colors.support && theme.colors.neutral;
5
+ };
6
+ const isConfigWithOldColorSchema = (config) => {
7
+ const currentConfig = JSON.parse(config);
8
+ if (!currentConfig.themes) return false;
9
+ return Object.values(currentConfig.themes).some(isOldColorSchema);
10
+ };
11
+ const yes = (config) => {
12
+ const currentConfig = JSON.parse(config);
13
+ const updatedThemes = {};
14
+ if (currentConfig.themes) for (const [themeName, _] of Object.entries(currentConfig.themes)) {
15
+ const theme = currentConfig.themes[themeName];
16
+ if (isOldColorSchema(theme)) {
17
+ const { main, support, neutral, ...restColors } = theme.colors;
18
+ updatedThemes[themeName] = {
19
+ ...theme,
20
+ colors: {
21
+ ...restColors,
22
+ ...main,
23
+ ...support,
24
+ neutral
25
+ }
26
+ };
27
+ } else updatedThemes[themeName] = theme;
28
+ }
29
+ const migratedConfig = {
30
+ ...currentConfig,
31
+ themes: updatedThemes
32
+ };
33
+ return JSON.stringify(migratedConfig, null, 2).concat("\n");
34
+ };
35
+ const migration = {
36
+ name: "Flatten color categories",
37
+ check: isConfigWithOldColorSchema,
38
+ message: `Your config file uses the old color schema with ${pc.yellow("main")} and ${pc.yellow("support")} categories.\nThis required migration will move all colors under a single ${pc.yellow("colors")} per theme.\n`,
39
+ yes: (config) => {
40
+ const migratedConfig = yes(config);
41
+ console.log(pc.green(`\nConfig file successfully migrated.`));
42
+ return migratedConfig;
43
+ },
44
+ no: (config) => {
45
+ const migratedConfig = yes(config);
46
+ console.log(pc.yellow("\nUsing existing config file but migration was skipped.\n"));
47
+ return migratedConfig;
48
+ }
49
+ };
50
+ //#endregion
51
+ export { migration as default };
@@ -1,3 +1,12 @@
1
+ export declare const automigrations: {
2
+ colorCategoryFlattening: {
3
+ name: string;
4
+ check: (config: any) => boolean;
5
+ message: string;
6
+ yes: (config: string) => string;
7
+ no: (config: string) => string;
8
+ };
9
+ };
1
10
  declare const _default: {
2
11
  'css-beta-to-v1': (glob?: string) => Promise<void>;
3
12
  'css-renames-next48-to-next49': (glob?: string) => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/migrations/index.ts"],"names":[],"mappings":";;;;AAGA,wBAGE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/migrations/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc;;;;;;;;CAE1B,CAAC;;;;;AAEF,wBAGE"}
@@ -1,9 +1,11 @@
1
1
  import beta_to_v1_default from "./beta-to-v1.js";
2
2
  import color_rename_next49_default from "./color-rename-next49.js";
3
+ import migration from "./flatten-color-categories.js";
3
4
  //#region src/migrations/index.ts
5
+ const automigrations = { colorCategoryFlattening: migration };
4
6
  var migrations_default = {
5
7
  "css-beta-to-v1": beta_to_v1_default,
6
8
  "css-renames-next48-to-next49": color_rename_next49_default
7
9
  };
8
10
  //#endregion
9
- export { migrations_default as default };
11
+ export { automigrations, migrations_default as default };
@@ -0,0 +1,29 @@
1
+ import type { z } from 'zod';
2
+ export declare const deprecatedCLIOptions: {
3
+ readonly outDir: "out-dir";
4
+ readonly clean: "clean";
5
+ readonly tailwind: "tailwind";
6
+ readonly theme: {
7
+ readonly colors: {
8
+ readonly main: "main-colors";
9
+ readonly support: "support-colors";
10
+ readonly neutral: "neutral-color";
11
+ };
12
+ readonly typography: {
13
+ readonly fontFamily: "font-family";
14
+ };
15
+ readonly borderRadius: "border-radius";
16
+ };
17
+ };
18
+ /**
19
+ * Validates a configuration object against a provided Zod schema.
20
+ *
21
+ * @template T - The expected type of the validated configuration.
22
+ * @param schema - A Zod schema used to validate the configuration object.
23
+ * @param unvalidatedConfig - The configuration object to validate.
24
+ * @returns The validated configuration object, typed as T.
25
+ * @throws Exits the process with code 1 if validation fails, after logging a friendly error message.
26
+ */
27
+ export declare function validateConfig<T>(schema: z.ZodType<T>, unvalidatedConfig: Record<string, unknown>, configFilePath: string): T;
28
+ export declare function parseConfig<T>(configFile: string, configFilePath: string): T;
29
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/schemas/helpers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAG7B,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;CAevB,CAAC;AAkCX;;;;;;;;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"}
@@ -0,0 +1,71 @@
1
+ import pc from "picocolors";
2
+ import * as R from "ramda";
3
+ import { fromError } from "zod-validation-error";
4
+ //#region src/schemas/helpers.ts
5
+ const deprecatedCLIOptions = {
6
+ outDir: "out-dir",
7
+ clean: "clean",
8
+ tailwind: "tailwind",
9
+ theme: {
10
+ colors: {
11
+ main: "main-colors",
12
+ support: "support-colors",
13
+ neutral: "neutral-color"
14
+ },
15
+ typography: { fontFamily: "font-family" },
16
+ borderRadius: "border-radius"
17
+ }
18
+ };
19
+ function mapPathToOptionName(path) {
20
+ const normalisedPath = path[0] === "themes" ? ["theme", ...R.drop(2, path)] : path;
21
+ const option = R.path(normalisedPath, deprecatedCLIOptions);
22
+ if (typeof option !== "string") return;
23
+ return option;
24
+ }
25
+ function makeFriendlyError(err) {
26
+ try {
27
+ return fromError(err, { messageBuilder: (issues) => issues.map((issue) => {
28
+ const issuePath = issue.path.join(".");
29
+ const optionName = mapPathToOptionName(issue.path);
30
+ const errorCode = `(error code: ${issue.code})`;
31
+ const optionMessage = optionName ? ` or CLI option --${optionName}` : "";
32
+ return ` - Error in JSON value ${pc.red(issuePath)}${optionMessage}:
33
+ ${issue.message} ${pc.dim(errorCode)}`;
34
+ }).join("\n") });
35
+ } catch (_err2) {
36
+ console.error(pc.red(err instanceof Error ? err.message : "Unknown error occurred while parsing config file"));
37
+ console.error(err instanceof Error ? err.stack : "No stack trace available");
38
+ }
39
+ }
40
+ /**
41
+ * Validates a configuration object against a provided Zod schema.
42
+ *
43
+ * @template T - The expected type of the validated configuration.
44
+ * @param schema - A Zod schema used to validate the configuration object.
45
+ * @param unvalidatedConfig - The configuration object to validate.
46
+ * @returns The validated configuration object, typed as T.
47
+ * @throws Exits the process with code 1 if validation fails, after logging a friendly error message.
48
+ */
49
+ function validateConfig(schema, unvalidatedConfig, configFilePath) {
50
+ try {
51
+ return schema.parse(unvalidatedConfig);
52
+ } catch (err) {
53
+ console.error(pc.redBright(`Invalid config file at ${pc.red(configFilePath)}`));
54
+ const validationError = makeFriendlyError(err);
55
+ console.error(validationError?.toString());
56
+ process.exit(1);
57
+ }
58
+ }
59
+ function parseConfig(configFile, configFilePath) {
60
+ if (!configFile) return {};
61
+ try {
62
+ return JSON.parse(configFile);
63
+ } catch (err) {
64
+ console.error(pc.redBright(`Failed parsing config file at ${pc.red(configFilePath)}`));
65
+ const validationError = makeFriendlyError(err);
66
+ console.error(validationError?.toString());
67
+ process.exit(1);
68
+ }
69
+ }
70
+ //#endregion
71
+ export { deprecatedCLIOptions, parseConfig, validateConfig };
@@ -1,15 +1,4 @@
1
1
  import { z } from 'zod';
2
- /**
3
- * Validates a configuration object against a provided Zod schema.
4
- *
5
- * @template T - The expected type of the validated configuration.
6
- * @param schema - A Zod schema used to validate the configuration object.
7
- * @param unvalidatedConfig - The configuration object to validate.
8
- * @returns The validated configuration object, typed as T.
9
- * @throws Exits the process with code 1 if validation fails, after logging a friendly error message.
10
- */
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
2
  export declare const colorRegex: RegExp;
14
3
  declare const overridesSchema: z.ZodOptional<z.ZodObject<{
15
4
  colors: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodEnum<{
@@ -178,4 +167,4 @@ export type CreateConfigSchema = z.infer<typeof configFileCreateSchema>;
178
167
  export type ConfigSchemaTheme = z.infer<typeof themeSchema>;
179
168
  export type ColorOverrideSchema = z.infer<typeof overridesSchema>;
180
169
  export {};
181
- //# sourceMappingURL=config.d.ts.map
170
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/schemas/v1/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB,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"}