@esri/calcite-design-tokens 1.0.0 → 1.1.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/.eslintrc.cjs +85 -0
  2. package/.turbo/turbo-build.log +64 -0
  3. package/.turbo/turbo-test.log +14 -0
  4. package/CHANGELOG.md +17 -4
  5. package/CONTRIBUTING.md +41 -0
  6. package/LICENSE.md +13 -0
  7. package/README.md +9 -11
  8. package/dist/css/brand-light.css +1 -1
  9. package/dist/css/calcite-dark.css +1 -1
  10. package/dist/css/calcite-headless.css +2 -1
  11. package/dist/css/calcite-light.css +1 -1
  12. package/dist/scss/brand-light.scss +1 -1
  13. package/dist/scss/calcite-dark.scss +1 -1
  14. package/dist/scss/calcite-headless.scss +3 -1
  15. package/dist/scss/calcite-light.scss +1 -1
  16. package/jest.config.json +16 -0
  17. package/package.json +11 -72
  18. package/src/$metadata.json +76 -0
  19. package/src/$themes.json +2360 -0
  20. package/src/brand/dark.json +1 -0
  21. package/src/brand/global.json +1 -0
  22. package/src/brand/light.json +20 -0
  23. package/src/calcite/dark.json +2488 -0
  24. package/src/calcite/light.json +2508 -0
  25. package/src/component/accordion-item.json +172 -0
  26. package/src/component/accordion.json +192 -0
  27. package/src/component/action-bar-grid.json +66 -0
  28. package/src/component/action-bar.json +66 -0
  29. package/src/component/action-pad-grid.json +80 -0
  30. package/src/component/action-pad.json +80 -0
  31. package/src/component/action.json +156 -0
  32. package/src/component/alert.json +258 -0
  33. package/src/component/avatar.json +140 -0
  34. package/src/component/block-section.json +124 -0
  35. package/src/component/block.json +198 -0
  36. package/src/component/button.json +650 -0
  37. package/src/component/card.json +116 -0
  38. package/src/component/checkbox.json +110 -0
  39. package/src/component/chip.json +382 -0
  40. package/src/component/color-picker.json +148 -0
  41. package/src/component/combobox.json +152 -0
  42. package/src/component/date-picker.json +354 -0
  43. package/src/component/dropdown-item.json +384 -0
  44. package/src/component/dropdown.json +58 -0
  45. package/src/component/fab.json +490 -0
  46. package/src/component/filter.json +174 -0
  47. package/src/component/input-date-picker.json +224 -0
  48. package/src/component/input-datetime-local.json +230 -0
  49. package/src/component/input-email.json +244 -0
  50. package/src/component/input-file.json +244 -0
  51. package/src/component/input-message.json +72 -0
  52. package/src/component/input-month.json +244 -0
  53. package/src/component/input-number.json +244 -0
  54. package/src/component/input-password.json +244 -0
  55. package/src/component/input-search.json +244 -0
  56. package/src/component/input-telephone.json +244 -0
  57. package/src/component/input-text.json +244 -0
  58. package/src/component/input-time.json +1 -0
  59. package/src/component/input-week.json +244 -0
  60. package/src/component/label.json +26 -0
  61. package/src/component/link.json +44 -0
  62. package/src/component/loader.json +130 -0
  63. package/src/component/modal.json +278 -0
  64. package/src/component/notice.json +280 -0
  65. package/src/component/pagination.json +152 -0
  66. package/src/component/panel-header.json +88 -0
  67. package/src/component/popover.json +170 -0
  68. package/src/component/radio.json +124 -0
  69. package/src/component/rating.json +243 -0
  70. package/src/component/scrim.json +18 -0
  71. package/src/component/segmented-control.json +154 -0
  72. package/src/component/slider-histogram-range.json +284 -0
  73. package/src/component/slider-histogram.json +280 -0
  74. package/src/component/slider-range.json +226 -0
  75. package/src/component/slider.json +226 -0
  76. package/src/component/split-button.json +830 -0
  77. package/src/component/stepper-item.json +372 -0
  78. package/src/component/stepper.json +152 -0
  79. package/src/component/switch.json +178 -0
  80. package/src/component/tab-title.json +228 -0
  81. package/src/component/tabs.json +242 -0
  82. package/src/component/textarea.json +200 -0
  83. package/src/component/time-picker.json +138 -0
  84. package/src/component/tip-manager.json +118 -0
  85. package/src/component/tip.json +114 -0
  86. package/src/component/tooltip.json +66 -0
  87. package/src/component/tree-item.json +176 -0
  88. package/src/core.json +1709 -0
  89. package/src/semantic.json +1709 -0
  90. package/support/run.ts +16 -0
  91. package/support/token-transformer/format/scss.ts +81 -0
  92. package/support/token-transformer/getThemes.ts +41 -0
  93. package/support/token-transformer/parse/expandComposites.test.ts +144 -0
  94. package/support/token-transformer/parse/expandComposites.ts +72 -0
  95. package/support/token-transformer/sd-run.ts +147 -0
  96. package/support/token-transformer/transform/nameCamelCase.test.ts +36 -0
  97. package/support/token-transformer/transform/nameCamelCase.ts +15 -0
  98. package/support/token-transformer/transform/nameKebabCase.test.ts +36 -0
  99. package/support/token-transformer/transform/nameKebabCase.ts +26 -0
  100. package/support/token-transformer/utils/compositeTokens.test.ts +133 -0
  101. package/support/token-transformer/utils/compositeTokens.ts +103 -0
  102. package/support/token-transformer/utils/convertTokenToStyleDictionaryFormat.ts +20 -0
  103. package/support/token-transformer/utils/matchList.ts +11 -0
  104. package/support/token-transformer/utils/parseName.ts +18 -0
  105. package/support/token-transformer/utils/parseTokenPath.ts +19 -0
  106. package/support/token-transformer/utils/regex.ts +9 -0
  107. package/support/token-transformer/utils/sortAllTokens.ts +12 -0
  108. package/support/token-transformer/utils/transformOptions.ts +31 -0
  109. package/tsconfig-base.json +17 -0
  110. package/tsconfig-eslint.json +4 -0
  111. package/tsconfig.json +4 -0
package/support/run.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { readFileSync } from "fs";
2
+ import { resolve, dirname } from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { getThemes } from "./token-transformer/getThemes.js";
5
+ import { run } from "./token-transformer/sd-run.js";
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+
10
+ /**
11
+ * Get all themes defined int the tokens/$themes.json and generate a Style Dictionary output for each theme
12
+ */
13
+ const rawData = readFileSync(resolve(__dirname, "../src/$themes.json"), { encoding: "utf-8" });
14
+ const data = JSON.parse(rawData);
15
+
16
+ getThemes(data).then((themes) => Promise.all(themes.map((theme) => run("src", "dist", theme))));
@@ -0,0 +1,81 @@
1
+ import { pascalCase, sentenceCase } from "change-case";
2
+ import StyleDictionary, { Dictionary, File, Platform, Options } from "style-dictionary";
3
+ import { sortAllTokens } from "../utils/sortAllTokens.js";
4
+
5
+ const regexThemeGroup = /calcite|brand/gi;
6
+ const regexFileNameWithoutExtension = /\w+(?=\.\w+$)/gi;
7
+
8
+ /**
9
+ * Exports SCSS style formats
10
+ *
11
+ * @param {object} fileInfo the file object
12
+ * @param {Dictionary} fileInfo.dictionary the Style Dictionary object
13
+ * @param {File} fileInfo.file information about the file to be generated
14
+ * @param {Platform} [fileInfo.platform] the platform to generate the asset for
15
+ * @param {Options} fileInfo.options the Style Dictionary format options passed from the config
16
+ * @returns {string} a string that is passed to fs.writeFileSync
17
+ */
18
+ export function formatSCSS(fileInfo: {
19
+ dictionary: Dictionary;
20
+ file: File;
21
+ platform?: Platform;
22
+ options: Options & { themeable?: boolean };
23
+ }): string {
24
+ const { dictionary, file, options } = fileInfo;
25
+ const { outputReferences } = options;
26
+ const themeName = pascalCase(
27
+ sentenceCase(file.destination.match(regexFileNameWithoutExtension)[0])
28
+ .split(" ")
29
+ .filter((n) => !regexThemeGroup.test(n))
30
+ .join(" ")
31
+ ).toLowerCase();
32
+ const sassProps = StyleDictionary.formatHelpers.createPropertyFormatter({
33
+ outputReferences,
34
+ dictionary,
35
+ format: "sass",
36
+ });
37
+ const cssProps = StyleDictionary.formatHelpers.createPropertyFormatter({
38
+ outputReferences,
39
+ dictionary,
40
+ format: "css",
41
+ });
42
+ const sortedTokens = sortAllTokens(dictionary, outputReferences);
43
+ const coreTokens = [...sortedTokens].reduce(
44
+ (acc, token) => {
45
+ token.value = token.value.includes(" ") ? `"${token.value}"` : token.value === "Demi" ? 600 : token.value;
46
+ acc[1].push(cssProps(token));
47
+
48
+ if (token.filePath.includes("core")) {
49
+ const sassToken = { ...token };
50
+ const path = sassToken.path.filter((p) => !/(core|default|font$)/.test(p));
51
+ sassToken.name = sassToken.type === "color" ? path.slice(-1).join("-") : path.join("-");
52
+ sassToken.original.value = sassToken.original.value[0] === "{" ? sassToken.value : sassToken.original.value;
53
+ acc[0].push(sassProps(sassToken));
54
+ }
55
+
56
+ if (/dark|light/.test(token.filePath) && !token.path.includes("component")) {
57
+ const sassToken = { ...token };
58
+ const path = sassToken.path.reduce((acc, p) => {
59
+ if (p === "default") {
60
+ return acc;
61
+ }
62
+ acc.push(p === "color" ? "ui" : p);
63
+ return acc;
64
+ }, []);
65
+ path.push(token.filePath.includes("dark") ? "dark" : "light");
66
+ sassToken.name = path.join("-");
67
+ acc[0].push(sassProps(sassToken));
68
+ }
69
+
70
+ return acc;
71
+ },
72
+ [[], []]
73
+ );
74
+
75
+ return `${StyleDictionary.formatHelpers.fileHeader({ file })}
76
+ ${coreTokens[0].join("\n")}
77
+
78
+ @mixin calcite-theme-${themeName}() {
79
+ ${coreTokens[1].join("\n")}
80
+ }`;
81
+ }
@@ -0,0 +1,41 @@
1
+ import { Options } from "style-dictionary";
2
+
3
+ export interface ThemeFileInterface {
4
+ id: string;
5
+ name: string;
6
+ selectedTokenSets: Record<string, "enabled" | "disabled" | "source">;
7
+ $figmaStyleReferences?: Record<string, string>;
8
+ }
9
+
10
+ export type Theme = {
11
+ name: string;
12
+ enabled: string[];
13
+ disabled: string[];
14
+ source: string[];
15
+ id?: string;
16
+ options?: Options;
17
+ };
18
+
19
+ /**
20
+ *
21
+ * @param {ThemeFileInterface} themes an array of Figma Token Studio theme definition objects
22
+ * @returns {Array} an array of Style Dictionary theme definition objects
23
+ */
24
+ export async function getThemes(themes: ThemeFileInterface[]): Promise<Theme[]> {
25
+ return themes.map((themeConfig) => {
26
+ const themeTypes = { enabled: [], disabled: [], source: [] };
27
+ const { name, id, selectedTokenSets } = themeConfig;
28
+ const { enabled, disabled, source } = Object.entries(selectedTokenSets).reduce((acc, [key, value]) => {
29
+ acc[value].push(key);
30
+ return acc;
31
+ }, themeTypes);
32
+
33
+ return {
34
+ name,
35
+ id,
36
+ enabled,
37
+ disabled,
38
+ source,
39
+ };
40
+ });
41
+ }
@@ -0,0 +1,144 @@
1
+ const mockCorrectTypeCompoundToken = {
2
+ core: {
3
+ 1: {
4
+ type: "sizing",
5
+ value: "10",
6
+ },
7
+ },
8
+ compound: {
9
+ value: {
10
+ fontFamily: "$core.font.font-family.primary",
11
+ fontWeight: "$core.font.font-weight.light",
12
+ lineHeight: "$core.font.line-height.fixed.0",
13
+ fontSize: "$core.font.font-size.0",
14
+ letterSpacing: "$core.font.letter-spacing.normal",
15
+ paragraphSpacing: "$core.font.paragraph-spacing.normal",
16
+ textDecoration: "$core.font.text-decoration.none",
17
+ textCase: "$core.font.text-case.none",
18
+ },
19
+ type: "typography",
20
+ },
21
+ };
22
+ const mockTransformedCompoundTokens = {
23
+ core: {
24
+ 1: {
25
+ type: "sizing",
26
+ value: "10",
27
+ },
28
+ },
29
+ compound: {
30
+ "font-family": {
31
+ value: "$core.font.font-family.primary",
32
+ type: "font-family",
33
+ },
34
+ "font-weight": {
35
+ value: "$core.font.font-weight.light",
36
+ type: "font-weights",
37
+ },
38
+ "line-height": {
39
+ value: "$core.font.line-height.fixed.0",
40
+ type: "line-heights",
41
+ },
42
+ "font-size": {
43
+ value: "$core.font.font-size.0",
44
+ type: "font-size",
45
+ },
46
+ "letter-spacing": {
47
+ value: "$core.font.letter-spacing.normal",
48
+ type: "letter-spacing",
49
+ },
50
+ "paragraph-spacing": {
51
+ value: "$core.font.paragraph-spacing.normal",
52
+ type: "paragraph-spacing",
53
+ },
54
+ "text-decoration": {
55
+ value: "$core.font.text-decoration.none",
56
+ type: "font-style",
57
+ },
58
+ "text-case": {
59
+ value: "$core.font.text-case.none",
60
+ type: "text-case",
61
+ },
62
+ },
63
+ };
64
+
65
+ const handleTokenStudioVariables = jest.fn((token) => (token.includes("$") ? `{${token.replace(/\$/g, "")}}` : token));
66
+ const convertTokenToStyleDictionaryFormat = jest.fn(() => handleTokenStudioVariables);
67
+ const shouldExpand = jest.fn().mockReturnValue(true);
68
+ const expandToken = jest.fn().mockReturnValue(mockTransformedCompoundTokens);
69
+
70
+ jest.mock("../utils/compositeTokens.js", () => {
71
+ const originalModule = jest.requireActual("../utils/compositeTokens.js");
72
+ return {
73
+ __esModule: false,
74
+ ...originalModule,
75
+ shouldExpand,
76
+ expandToken,
77
+ };
78
+ });
79
+
80
+ jest.mock("../utils/convertTokenToStyleDictionaryFormat.js", () => {
81
+ const originalModule = jest.requireActual("../utils/convertTokenToStyleDictionaryFormat.js");
82
+ return {
83
+ __esModule: false,
84
+ ...originalModule,
85
+ convertTokenToStyleDictionaryFormat,
86
+ };
87
+ });
88
+
89
+ import * as expandComposites from "./expandComposites";
90
+
91
+ describe("expand token dictionary", () => {
92
+ beforeEach(() => {
93
+ jest.clearAllMocks();
94
+ });
95
+
96
+ it("should not add placeholder elements", () => {
97
+ const placeolderToken = {
98
+ "[placeholder-component]": {
99
+ type: "other",
100
+ value: "#333",
101
+ },
102
+ };
103
+ const placeholderValue = {
104
+ compoonent: {
105
+ type: "other",
106
+ value: "[placholder-value]",
107
+ },
108
+ };
109
+
110
+ // @ts-expect-error - it's fine.
111
+ const testExpandPlaceholderKey = expandComposites.expandComposites(placeolderToken, "./fakePath");
112
+ // @ts-expect-error - it's fine.
113
+ const testExpandPlaceholderValue = expandComposites.expandComposites(placeholderValue, "./fakePath");
114
+
115
+ expect(testExpandPlaceholderKey).toMatchObject({});
116
+ expect(testExpandPlaceholderValue).toMatchObject({});
117
+ });
118
+
119
+ it('should loop through a dictionary and run "shouldExpand" and "expandToken" on each composite token', () => {
120
+ // @ts-expect-error - it's fine.
121
+ const testExpandComposite = expandComposites.expandComposites(mockCorrectTypeCompoundToken, "./fakePath");
122
+ expect(handleTokenStudioVariables).toHaveBeenCalledTimes(1);
123
+ expect(shouldExpand).toHaveBeenCalledTimes(1);
124
+ expect(expandToken).toHaveBeenCalledTimes(1);
125
+ expect(testExpandComposite).toMatchObject(mockTransformedCompoundTokens);
126
+ });
127
+
128
+ it("should not run expand token on unrecognized types", () => {
129
+ const mockDictionary = {
130
+ core: {
131
+ type: "customType",
132
+ value: {
133
+ fontFamily: "Avanir",
134
+ fontSize: "12px",
135
+ },
136
+ },
137
+ };
138
+ // @ts-expect-error - it's fine this is a test
139
+ const testExpandComposite = expandComposites.expandComposites(mockDictionary, "./fakePath");
140
+ expect(shouldExpand).not.toHaveBeenCalled();
141
+ expect(expandToken).not.toHaveBeenCalled();
142
+ expect(testExpandComposite).toMatchObject(mockDictionary);
143
+ });
144
+ });
@@ -0,0 +1,72 @@
1
+ import { DeepKeyTokenMap } from "@tokens-studio/types";
2
+ import { DesignToken } from "style-dictionary/types/DesignToken.js";
3
+ import {
4
+ TransformOptions,
5
+ ExpandablesAsStrings,
6
+ Expandables,
7
+ expandablesAsStringsArr,
8
+ } from "../utils/transformOptions.js";
9
+ import { matchPlaceholderElement, tokenStudioCustomVariableIndicator } from "../utils/regex.js";
10
+ import { shouldExpand, expandToken } from "../utils/compositeTokens.js";
11
+ import { convertTokenToStyleDictionaryFormat } from "../utils/convertTokenToStyleDictionaryFormat.js";
12
+
13
+ /**
14
+ * Figma Token Studio creates an odd type of composite token where the "value" may contain an object of "key: value" pairs rather than a sting.
15
+ * Here we will lift these composite tokens up to match the Style Dictionary format.
16
+ *
17
+ * @param {DeepKeyTokenMap} dictionary the raw JSON object in the token files. We will assume this is a token object generated by Figma Token Studio and may require composite tokens to be expanded.
18
+ * @param {string} filePath the absolute file path to the JSON token file.
19
+ * @param {TransformOptions} transformOpts the options passed in from the Style Dictionary config and runner
20
+ * @returns {DeepKeyTokenMap} a token object where any Figma Token Studio composite tokens now match the Style Dictionary token format
21
+ */
22
+ export function expandComposites(
23
+ dictionary: DeepKeyTokenMap,
24
+ filePath: string,
25
+ transformOpts: TransformOptions = {}
26
+ ): DeepKeyTokenMap {
27
+ const opts = {
28
+ ...transformOpts,
29
+ expand: {
30
+ composition: true,
31
+ typography: false,
32
+ border: false,
33
+ shadow: false,
34
+ ...transformOpts.expand,
35
+ },
36
+ };
37
+ const returnSlice: DeepKeyTokenMap = {};
38
+ const handleTokenStudioVariables = convertTokenToStyleDictionaryFormat(tokenStudioCustomVariableIndicator);
39
+ const newDictionary = Object.entries(dictionary).reduce((acc, [key, token]) => {
40
+ const { type } = token;
41
+
42
+ if (
43
+ matchPlaceholderElement.test(`${key}`) ||
44
+ (typeof token.value === "string" && matchPlaceholderElement.test(`${token.value}`))
45
+ ) {
46
+ return acc;
47
+ }
48
+
49
+ if (token.value && type) {
50
+ const includesType = expandablesAsStringsArr.includes(`${type}`);
51
+
52
+ if (includesType) {
53
+ const expandType = (type as ExpandablesAsStrings) === "boxShadow" ? "shadow" : type;
54
+ const expand = shouldExpand<Expandables>(token as Expandables, opts.expand[`${expandType}`], filePath);
55
+ if (expand) {
56
+ const expandedToken = expandToken(token as DesignToken, expandType === "shadow", handleTokenStudioVariables);
57
+ return expandedToken;
58
+ }
59
+ } else if (typeof token.value === "string") {
60
+ token.value = handleTokenStudioVariables(token.value);
61
+ acc[key] = token;
62
+ } else {
63
+ acc[key] = token;
64
+ }
65
+ } else if (typeof token === "object") {
66
+ acc[key] = expandComposites(token as DeepKeyTokenMap, filePath, transformOpts);
67
+ }
68
+ return acc;
69
+ }, returnSlice);
70
+
71
+ return newDictionary || {};
72
+ }
@@ -0,0 +1,147 @@
1
+ import { registerTransforms } from "@tokens-studio/sd-transforms";
2
+ import StyleDictionary from "style-dictionary";
3
+ import { expandComposites } from "./parse/expandComposites.js";
4
+ import { formatSCSS } from "./format/scss.js";
5
+ import { matchExclusions } from "./utils/regex.js";
6
+ import { matchList } from "./utils/matchList.js";
7
+ import { nameCamelCase } from "./transform/nameCamelCase.js";
8
+ import { nameKebabCase } from "./transform/nameKebabCase.js";
9
+ import { parseName } from "./utils/parseName.js";
10
+ import { Theme } from "./getThemes.js";
11
+
12
+ /**
13
+ * Style Dictionary runner configuration overrides.
14
+ *
15
+ * @param {string} tokenDir the directory containing design token files
16
+ * @param {string} buildPath the directory to write generated assets to
17
+ * @param {Theme} theme the theme configuration to use to generate the platform asset files
18
+ * @param {string} theme.name the name of the theme. This will be used as the basis for the generated asset file names.
19
+ * @param {string[]} theme.enabled an array of partial file names matching the token files which should be included in the output
20
+ * @param {string[]} theme.disabled an array of partial file names matching the token files which should explicitly not be included in the output
21
+ * @param {string[]} theme.source an array of partial file names matching the token files which should not always be included in the output but who's values should be used for variables references in the "enabled" files
22
+ */
23
+ export const run = async (
24
+ tokenDir = "tokens",
25
+ buildPath = "dist",
26
+ theme: Pick<Theme, "enabled" | "disabled" | "name" | "source">
27
+ ): Promise<void> => {
28
+ const fileName = parseName(theme.name);
29
+ const include = theme.source.map((tokenFile) => `${tokenDir}/${tokenFile}.json`);
30
+ const source = theme.enabled.map((tokenFile) => `${tokenDir}/${tokenFile}.json`);
31
+ const options = {
32
+ enabled: theme.enabled,
33
+ source: theme.source,
34
+ disabled: theme.disabled,
35
+ outputReferences: false,
36
+ sourceReferencesOnly: false,
37
+ };
38
+
39
+ // Here we are registering the Transforms provided by Token Studio
40
+ // https://github.com/tokens-studio/sd-transforms
41
+ // @ts-expect-error - @token-studio does not keep their types up to date.
42
+ await registerTransforms(StyleDictionary, {
43
+ expand: false,
44
+ });
45
+
46
+ // Register custom formatter https://amzn.github.io/style-dictionary/#/formats?id=custom-formats
47
+ StyleDictionary.registerFormat({
48
+ name: "calcite/scss",
49
+ formatter: formatSCSS,
50
+ });
51
+
52
+ // Registering Style Dictionary transformers https://amzn.github.io/style-dictionary/#/transforms?id=defining-custom-transforms
53
+ StyleDictionary.registerTransform({
54
+ name: "name/calcite/camel",
55
+ type: "name",
56
+ transformer: nameCamelCase,
57
+ });
58
+
59
+ StyleDictionary.registerTransform({
60
+ name: "name/calcite/kebab",
61
+ type: "name",
62
+ transformer: nameKebabCase,
63
+ });
64
+
65
+ StyleDictionary.registerFilter({
66
+ name: "filterSource",
67
+ matcher: (token) => token.isSource,
68
+ });
69
+
70
+ // We are programmatically creating the Style Dictionary configuration here
71
+ // https://amzn.github.io/style-dictionary/#/config
72
+ const sd = StyleDictionary.extend({
73
+ source,
74
+ include,
75
+ platforms: {
76
+ css: {
77
+ prefix: "calcite",
78
+ transforms: [
79
+ "ts/descriptionToComment",
80
+ "ts/size/px",
81
+ "ts/opacity",
82
+ "ts/size/lineheight",
83
+ "ts/type/fontWeight",
84
+ "ts/resolveMath",
85
+ "ts/size/css/letterspacing",
86
+ "ts/color/css/hexrgba",
87
+ "ts/color/modifiers",
88
+ "name/calcite/kebab",
89
+ ],
90
+ buildPath: `${buildPath}/css/`,
91
+ files: [
92
+ {
93
+ destination: `${fileName}.css`,
94
+ format: "css/variables",
95
+ filter: /headless/gi.test(fileName) ? null : "filterSource",
96
+ options: /headless/gi.test(fileName) ? { ...options, outputReferences: true } : options,
97
+ },
98
+ ],
99
+ },
100
+ scss: {
101
+ prefix: "calcite",
102
+ transforms: [
103
+ "ts/descriptionToComment",
104
+ "ts/size/px",
105
+ "ts/opacity",
106
+ "ts/size/lineheight",
107
+ "ts/type/fontWeight",
108
+ "ts/resolveMath",
109
+ "ts/size/css/letterspacing",
110
+ "ts/color/css/hexrgba",
111
+ "ts/color/modifiers",
112
+ "name/calcite/kebab",
113
+ ],
114
+ buildPath: `${buildPath}/scss/`,
115
+ files: [
116
+ {
117
+ destination: `${fileName}.scss`,
118
+ format: "calcite/scss",
119
+ filter: /headless/gi.test(fileName) ? null : "filterSource",
120
+ options: /headless/gi.test(fileName) ? { ...options, outputReferences: true } : options,
121
+ },
122
+ ],
123
+ },
124
+ },
125
+ parsers: [
126
+ {
127
+ pattern: /\.json$/,
128
+ parse: (file) => {
129
+ if (matchList(file.filePath, [...include, ...theme.source, ...theme.enabled], matchExclusions)) {
130
+ const obj = JSON.parse(file.contents);
131
+ const expanded = expandComposites(obj, file.filePath);
132
+ return expanded;
133
+ }
134
+
135
+ return {};
136
+ },
137
+ },
138
+ ],
139
+ });
140
+
141
+ try {
142
+ sd.cleanAllPlatforms();
143
+ sd.buildAllPlatforms();
144
+ } catch (error) {
145
+ console.error(error);
146
+ }
147
+ };
@@ -0,0 +1,36 @@
1
+ import { nameCamelCase } from "./nameCamelCase";
2
+
3
+ describe("transform names to camel case", () => {
4
+ it("should transform a token path to token name in a camel case format", () => {
5
+ const mockToken = {
6
+ name: "current-name",
7
+ path: ["test", "token", "name"],
8
+ value: "fake-value",
9
+ filePath: "./fakePath.json",
10
+ original: {
11
+ value: "fake-value",
12
+ type: "composite",
13
+ },
14
+ isSource: true,
15
+ };
16
+ expect(nameCamelCase(mockToken, {})).toBe("testTokenName");
17
+ });
18
+
19
+ it("should add prefix to token name", () => {
20
+ const mockToken = {
21
+ name: "current-name",
22
+ path: ["test", "token", "name"],
23
+ value: "fake-value",
24
+ filePath: "./fakePath.json",
25
+ original: {
26
+ value: "fake-value",
27
+ type: "composite",
28
+ },
29
+ isSource: true,
30
+ };
31
+ const mockOptions = {
32
+ prefix: "calcite",
33
+ };
34
+ expect(nameCamelCase(mockToken, mockOptions)).toBe("calciteTestTokenName");
35
+ });
36
+ });
@@ -0,0 +1,15 @@
1
+ import { camelCase } from "change-case";
2
+ import { TransformedToken } from "style-dictionary/types/TransformedToken.js";
3
+ import { Options } from "style-dictionary/types/Options.js";
4
+ import { parseTokenPath } from "../utils/parseTokenPath.js";
5
+
6
+ /**
7
+ * Convert token name to camel case
8
+ *
9
+ * @param {TransformedToken} token Style Dictionary token object
10
+ * @param {Options} options Style Dictionary format options
11
+ * @returns {string} an updated name for the token which will be used for the final output
12
+ */
13
+ export function nameCamelCase(token: TransformedToken, options: Options): string {
14
+ return camelCase([options.prefix].concat(parseTokenPath(token.path)).join(" "));
15
+ }
@@ -0,0 +1,36 @@
1
+ import { nameKebabCase } from "./nameKebabCase";
2
+
3
+ describe("transform names to kebab case", () => {
4
+ it("should transform a token path to token name in a kebab case format", () => {
5
+ const mockToken = {
6
+ name: "current-name",
7
+ path: ["test", "token", "name"],
8
+ value: "fake-value",
9
+ filePath: "./fakePath.json",
10
+ original: {
11
+ value: "fake-value",
12
+ type: "composite",
13
+ },
14
+ isSource: true,
15
+ };
16
+ expect(nameKebabCase(mockToken, {})).toBe("test-token-name");
17
+ });
18
+
19
+ it("should add prefix to token name", () => {
20
+ const mockToken = {
21
+ name: "current-name",
22
+ path: ["test", "token", "name"],
23
+ value: "fake-value",
24
+ filePath: "./fakePath.json",
25
+ original: {
26
+ value: "fake-value",
27
+ type: "composite",
28
+ },
29
+ isSource: true,
30
+ };
31
+ const mockOptions = {
32
+ prefix: "calcite",
33
+ };
34
+ expect(nameKebabCase(mockToken, mockOptions)).toBe("calcite-test-token-name");
35
+ });
36
+ });
@@ -0,0 +1,26 @@
1
+ import { paramCase } from "change-case";
2
+ import { TransformedToken } from "style-dictionary/types/TransformedToken.js";
3
+ import { Options } from "style-dictionary/types/Options.js";
4
+ import { parseTokenPath } from "../utils/parseTokenPath.js";
5
+
6
+ /**
7
+ * convert token name to kebab case
8
+ *
9
+ * @param {TransformedToken} token Style Dictionary token object
10
+ * @param {Options} options Style Dictionary format options
11
+ * @returns {string} an updated name for the token which will be used for the final output
12
+ */
13
+ export function nameKebabCase(token: TransformedToken, options: Options): string {
14
+ const paths = token.path.reduce((acc, p, idx) => {
15
+ if (p === "core") {
16
+ acc.push("app");
17
+ } else if (typeof token.path[idx + 1] === "string" && !new RegExp(`${p}`).test(token.path[idx + 1])) {
18
+ acc.push(p);
19
+ } else if (idx === token.path.length - 1) {
20
+ acc.push(p);
21
+ }
22
+ return acc;
23
+ }, []);
24
+
25
+ return paramCase([options.prefix].concat(parseTokenPath(paths)).join(" "));
26
+ }