From 8ac92a344bf3530d957cf098f11fd18ea49b48be Mon Sep 17 00:00:00 2001 From: naporin0624 Date: Wed, 16 Oct 2024 06:59:45 +0000 Subject: [PATCH 01/15] feat(theme): create token json merger --- packages/theme/package.json | 1 + .../__snapshots__/token-object.test.ts.snap | 681 ++++++++++++++++++ .../theme/src/themes/token-object.test.ts | 143 ++++ packages/theme/src/themes/token-object.ts | 84 +++ packages/theme/src/themes/types.ts | 35 + packages/theme/tsconfig.json | 3 + packages/theme/vitest.config.ts | 1 - yarn.lock | 8 + 8 files changed, 955 insertions(+), 1 deletion(-) create mode 100644 packages/theme/src/themes/__snapshots__/token-object.test.ts.snap create mode 100644 packages/theme/src/themes/token-object.test.ts create mode 100644 packages/theme/src/themes/token-object.ts create mode 100644 packages/theme/src/themes/types.ts diff --git a/packages/theme/package.json b/packages/theme/package.json index 8cadc2ba1..2e7e5da16 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -37,6 +37,7 @@ "dependencies": { "@charcoal-ui/foundation": "^4.0.0-beta.14", "@charcoal-ui/utils": "^4.0.0-beta.14", + "deepmerge": "^4.3.1", "polished": "^4.1.4" }, "files": [ diff --git a/packages/theme/src/themes/__snapshots__/token-object.test.ts.snap b/packages/theme/src/themes/__snapshots__/token-object.test.ts.snap new file mode 100644 index 000000000..fb26c460f --- /dev/null +++ b/packages/theme/src/themes/__snapshots__/token-object.test.ts.snap @@ -0,0 +1,681 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` +{ + "border-width": { + "focus": { + "1": "1", + "2": "2", + }, + "l": "2", + "m": "1", + }, + "color": { + "background": { + "default": "rgba(31, 31, 31, 1)", + "secondary": "rgba(21, 21, 21, 1)", + "tertiary": "rgba(6, 6, 6, 1)", + }, + "border": { + "default": "rgba(228, 228, 228, 0.41)", + "disable": "rgba(228, 228, 228, 0.05)", + "focus": { + "1": "rgba(114, 181, 245, 1)", + "2": "rgba(39, 84, 126, 1)", + "legacy": "rgba(0, 150, 250, 0.32)", + }, + "hover": "rgba(228, 228, 228, 0.505)", + "hud": "rgba(31, 31, 31, 1)", + "negative": "rgba(136, 54, 46, 1)", + "press": "rgba(228, 228, 228, 0.61)", + "secondary": "rgba(228, 228, 228, 0.1)", + "selected": "rgba(8, 114, 190, 1)", + }, + "container": { + "default": "rgba(31, 31, 31, 1)", + "default-a": "rgba(228, 228, 228, 0)", + "disable": "rgba(51, 51, 51, 1)", + "discovery": { + "default": "rgba(197, 60, 51, 1)", + "hover": "rgba(217, 88, 76, 1)", + "press": "rgba(233, 114, 102, 1)", + }, + "hover": "rgba(41, 41, 41, 1)", + "hover-a": "rgba(228, 228, 228, 0.05)", + "hud": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(202, 202, 202, 1)", + "press": "rgba(188, 188, 188, 1)", + }, + "negative": { + "default": "rgba(197, 60, 51, 1)", + "hover": "rgba(217, 88, 76, 1)", + "press": "rgba(233, 114, 102, 1)", + }, + "neutral": { + "default": "rgba(112, 112, 112, 1)", + "hover": "rgba(130, 130, 130, 1)", + "press": "rgba(151, 151, 151, 1)", + }, + "notice": { + "default": "rgba(222, 185, 7, 1)", + "hover": "rgba(231, 199, 80, 1)", + "press": "rgba(252, 227, 145, 1)", + }, + "on-img": { + "default": "rgba(31, 31, 31, 0.37)", + "hover": "rgba(31, 31, 31, 0.475)", + "press": "rgba(31, 31, 31, 0.635)", + }, + "positive": { + "default": "rgba(13, 129, 5, 1)", + "hover": "rgba(58, 150, 52, 1)", + "press": "rgba(86, 169, 79, 1)", + }, + "press": "rgba(51, 51, 51, 1)", + "press-a": "rgba(228, 228, 228, 0.1)", + "primary": { + "default": "rgba(8, 114, 190, 1)", + "hover": "rgba(55, 136, 208, 1)", + "press": "rgba(83, 156, 224, 1)", + }, + "secondary": { + "default": "rgba(41, 41, 41, 1)", + "default-a": "rgba(228, 228, 228, 0.05)", + "hover": "rgba(51, 51, 51, 1)", + "hover-a": "rgba(228, 228, 228, 0.1)", + "press": "rgba(81, 81, 81, 1)", + "press-a": "rgba(228, 228, 228, 0.255)", + }, + "skeleton": "rgba(228, 228, 228, 0.05)", + "subtle": "rgba(228, 228, 228, 0.02)", + "tertiary": { + "default": "rgba(51, 51, 51, 1)", + "default-a": "rgba(228, 228, 228, 0.1)", + "hover": "rgba(81, 81, 81, 1)", + "hover-a": "rgba(228, 228, 228, 0.255)", + "press": "rgba(112, 112, 112, 1)", + "pressA": "rgba(228, 228, 228, 0.41)", + }, + }, + "icon": { + "default": "rgba(228, 228, 228, 1)", + "disable": "rgba(130, 130, 130, 1)", + "hover": "rgba(202, 202, 202, 1)", + "negative": { + "default": "rgba(252, 147, 134, 1)", + "hover": "rgba(249, 186, 177, 1)", + "press": "rgba(254, 219, 214, 1)", + }, + "notice": { + "default": "rgba(222, 167, 29, 1)", + "hover": "rgba(231, 199, 80, 1)", + "press": "rgba(252, 227, 145, 1)", + }, + "on-negative": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "on-neutral": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "on-notice": { + "default": "rgba(41, 41, 41, 1)", + "hover": "rgba(41, 41, 41, 1)", + "press": "rgba(41, 41, 41, 1)", + }, + "on-on-img": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "on-positive": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "on-primary": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "positive": { + "default": "rgba(120, 194, 113, 1)", + "hover": "rgba(161, 215, 155, 1)", + "press": "rgba(191, 241, 186, 1)", + }, + "press": "rgba(188, 188, 188, 1)", + "secondary": { + "default": "rgba(175, 175, 175, 1)", + "hover": "rgba(188, 188, 188, 1)", + "press": "rgba(202, 202, 202, 1)", + }, + "tertiary": { + "default": "rgba(130, 130, 130, 1)", + "hover": "rgba(175, 175, 175, 1)", + "press": "rgba(188, 188, 188, 1)", + }, + }, + "text": { + "brand-premium": { + "default": "rgba(253, 158, 22, 1)", + "hover": "rgba(243, 152, 21, 1)", + "press": "rgba(213, 133, 18, 1)", + }, + "default": "rgba(228, 228, 228, 1)", + "disable": "rgba(130, 130, 130, 1)", + "hover": "rgba(202, 202, 202, 1)", + "info": { + "default": "rgba(114, 181, 245, 1)", + "hover": "rgba(166, 205, 245, 1)", + "press": "rgba(207, 230, 253, 1)", + }, + "negative": { + "default": "rgba(252, 147, 134, 1)", + "hover": "rgba(249, 186, 177, 1)", + "press": "rgba(254, 219, 214, 1)", + }, + "notice": { + "default": "rgba(222, 167, 29, 1)", + "hover": "rgba(231, 199, 80, 1)", + "press": "rgba(252, 227, 145, 1)", + }, + "on-discovery": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "on-hud": { + "default": "rgba(31, 31, 31, 1)", + "hover": "rgba(31, 31, 31, 1)", + "press": "rgba(31, 31, 31, 1)", + }, + "on-negative": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "on-notice": { + "default": "rgba(41, 41, 41, 1)", + "hover": "rgba(41, 41, 41, 1)", + "press": "rgba(41, 41, 41, 1)", + }, + "on-on-img": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "on-positive": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "on-primary": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "placeholder": { + "default": "rgba(112, 112, 112, 1)", + "hover": "rgba(112, 112, 112, 1)", + "press": "rgba(112, 112, 112, 1)", + }, + "positive": { + "default": "rgba(120, 194, 113, 1)", + "hover": "rgba(161, 215, 155, 1)", + "press": "rgba(191, 241, 186, 1)", + }, + "press": "rgba(188, 188, 188, 1)", + "secondary": { + "default": "rgba(175, 175, 175, 1)", + "hover": "rgba(188, 188, 188, 1)", + "press": "rgba(202, 202, 202, 1)", + }, + "tertiary": { + "default": "rgba(130, 130, 130, 1)", + "hover": "rgba(175, 175, 175, 1)", + "press": "rgba(188, 188, 188, 1)", + }, + "visited": { + "default": "rgba(191, 160, 246, 1)", + "hover": "rgba(210, 192, 245, 1)", + "press": "rgba(233, 223, 255, 1)", + }, + }, + }, + "paragraph-width": { + "l": "672", + "l-compact": "588", + "l-cozy": "924", + "m": "448", + "m-compact": "392", + "m-cozy": "616", + "s": "320", + "s-compact": "280", + "s-cozy": "588", + }, + "radius": { + "0": "0", + "l": "12", + "m": "8", + "oval": "999999", + "s": "4", + "xl": "16", + "xs": "2", + "xxl": "24", + }, + "space": { + "between-checkboxes": { + "horizontal": "12", + "vertical": "0", + }, + "layout": { + "0": "0", + "10": "4", + "100": "440", + "20": "8", + "25": "12", + "30": "16", + "40": "24", + "50": "40", + "60": "64", + "70": "104", + "80": "168", + "90": "272", + }, + "target": { + "l": "48", + "m": "40", + "s": "32", + "xs": "24", + }, + }, + "text": { + "font-size": { + "body": "16", + "caption": { + "m": "14", + "s": "12", + }, + "heading": { + "l": "28", + "m": "25", + "s": "22", + "xl": "32", + "xs": "20", + "xxl": "36", + "xxs": "18", + "xxxl": "40", + "xxxs": "14", + }, + "paragraph": "16", + }, + "font-weight": { + "bold": "700", + "regular": "400", + }, + "line-height": { + "body": "24", + "caption": { + "m": "20", + "s": "18", + }, + "heading": { + "l": "36", + "m": "32", + "s": "28", + "xl": "40", + "xs": "28", + "xxl": "44", + "xxs": "24", + "xxxl": "52", + "xxxs": "20", + }, + "paragraph": "28", + }, + }, +} +`; + +exports[`createTokenObject test: light theme > should match snapshot 1`] = ` +{ + "border-width": { + "focus": { + "1": "1", + "2": "2", + }, + "l": "2", + "m": "1", + }, + "color": { + "background": { + "default": "rgba(255, 255, 255, 1)", + "secondary": "rgba(243, 243, 243, 1)", + "tertiary": "rgba(232, 232, 232, 1)", + }, + "border": { + "default": "rgba(31, 31, 31, 0.475)", + "disable": "rgba(31, 31, 31, 0.102)", + "focus": { + "1": "rgba(31, 117, 188, 1)", + "2": "rgba(188, 222, 252, 1)", + "legacy": "rgba(0, 150, 250, 0.32)", + }, + "hover": "rgba(31, 31, 31, 0.635)", + "hud": "rgba(255, 255, 255, 1)", + "negative": "rgba(253, 206, 199, 1)", + "press": "rgba(31, 31, 31, 0.775)", + "secondary": "rgba(31, 31, 31, 0.102)", + "selected": "rgba(0, 150, 250, 1)", + }, + "container": { + "default": "rgba(255, 255, 255, 1)", + "default-a": "rgba(31, 31, 31, 0)", + "disable": "rgba(232, 232, 232, 1)", + "discovery": { + "default": "rgba(253, 91, 78, 1)", + "hover": "rgba(206, 54, 46, 1)", + "press": "rgba(147, 33, 28, 1)", + }, + "hover": "rgba(243, 243, 243, 1)", + "hover-a": "rgba(31, 31, 31, 0.055)", + "hud": { + "default": "rgba(56, 56, 56, 1)", + "hover": "rgba(81, 81, 81, 1)", + "press": "rgba(113, 113, 113, 1)", + }, + "negative": { + "default": "rgba(253, 91, 78, 1)", + "hover": "rgba(206, 54, 46, 1)", + "press": "rgba(147, 33, 28, 1)", + }, + "neutral": { + "default": "rgba(148, 148, 148, 1)", + "hover": "rgba(113, 113, 113, 1)", + "press": "rgba(81, 81, 81, 1)", + }, + "notice": { + "default": "rgba(254, 214, 61, 1)", + "hover": "rgba(245, 183, 17, 1)", + "press": "rgba(231, 157, 20, 1)", + }, + "on-img": { + "default": "rgba(31, 31, 31, 0.37)", + "hover": "rgba(31, 31, 31, 0.475)", + "press": "rgba(31, 31, 31, 0.635)", + }, + "positive": { + "default": "rgba(37, 170, 28, 1)", + "hover": "rgba(17, 131, 8, 1)", + "press": "rgba(4, 93, 0, 1)", + }, + "press": "rgba(232, 232, 232, 1)", + "press-a": "rgba(31, 31, 31, 0.102)", + "primary": { + "default": "rgba(0, 150, 250, 1)", + "hover": "rgba(31, 117, 188, 1)", + "press": "rgba(24, 81, 130, 1)", + }, + "secondary": { + "default": "rgba(243, 243, 243, 1)", + "default-a": "rgba(31, 31, 31, 0.055)", + "hover": "rgba(232, 232, 232, 1)", + "hover-a": "rgba(31, 31, 31, 0.102)", + "press": "rgba(217, 217, 217, 1)", + "press-a": "rgba(31, 31, 31, 0.17)", + }, + "skeleton": "rgba(31, 31, 31, 0.055)", + "subtle": "rgba(31, 31, 31, 0.02)", + "tertiary": { + "default": "rgba(232, 232, 232, 1)", + "default-a": "rgba(31, 31, 31, 0.102)", + "hover": "rgba(217, 217, 217, 1)", + "hover-a": "rgba(31, 31, 31, 0.17)", + "press": "rgba(194, 194, 194, 1)", + "pressA": "rgba(31, 31, 31, 0.27)", + }, + }, + "icon": { + "default": "rgba(31, 31, 31, 1)", + "disable": "rgba(194, 194, 194, 1)", + "hover": "rgba(56, 56, 56, 1)", + "negative": { + "default": "rgba(206, 54, 46, 1)", + "hover": "rgba(147, 33, 28, 1)", + "press": "rgba(103, 22, 17, 1)", + }, + "notice": { + "default": "rgba(161, 99, 9, 1)", + "hover": "rgba(110, 72, 5, 1)", + "press": "rgba(74, 51, 7, 1)", + }, + "on-negative": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "on-neutral": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(243, 243, 243, 1)", + "press": "rgba(232, 232, 232, 1)", + }, + "on-notice": { + "default": "rgba(31, 31, 31, 1)", + "hover": "rgba(31, 31, 31, 1)", + "press": "rgba(31, 31, 31, 1)", + }, + "on-on-img": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "on-positive": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "on-primary": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "positive": { + "default": "rgba(17, 131, 8, 1)", + "hover": "rgba(4, 93, 0, 1)", + "press": "rgba(4, 93, 0, 1)", + }, + "press": "rgba(81, 81, 81, 1)", + "secondary": { + "default": "rgba(81, 81, 81, 1)", + "hover": "rgba(56, 56, 56, 1)", + "press": "rgba(31, 31, 31, 1)", + }, + "tertiary": { + "default": "rgba(113, 113, 113, 1)", + "hover": "rgba(81, 81, 81, 1)", + "press": "rgba(56, 56, 56, 1)", + }, + }, + "text": { + "brand-premium": { + "default": "rgba(253, 158, 22, 1)", + "hover": "rgba(243, 152, 21, 1)", + "press": "rgba(213, 133, 18, 1)", + }, + "default": "rgba(31, 31, 31, 1)", + "disable": "rgba(194, 194, 194, 1)", + "hover": "rgba(56, 56, 56, 1)", + "info": { + "default": "rgba(31, 117, 188, 1)", + "hover": "rgba(24, 81, 130, 1)", + "press": "rgba(19, 58, 93, 1)", + }, + "negative": { + "default": "rgba(206, 54, 46, 1)", + "hover": "rgba(147, 33, 28, 1)", + "press": "rgba(103, 22, 17, 1)", + }, + "notice": { + "default": "rgba(161, 99, 9, 1)", + "hover": "rgba(110, 72, 5, 1)", + "press": "rgba(74, 51, 7, 1)", + }, + "on-discovery": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "on-hud": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "on-negative": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "on-notice": { + "default": "rgba(31, 31, 31, 1)", + "hover": "rgba(31, 31, 31, 1)", + "press": "rgba(31, 31, 31, 1)", + }, + "on-on-img": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "on-positive": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "on-primary": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "placeholder": { + "default": "rgba(148, 148, 148, 1)", + "hover": "rgba(148, 148, 148, 1)", + "press": "rgba(148, 148, 148, 1)", + }, + "positive": { + "default": "rgba(17, 131, 8, 1)", + "hover": "rgba(4, 93, 0, 1)", + "press": "rgba(7, 64, 4, 1)", + }, + "press": "rgba(81, 81, 81, 1)", + "secondary": { + "default": "rgba(81, 81, 81, 1)", + "hover": "rgba(56, 56, 56, 1)", + "press": "rgba(31, 31, 31, 1)", + }, + "tertiary": { + "default": "rgba(113, 113, 113, 1)", + "hover": "rgba(81, 81, 81, 1)", + "press": "rgba(56, 56, 56, 1)", + }, + "visited": { + "default": "rgba(103, 39, 171, 1)", + "hover": "rgba(70, 32, 115, 1)", + "press": "rgba(40, 16, 70, 1)", + }, + }, + }, + "paragraph-width": { + "l": "672", + "l-compact": "588", + "l-cozy": "924", + "m": "448", + "m-compact": "392", + "m-cozy": "616", + "s": "320", + "s-compact": "280", + "s-cozy": "588", + }, + "radius": { + "0": "0", + "l": "12", + "m": "8", + "oval": "999999", + "s": "4", + "xl": "16", + "xs": "2", + "xxl": "24", + }, + "space": { + "between-checkboxes": { + "horizontal": "12", + "vertical": "0", + }, + "layout": { + "0": "0", + "10": "4", + "100": "440", + "20": "8", + "25": "12", + "30": "16", + "40": "24", + "50": "40", + "60": "64", + "70": "104", + "80": "168", + "90": "272", + }, + "target": { + "l": "48", + "m": "40", + "s": "32", + "xs": "24", + }, + }, + "text": { + "font-size": { + "body": "16", + "caption": { + "m": "14", + "s": "12", + }, + "heading": { + "l": "28", + "m": "25", + "s": "22", + "xl": "32", + "xs": "20", + "xxl": "36", + "xxs": "18", + "xxxl": "40", + "xxxs": "14", + }, + "paragraph": "16", + }, + "font-weight": { + "bold": "700", + "regular": "400", + }, + "line-height": { + "body": "24", + "caption": { + "m": "20", + "s": "18", + }, + "heading": { + "l": "36", + "m": "32", + "s": "28", + "xl": "40", + "xs": "28", + "xxl": "44", + "xxs": "24", + "xxxl": "52", + "xxxs": "20", + }, + "paragraph": "28", + }, + }, +} +`; diff --git a/packages/theme/src/themes/token-object.test.ts b/packages/theme/src/themes/token-object.test.ts new file mode 100644 index 000000000..e9752f8c6 --- /dev/null +++ b/packages/theme/src/themes/token-object.test.ts @@ -0,0 +1,143 @@ +import { + createTemplateResolver, + createTokenObject, + mappedTokenObject, + pathToObject, +} from './token-object' +import lightToken from '../json/pixiv-light.json' +import darkToken from '../json/pixiv-dark.json' +import baseToken from '../json/base.json' +import deepmerge from 'deepmerge' +import { Tokens } from './types' + + + +describe.each([ + ['light theme', lightToken], + ['dark theme', darkToken], +] as const)('createTokenObject test: %s', (description, token) => { + const theme = createTokenObject(token, baseToken) + const keys = Object.keys(token) + // 結局実装で使ってるコードで検証してるので意味ないかも + // とはいえほぼこの実装になると思うどうしよう + const templateResolver = createTemplateResolver(token, baseToken) + + // スナップショット + it('should match snapshot', () => { + expect(theme).toMatchSnapshot() + }) + + describe.each(keys)(`[${description}] Category: %s`, (category) => { + const _category = category as keyof typeof token + const tokens = token[_category] + + it.each(Object.keys(tokens))( + `[${description}] ${category} should have %s`, + (key) => { + const splitted = key.split('/') + const tokenValue: { value: string } = tokens[key as keyof typeof tokens] + + expect(theme).toHaveProperty([_category, ...splitted], templateResolver(tokenValue.value)) + } + ) + }) +}) + +describe('pathToObject', () => { + it('should create a nested object from a single key path', () => { + const path: ['a'] = ['a'] + const value = 'value' + const result = pathToObject(path, value) + expect(result).toEqual({ a: value }) + }) + + it('should create a nested object from a multi-level key path', () => { + const path: ['a', 'b', 'c'] = ['a', 'b', 'c'] + const value = 'value' + const result = pathToObject(path, value) + expect(result).toEqual({ a: { b: { c: value } } }) + }) + + it('should handle nested paths correctly', () => { + const path: ['theme', 'color', 'background', 'default'] = [ + 'theme', + 'color', + 'background', + 'default', + ] + const value = '#ffffff' + const result = pathToObject(path, value) + expect(result).toEqual({ + theme: { color: { background: { default: value } } }, + }) + }) + + it('should throw an error if an empty path is provided', () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + expect(() => pathToObject([], 'value')).toThrowError( + 'Path must be a non-empty array' + ) + }) +}) + +describe('mappedTokenObject', () => { + const tokens: Tokens = { + 'color/background/default': { value: '#ffffff' }, + 'color/background/secondary': { value: '#f2f2f2' }, + 'space/target/xs': { value: '4px' }, + 'space/target/md': { value: '8px' }, + } + + it('should create a nested object from token paths', () => { + const result = mappedTokenObject(tokens) + + expect(result).toEqual({ + color: { + background: { + default: tokens['color/background/default'].value, + secondary: tokens['color/background/secondary'].value, + }, + }, + space: { + target: { + xs: tokens['space/target/xs'].value, + md: tokens['space/target/md'].value, + }, + }, + }) + }) + + it('should handle merging of token paths with deepmerge', () => { + const additionalTokens: Tokens = { + 'color/background/default': { value: '#000000' }, + 'space/target/lg': { value: '16px' }, + } + + const mergedResult = deepmerge( + mappedTokenObject(tokens), + mappedTokenObject(additionalTokens) + ) + + expect(mergedResult).toEqual({ + color: { + background: { + default: additionalTokens['color/background/default'].value, + secondary: '#f2f2f2', + }, + }, + space: { + target: { + xs: '4px', + md: '8px', + lg: additionalTokens['space/target/lg'].value, + }, + }, + }) + }) + + it('should return an empty object for empty tokens', () => { + const result = mappedTokenObject({}) + expect(result).toEqual({}) + }) +}) diff --git a/packages/theme/src/themes/token-object.ts b/packages/theme/src/themes/token-object.ts new file mode 100644 index 000000000..fa8a7a13c --- /dev/null +++ b/packages/theme/src/themes/token-object.ts @@ -0,0 +1,84 @@ +import deepmerge from 'deepmerge' +import { NestedObject, Tokens, MappedTokenObject, TokenMap } from './types' + +const isNonEmptyArray = (arr: T[]): arr is [T, ...T[]] => arr.length > 0 + +export const pathToObject =

( + path: P, + value: T +): NestedObject => { + if (!isNonEmptyArray(path)) throw new Error('Path must be a non-empty array') + + const [key, ...rest] = path + if (!isNonEmptyArray(rest)) return { [key]: value } as NestedObject + + return { + [key]: pathToObject(rest, value), + } as NestedObject +} + +export const mappedTokenObject = ( + tokens: T +): MappedTokenObject => { + let result = {} + for (const key in tokens) { + const { value } = tokens[key] + const splitted = key.split('/') + if (!isNonEmptyArray(splitted)) continue + + const v = pathToObject(splitted, value) + result = deepmerge(result, v) + } + + return result as MappedTokenObject +} + +type Template = `{${string}}` +const isTemplate = (value: string): value is Template => + value.startsWith('{') && value.endsWith('}') + +const parseTemplate = (value: Template): [string, string] => { + const [category, key] = value.slice(1, -1).split('.') + + return [category, key] +} + +export const createTemplateResolver = ( + tokenMap: T, + baseToken: TokenMap +) => { + const resolver = (value: string): string => { + if (!isTemplate(value)) return value + + const [category, tokenKey] = parseTemplate(value) + const baseTokens = baseToken[category] as Tokens | undefined + + return resolver( + (baseTokens?.[tokenKey] ?? tokenMap[category][tokenKey]).value + ) + } + + return resolver +} + +export const createTokenObject = ( + tokenMap: T, + baseToken: TokenMap +): { [K in keyof T]: MappedTokenObject } => { + const result = {} as { [K in keyof T]: MappedTokenObject } + const templateResolver = createTemplateResolver(tokenMap, baseToken) + + for (const key in tokenMap) { + const value = tokenMap[key] + const transformed = Object.fromEntries( + Object.entries(value).map(([key, value]) => [ + key, + { value: templateResolver(value.value) }, + ]) + ) as typeof value + + result[key] = mappedTokenObject(transformed) + } + + return result +} diff --git a/packages/theme/src/themes/types.ts b/packages/theme/src/themes/types.ts new file mode 100644 index 000000000..b06eae54c --- /dev/null +++ b/packages/theme/src/themes/types.ts @@ -0,0 +1,35 @@ +type UnionToIntersection = + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (U extends any ? (k: U) => void : never) extends (k: infer I) => void + ? I + : never + +type TokenValue = string +export type Tokens = Record +export type TokenMap = Record +type ToTokenObject = S extends `${infer F}/${infer R}` + ? { + [K in F]: ToTokenObject + } + : { + [K in S]: V + } + +export type MappedTokenObject = UnionToIntersection< + { + [K in keyof T]: ToTokenObject + }[keyof T] +> + +export type NestedObject

= P extends [ + infer Head, + ...infer Tail +] + ? Head extends string + ? Tail extends string[] + ? { + [K in Head]: NestedObject + } + : { [K in Head]: T } + : T + : T diff --git a/packages/theme/tsconfig.json b/packages/theme/tsconfig.json index ce897777d..5fc5adff9 100644 --- a/packages/theme/tsconfig.json +++ b/packages/theme/tsconfig.json @@ -1,4 +1,7 @@ { "extends": "../../tsconfig.base.json", + "compilerOptions": { + "resolveJsonModule": true + }, "include": ["./src"] } diff --git a/packages/theme/vitest.config.ts b/packages/theme/vitest.config.ts index dad7dcd32..eec0e43b9 100644 --- a/packages/theme/vitest.config.ts +++ b/packages/theme/vitest.config.ts @@ -5,7 +5,6 @@ import * as path from 'node:path' export default defineConfig({ test: { globals: true, - setupFiles: ['../../vitest.setup.ts'], alias: [ { find: /@charcoal-ui\/(.*)/, diff --git a/yarn.lock b/yarn.lock index 84f294485..8ea44bc0c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2225,6 +2225,7 @@ __metadata: "@charcoal-ui/foundation": ^4.0.0-beta.14 "@charcoal-ui/utils": ^4.0.0-beta.14 cpx: ^1.5.0 + deepmerge: ^4.3.1 npm-run-all: ^4.1.5 polished: ^4.1.4 rimraf: ^3.0.2 @@ -13859,6 +13860,13 @@ __metadata: languageName: node linkType: hard +"deepmerge@npm:^4.3.1": + version: 4.3.1 + resolution: "deepmerge@npm:4.3.1" + checksum: 2024c6a980a1b7128084170c4cf56b0fd58a63f2da1660dcfe977415f27b17dbe5888668b59d0b063753f3220719d5e400b7f113609489c90160bb9a5518d052 + languageName: node + linkType: hard + "default-browser-id@npm:3.0.0": version: 3.0.0 resolution: "default-browser-id@npm:3.0.0" From b28a6a38d48574a662cb61602abda26e83c47e59 Mon Sep 17 00:00:00 2001 From: naporin0624 Date: Wed, 16 Oct 2024 07:28:47 +0000 Subject: [PATCH 02/15] chore(theme): add resolvejsonmodule --- packages/theme/tsconfig.build.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/theme/tsconfig.build.json b/packages/theme/tsconfig.build.json index 585b3f945..49408884f 100644 --- a/packages/theme/tsconfig.build.json +++ b/packages/theme/tsconfig.build.json @@ -2,6 +2,7 @@ "extends": "../../tsconfig.build.json", "compilerOptions": { "incremental": true, + "resolveJsonModule": true, "outDir": "./dist", "tsBuildInfoFile": "./.tsbuildinfo" }, From 417573f856fb5140c25fcf6f4a65f4f3c221d8b0 Mon Sep 17 00:00:00 2001 From: naporin0624 Date: Wed, 16 Oct 2024 12:37:15 +0000 Subject: [PATCH 03/15] feat(theme): change camelcase --- packages/theme/package.json | 1 + .../__snapshots__/token-object.test.ts.snap | 140 +++++++++--------- .../theme/src/themes/token-object.test.ts | 8 +- packages/theme/src/themes/token-object.ts | 29 +++- packages/theme/src/themes/types.ts | 11 +- yarn.lock | 8 + 6 files changed, 114 insertions(+), 83 deletions(-) diff --git a/packages/theme/package.json b/packages/theme/package.json index 2e7e5da16..35a03a7d4 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -37,6 +37,7 @@ "dependencies": { "@charcoal-ui/foundation": "^4.0.0-beta.14", "@charcoal-ui/utils": "^4.0.0-beta.14", + "change-case": "^5.4.4", "deepmerge": "^4.3.1", "polished": "^4.1.4" }, diff --git a/packages/theme/src/themes/__snapshots__/token-object.test.ts.snap b/packages/theme/src/themes/__snapshots__/token-object.test.ts.snap index fb26c460f..1d7d54dbb 100644 --- a/packages/theme/src/themes/__snapshots__/token-object.test.ts.snap +++ b/packages/theme/src/themes/__snapshots__/token-object.test.ts.snap @@ -2,7 +2,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` { - "border-width": { + "borderWidth": { "focus": { "1": "1", "2": "2", @@ -33,7 +33,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` }, "container": { "default": "rgba(31, 31, 31, 1)", - "default-a": "rgba(228, 228, 228, 0)", + "defaultA": "rgba(228, 228, 228, 0)", "disable": "rgba(51, 51, 51, 1)", "discovery": { "default": "rgba(197, 60, 51, 1)", @@ -41,7 +41,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` "press": "rgba(233, 114, 102, 1)", }, "hover": "rgba(41, 41, 41, 1)", - "hover-a": "rgba(228, 228, 228, 0.05)", + "hoverA": "rgba(228, 228, 228, 0.05)", "hud": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(202, 202, 202, 1)", @@ -62,7 +62,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` "hover": "rgba(231, 199, 80, 1)", "press": "rgba(252, 227, 145, 1)", }, - "on-img": { + "onImg": { "default": "rgba(31, 31, 31, 0.37)", "hover": "rgba(31, 31, 31, 0.475)", "press": "rgba(31, 31, 31, 0.635)", @@ -73,7 +73,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` "press": "rgba(86, 169, 79, 1)", }, "press": "rgba(51, 51, 51, 1)", - "press-a": "rgba(228, 228, 228, 0.1)", + "pressA": "rgba(228, 228, 228, 0.1)", "primary": { "default": "rgba(8, 114, 190, 1)", "hover": "rgba(55, 136, 208, 1)", @@ -81,19 +81,19 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` }, "secondary": { "default": "rgba(41, 41, 41, 1)", - "default-a": "rgba(228, 228, 228, 0.05)", + "defaultA": "rgba(228, 228, 228, 0.05)", "hover": "rgba(51, 51, 51, 1)", - "hover-a": "rgba(228, 228, 228, 0.1)", + "hoverA": "rgba(228, 228, 228, 0.1)", "press": "rgba(81, 81, 81, 1)", - "press-a": "rgba(228, 228, 228, 0.255)", + "pressA": "rgba(228, 228, 228, 0.255)", }, "skeleton": "rgba(228, 228, 228, 0.05)", "subtle": "rgba(228, 228, 228, 0.02)", "tertiary": { "default": "rgba(51, 51, 51, 1)", - "default-a": "rgba(228, 228, 228, 0.1)", + "defaultA": "rgba(228, 228, 228, 0.1)", "hover": "rgba(81, 81, 81, 1)", - "hover-a": "rgba(228, 228, 228, 0.255)", + "hoverA": "rgba(228, 228, 228, 0.255)", "press": "rgba(112, 112, 112, 1)", "pressA": "rgba(228, 228, 228, 0.41)", }, @@ -112,32 +112,32 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` "hover": "rgba(231, 199, 80, 1)", "press": "rgba(252, 227, 145, 1)", }, - "on-negative": { + "onNegative": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "on-neutral": { + "onNeutral": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "on-notice": { + "onNotice": { "default": "rgba(41, 41, 41, 1)", "hover": "rgba(41, 41, 41, 1)", "press": "rgba(41, 41, 41, 1)", }, - "on-on-img": { + "onOnImg": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "on-positive": { + "onPositive": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "on-primary": { + "onPrimary": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", @@ -160,7 +160,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` }, }, "text": { - "brand-premium": { + "brandPremium": { "default": "rgba(253, 158, 22, 1)", "hover": "rgba(243, 152, 21, 1)", "press": "rgba(213, 133, 18, 1)", @@ -183,37 +183,37 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` "hover": "rgba(231, 199, 80, 1)", "press": "rgba(252, 227, 145, 1)", }, - "on-discovery": { + "onDiscovery": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "on-hud": { + "onHud": { "default": "rgba(31, 31, 31, 1)", "hover": "rgba(31, 31, 31, 1)", "press": "rgba(31, 31, 31, 1)", }, - "on-negative": { + "onNegative": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "on-notice": { + "onNotice": { "default": "rgba(41, 41, 41, 1)", "hover": "rgba(41, 41, 41, 1)", "press": "rgba(41, 41, 41, 1)", }, - "on-on-img": { + "onOnImg": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "on-positive": { + "onPositive": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "on-primary": { + "onPrimary": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", @@ -246,16 +246,16 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` }, }, }, - "paragraph-width": { + "paragraphWidth": { "l": "672", - "l-compact": "588", - "l-cozy": "924", + "lCompact": "588", + "lCozy": "924", "m": "448", - "m-compact": "392", - "m-cozy": "616", + "mCompact": "392", + "mCozy": "616", "s": "320", - "s-compact": "280", - "s-cozy": "588", + "sCompact": "280", + "sCozy": "588", }, "radius": { "0": "0", @@ -268,7 +268,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` "xxl": "24", }, "space": { - "between-checkboxes": { + "betweenCheckboxes": { "horizontal": "12", "vertical": "0", }, @@ -294,7 +294,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` }, }, "text": { - "font-size": { + "fontSize": { "body": "16", "caption": { "m": "14", @@ -313,11 +313,11 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` }, "paragraph": "16", }, - "font-weight": { + "fontWeight": { "bold": "700", "regular": "400", }, - "line-height": { + "lineHeight": { "body": "24", "caption": { "m": "20", @@ -342,7 +342,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` exports[`createTokenObject test: light theme > should match snapshot 1`] = ` { - "border-width": { + "borderWidth": { "focus": { "1": "1", "2": "2", @@ -373,7 +373,7 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` }, "container": { "default": "rgba(255, 255, 255, 1)", - "default-a": "rgba(31, 31, 31, 0)", + "defaultA": "rgba(31, 31, 31, 0)", "disable": "rgba(232, 232, 232, 1)", "discovery": { "default": "rgba(253, 91, 78, 1)", @@ -381,7 +381,7 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` "press": "rgba(147, 33, 28, 1)", }, "hover": "rgba(243, 243, 243, 1)", - "hover-a": "rgba(31, 31, 31, 0.055)", + "hoverA": "rgba(31, 31, 31, 0.055)", "hud": { "default": "rgba(56, 56, 56, 1)", "hover": "rgba(81, 81, 81, 1)", @@ -402,7 +402,7 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` "hover": "rgba(245, 183, 17, 1)", "press": "rgba(231, 157, 20, 1)", }, - "on-img": { + "onImg": { "default": "rgba(31, 31, 31, 0.37)", "hover": "rgba(31, 31, 31, 0.475)", "press": "rgba(31, 31, 31, 0.635)", @@ -413,7 +413,7 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` "press": "rgba(4, 93, 0, 1)", }, "press": "rgba(232, 232, 232, 1)", - "press-a": "rgba(31, 31, 31, 0.102)", + "pressA": "rgba(31, 31, 31, 0.102)", "primary": { "default": "rgba(0, 150, 250, 1)", "hover": "rgba(31, 117, 188, 1)", @@ -421,19 +421,19 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` }, "secondary": { "default": "rgba(243, 243, 243, 1)", - "default-a": "rgba(31, 31, 31, 0.055)", + "defaultA": "rgba(31, 31, 31, 0.055)", "hover": "rgba(232, 232, 232, 1)", - "hover-a": "rgba(31, 31, 31, 0.102)", + "hoverA": "rgba(31, 31, 31, 0.102)", "press": "rgba(217, 217, 217, 1)", - "press-a": "rgba(31, 31, 31, 0.17)", + "pressA": "rgba(31, 31, 31, 0.17)", }, "skeleton": "rgba(31, 31, 31, 0.055)", "subtle": "rgba(31, 31, 31, 0.02)", "tertiary": { "default": "rgba(232, 232, 232, 1)", - "default-a": "rgba(31, 31, 31, 0.102)", + "defaultA": "rgba(31, 31, 31, 0.102)", "hover": "rgba(217, 217, 217, 1)", - "hover-a": "rgba(31, 31, 31, 0.17)", + "hoverA": "rgba(31, 31, 31, 0.17)", "press": "rgba(194, 194, 194, 1)", "pressA": "rgba(31, 31, 31, 0.27)", }, @@ -452,32 +452,32 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` "hover": "rgba(110, 72, 5, 1)", "press": "rgba(74, 51, 7, 1)", }, - "on-negative": { + "onNegative": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", }, - "on-neutral": { + "onNeutral": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(243, 243, 243, 1)", "press": "rgba(232, 232, 232, 1)", }, - "on-notice": { + "onNotice": { "default": "rgba(31, 31, 31, 1)", "hover": "rgba(31, 31, 31, 1)", "press": "rgba(31, 31, 31, 1)", }, - "on-on-img": { + "onOnImg": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", }, - "on-positive": { + "onPositive": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", }, - "on-primary": { + "onPrimary": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", @@ -500,7 +500,7 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` }, }, "text": { - "brand-premium": { + "brandPremium": { "default": "rgba(253, 158, 22, 1)", "hover": "rgba(243, 152, 21, 1)", "press": "rgba(213, 133, 18, 1)", @@ -523,37 +523,37 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` "hover": "rgba(110, 72, 5, 1)", "press": "rgba(74, 51, 7, 1)", }, - "on-discovery": { + "onDiscovery": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", }, - "on-hud": { + "onHud": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "on-negative": { + "onNegative": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", }, - "on-notice": { + "onNotice": { "default": "rgba(31, 31, 31, 1)", "hover": "rgba(31, 31, 31, 1)", "press": "rgba(31, 31, 31, 1)", }, - "on-on-img": { + "onOnImg": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", }, - "on-positive": { + "onPositive": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", }, - "on-primary": { + "onPrimary": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", @@ -586,16 +586,16 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` }, }, }, - "paragraph-width": { + "paragraphWidth": { "l": "672", - "l-compact": "588", - "l-cozy": "924", + "lCompact": "588", + "lCozy": "924", "m": "448", - "m-compact": "392", - "m-cozy": "616", + "mCompact": "392", + "mCozy": "616", "s": "320", - "s-compact": "280", - "s-cozy": "588", + "sCompact": "280", + "sCozy": "588", }, "radius": { "0": "0", @@ -608,7 +608,7 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` "xxl": "24", }, "space": { - "between-checkboxes": { + "betweenCheckboxes": { "horizontal": "12", "vertical": "0", }, @@ -634,7 +634,7 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` }, }, "text": { - "font-size": { + "fontSize": { "body": "16", "caption": { "m": "14", @@ -653,11 +653,11 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` }, "paragraph": "16", }, - "font-weight": { + "fontWeight": { "bold": "700", "regular": "400", }, - "line-height": { + "lineHeight": { "body": "24", "caption": { "m": "20", diff --git a/packages/theme/src/themes/token-object.test.ts b/packages/theme/src/themes/token-object.test.ts index e9752f8c6..1c7428eb0 100644 --- a/packages/theme/src/themes/token-object.test.ts +++ b/packages/theme/src/themes/token-object.test.ts @@ -9,8 +9,7 @@ import darkToken from '../json/pixiv-dark.json' import baseToken from '../json/base.json' import deepmerge from 'deepmerge' import { Tokens } from './types' - - +import { camelCase } from 'change-case' describe.each([ ['light theme', lightToken], @@ -37,7 +36,10 @@ describe.each([ const splitted = key.split('/') const tokenValue: { value: string } = tokens[key as keyof typeof tokens] - expect(theme).toHaveProperty([_category, ...splitted], templateResolver(tokenValue.value)) + expect(theme).toHaveProperty( + [_category, ...splitted].map((key) => camelCase(key)), + templateResolver(tokenValue.value) + ) } ) }) diff --git a/packages/theme/src/themes/token-object.ts b/packages/theme/src/themes/token-object.ts index fa8a7a13c..fdd7eb6f9 100644 --- a/packages/theme/src/themes/token-object.ts +++ b/packages/theme/src/themes/token-object.ts @@ -1,5 +1,12 @@ import deepmerge from 'deepmerge' -import { NestedObject, Tokens, MappedTokenObject, TokenMap } from './types' +import { + NestedObject, + Tokens, + MappedTokenObject, + TokenMap, + ChainCaseToCamelCase, +} from './types' +import { camelCase } from 'change-case' const isNonEmptyArray = (arr: T[]): arr is [T, ...T[]] => arr.length > 0 @@ -23,7 +30,7 @@ export const mappedTokenObject = ( let result = {} for (const key in tokens) { const { value } = tokens[key] - const splitted = key.split('/') + const splitted = key.split('/').map((key) => camelCase(key)) if (!isNonEmptyArray(splitted)) continue const v = pathToObject(splitted, value) @@ -64,12 +71,16 @@ export const createTemplateResolver = ( export const createTokenObject = ( tokenMap: T, baseToken: TokenMap -): { [K in keyof T]: MappedTokenObject } => { - const result = {} as { [K in keyof T]: MappedTokenObject } +): { + [K in keyof T as ChainCaseToCamelCase]: MappedTokenObject +} => { + const result = {} as Record const templateResolver = createTemplateResolver(tokenMap, baseToken) - for (const key in tokenMap) { - const value = tokenMap[key] + for (const category in tokenMap) { + const value = tokenMap[category] + + // category ごとに template を展開していく const transformed = Object.fromEntries( Object.entries(value).map(([key, value]) => [ key, @@ -77,8 +88,10 @@ export const createTokenObject = ( ]) ) as typeof value - result[key] = mappedTokenObject(transformed) + result[camelCase(category)] = mappedTokenObject(transformed) } - return result + return result as { + [K in keyof T as ChainCaseToCamelCase]: MappedTokenObject + } } diff --git a/packages/theme/src/themes/types.ts b/packages/theme/src/themes/types.ts index b06eae54c..990f62bf3 100644 --- a/packages/theme/src/themes/types.ts +++ b/packages/theme/src/themes/types.ts @@ -7,12 +7,19 @@ type UnionToIntersection = type TokenValue = string export type Tokens = Record export type TokenMap = Record + +export type ChainCaseToCamelCase< + S extends string, + D extends string = '-' +> = S extends `${infer F}${D}${infer R}` + ? `${F}${Capitalize>}` + : S type ToTokenObject = S extends `${infer F}/${infer R}` ? { - [K in F]: ToTokenObject + [K in F as ChainCaseToCamelCase]: ToTokenObject } : { - [K in S]: V + [K in S as ChainCaseToCamelCase]: V } export type MappedTokenObject = UnionToIntersection< diff --git a/yarn.lock b/yarn.lock index 8ea44bc0c..bdee63809 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2224,6 +2224,7 @@ __metadata: dependencies: "@charcoal-ui/foundation": ^4.0.0-beta.14 "@charcoal-ui/utils": ^4.0.0-beta.14 + change-case: ^5.4.4 cpx: ^1.5.0 deepmerge: ^4.3.1 npm-run-all: ^4.1.5 @@ -12293,6 +12294,13 @@ __metadata: languageName: node linkType: hard +"change-case@npm:^5.4.4": + version: 5.4.4 + resolution: "change-case@npm:5.4.4" + checksum: a22a25a763719658424ffbcd41e931d2d19cc22399cc765dca447fbe1eaf13e179d5e8ab1677af75f2e814dbddf74e42ffdecb526cd5bc906cc859f62aa154b2 + languageName: node + linkType: hard + "char-regex@npm:^1.0.2": version: 1.0.2 resolution: "char-regex@npm:1.0.2" From 6ba78642ba3d8dbc9e22e2e7696ce56a39e60749 Mon Sep 17 00:00:00 2001 From: naporin0624 Date: Wed, 16 Oct 2024 16:52:49 +0000 Subject: [PATCH 04/15] chore(theme): remove directory --- .../__snapshots__/token-object.test.ts.snap | 140 +++++++++--------- .../token-object.test.ts | 3 +- .../{themes => token-object}/token-object.ts | 18 +-- .../src/{themes => token-object}/types.ts | 24 +-- 4 files changed, 89 insertions(+), 96 deletions(-) rename packages/theme/src/{themes => token-object}/__snapshots__/token-object.test.ts.snap (89%) rename packages/theme/src/{themes => token-object}/token-object.test.ts (97%) rename packages/theme/src/{themes => token-object}/token-object.ts (82%) rename packages/theme/src/{themes => token-object}/types.ts (75%) diff --git a/packages/theme/src/themes/__snapshots__/token-object.test.ts.snap b/packages/theme/src/token-object/__snapshots__/token-object.test.ts.snap similarity index 89% rename from packages/theme/src/themes/__snapshots__/token-object.test.ts.snap rename to packages/theme/src/token-object/__snapshots__/token-object.test.ts.snap index 1d7d54dbb..fb26c460f 100644 --- a/packages/theme/src/themes/__snapshots__/token-object.test.ts.snap +++ b/packages/theme/src/token-object/__snapshots__/token-object.test.ts.snap @@ -2,7 +2,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` { - "borderWidth": { + "border-width": { "focus": { "1": "1", "2": "2", @@ -33,7 +33,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` }, "container": { "default": "rgba(31, 31, 31, 1)", - "defaultA": "rgba(228, 228, 228, 0)", + "default-a": "rgba(228, 228, 228, 0)", "disable": "rgba(51, 51, 51, 1)", "discovery": { "default": "rgba(197, 60, 51, 1)", @@ -41,7 +41,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` "press": "rgba(233, 114, 102, 1)", }, "hover": "rgba(41, 41, 41, 1)", - "hoverA": "rgba(228, 228, 228, 0.05)", + "hover-a": "rgba(228, 228, 228, 0.05)", "hud": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(202, 202, 202, 1)", @@ -62,7 +62,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` "hover": "rgba(231, 199, 80, 1)", "press": "rgba(252, 227, 145, 1)", }, - "onImg": { + "on-img": { "default": "rgba(31, 31, 31, 0.37)", "hover": "rgba(31, 31, 31, 0.475)", "press": "rgba(31, 31, 31, 0.635)", @@ -73,7 +73,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` "press": "rgba(86, 169, 79, 1)", }, "press": "rgba(51, 51, 51, 1)", - "pressA": "rgba(228, 228, 228, 0.1)", + "press-a": "rgba(228, 228, 228, 0.1)", "primary": { "default": "rgba(8, 114, 190, 1)", "hover": "rgba(55, 136, 208, 1)", @@ -81,19 +81,19 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` }, "secondary": { "default": "rgba(41, 41, 41, 1)", - "defaultA": "rgba(228, 228, 228, 0.05)", + "default-a": "rgba(228, 228, 228, 0.05)", "hover": "rgba(51, 51, 51, 1)", - "hoverA": "rgba(228, 228, 228, 0.1)", + "hover-a": "rgba(228, 228, 228, 0.1)", "press": "rgba(81, 81, 81, 1)", - "pressA": "rgba(228, 228, 228, 0.255)", + "press-a": "rgba(228, 228, 228, 0.255)", }, "skeleton": "rgba(228, 228, 228, 0.05)", "subtle": "rgba(228, 228, 228, 0.02)", "tertiary": { "default": "rgba(51, 51, 51, 1)", - "defaultA": "rgba(228, 228, 228, 0.1)", + "default-a": "rgba(228, 228, 228, 0.1)", "hover": "rgba(81, 81, 81, 1)", - "hoverA": "rgba(228, 228, 228, 0.255)", + "hover-a": "rgba(228, 228, 228, 0.255)", "press": "rgba(112, 112, 112, 1)", "pressA": "rgba(228, 228, 228, 0.41)", }, @@ -112,32 +112,32 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` "hover": "rgba(231, 199, 80, 1)", "press": "rgba(252, 227, 145, 1)", }, - "onNegative": { + "on-negative": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "onNeutral": { + "on-neutral": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "onNotice": { + "on-notice": { "default": "rgba(41, 41, 41, 1)", "hover": "rgba(41, 41, 41, 1)", "press": "rgba(41, 41, 41, 1)", }, - "onOnImg": { + "on-on-img": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "onPositive": { + "on-positive": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "onPrimary": { + "on-primary": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", @@ -160,7 +160,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` }, }, "text": { - "brandPremium": { + "brand-premium": { "default": "rgba(253, 158, 22, 1)", "hover": "rgba(243, 152, 21, 1)", "press": "rgba(213, 133, 18, 1)", @@ -183,37 +183,37 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` "hover": "rgba(231, 199, 80, 1)", "press": "rgba(252, 227, 145, 1)", }, - "onDiscovery": { + "on-discovery": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "onHud": { + "on-hud": { "default": "rgba(31, 31, 31, 1)", "hover": "rgba(31, 31, 31, 1)", "press": "rgba(31, 31, 31, 1)", }, - "onNegative": { + "on-negative": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "onNotice": { + "on-notice": { "default": "rgba(41, 41, 41, 1)", "hover": "rgba(41, 41, 41, 1)", "press": "rgba(41, 41, 41, 1)", }, - "onOnImg": { + "on-on-img": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "onPositive": { + "on-positive": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "onPrimary": { + "on-primary": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", @@ -246,16 +246,16 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` }, }, }, - "paragraphWidth": { + "paragraph-width": { "l": "672", - "lCompact": "588", - "lCozy": "924", + "l-compact": "588", + "l-cozy": "924", "m": "448", - "mCompact": "392", - "mCozy": "616", + "m-compact": "392", + "m-cozy": "616", "s": "320", - "sCompact": "280", - "sCozy": "588", + "s-compact": "280", + "s-cozy": "588", }, "radius": { "0": "0", @@ -268,7 +268,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` "xxl": "24", }, "space": { - "betweenCheckboxes": { + "between-checkboxes": { "horizontal": "12", "vertical": "0", }, @@ -294,7 +294,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` }, }, "text": { - "fontSize": { + "font-size": { "body": "16", "caption": { "m": "14", @@ -313,11 +313,11 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` }, "paragraph": "16", }, - "fontWeight": { + "font-weight": { "bold": "700", "regular": "400", }, - "lineHeight": { + "line-height": { "body": "24", "caption": { "m": "20", @@ -342,7 +342,7 @@ exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` exports[`createTokenObject test: light theme > should match snapshot 1`] = ` { - "borderWidth": { + "border-width": { "focus": { "1": "1", "2": "2", @@ -373,7 +373,7 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` }, "container": { "default": "rgba(255, 255, 255, 1)", - "defaultA": "rgba(31, 31, 31, 0)", + "default-a": "rgba(31, 31, 31, 0)", "disable": "rgba(232, 232, 232, 1)", "discovery": { "default": "rgba(253, 91, 78, 1)", @@ -381,7 +381,7 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` "press": "rgba(147, 33, 28, 1)", }, "hover": "rgba(243, 243, 243, 1)", - "hoverA": "rgba(31, 31, 31, 0.055)", + "hover-a": "rgba(31, 31, 31, 0.055)", "hud": { "default": "rgba(56, 56, 56, 1)", "hover": "rgba(81, 81, 81, 1)", @@ -402,7 +402,7 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` "hover": "rgba(245, 183, 17, 1)", "press": "rgba(231, 157, 20, 1)", }, - "onImg": { + "on-img": { "default": "rgba(31, 31, 31, 0.37)", "hover": "rgba(31, 31, 31, 0.475)", "press": "rgba(31, 31, 31, 0.635)", @@ -413,7 +413,7 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` "press": "rgba(4, 93, 0, 1)", }, "press": "rgba(232, 232, 232, 1)", - "pressA": "rgba(31, 31, 31, 0.102)", + "press-a": "rgba(31, 31, 31, 0.102)", "primary": { "default": "rgba(0, 150, 250, 1)", "hover": "rgba(31, 117, 188, 1)", @@ -421,19 +421,19 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` }, "secondary": { "default": "rgba(243, 243, 243, 1)", - "defaultA": "rgba(31, 31, 31, 0.055)", + "default-a": "rgba(31, 31, 31, 0.055)", "hover": "rgba(232, 232, 232, 1)", - "hoverA": "rgba(31, 31, 31, 0.102)", + "hover-a": "rgba(31, 31, 31, 0.102)", "press": "rgba(217, 217, 217, 1)", - "pressA": "rgba(31, 31, 31, 0.17)", + "press-a": "rgba(31, 31, 31, 0.17)", }, "skeleton": "rgba(31, 31, 31, 0.055)", "subtle": "rgba(31, 31, 31, 0.02)", "tertiary": { "default": "rgba(232, 232, 232, 1)", - "defaultA": "rgba(31, 31, 31, 0.102)", + "default-a": "rgba(31, 31, 31, 0.102)", "hover": "rgba(217, 217, 217, 1)", - "hoverA": "rgba(31, 31, 31, 0.17)", + "hover-a": "rgba(31, 31, 31, 0.17)", "press": "rgba(194, 194, 194, 1)", "pressA": "rgba(31, 31, 31, 0.27)", }, @@ -452,32 +452,32 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` "hover": "rgba(110, 72, 5, 1)", "press": "rgba(74, 51, 7, 1)", }, - "onNegative": { + "on-negative": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", }, - "onNeutral": { + "on-neutral": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(243, 243, 243, 1)", "press": "rgba(232, 232, 232, 1)", }, - "onNotice": { + "on-notice": { "default": "rgba(31, 31, 31, 1)", "hover": "rgba(31, 31, 31, 1)", "press": "rgba(31, 31, 31, 1)", }, - "onOnImg": { + "on-on-img": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", }, - "onPositive": { + "on-positive": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", }, - "onPrimary": { + "on-primary": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", @@ -500,7 +500,7 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` }, }, "text": { - "brandPremium": { + "brand-premium": { "default": "rgba(253, 158, 22, 1)", "hover": "rgba(243, 152, 21, 1)", "press": "rgba(213, 133, 18, 1)", @@ -523,37 +523,37 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` "hover": "rgba(110, 72, 5, 1)", "press": "rgba(74, 51, 7, 1)", }, - "onDiscovery": { + "on-discovery": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", }, - "onHud": { + "on-hud": { "default": "rgba(228, 228, 228, 1)", "hover": "rgba(228, 228, 228, 1)", "press": "rgba(228, 228, 228, 1)", }, - "onNegative": { + "on-negative": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", }, - "onNotice": { + "on-notice": { "default": "rgba(31, 31, 31, 1)", "hover": "rgba(31, 31, 31, 1)", "press": "rgba(31, 31, 31, 1)", }, - "onOnImg": { + "on-on-img": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", }, - "onPositive": { + "on-positive": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", }, - "onPrimary": { + "on-primary": { "default": "rgba(255, 255, 255, 1)", "hover": "rgba(255, 255, 255, 1)", "press": "rgba(255, 255, 255, 1)", @@ -586,16 +586,16 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` }, }, }, - "paragraphWidth": { + "paragraph-width": { "l": "672", - "lCompact": "588", - "lCozy": "924", + "l-compact": "588", + "l-cozy": "924", "m": "448", - "mCompact": "392", - "mCozy": "616", + "m-compact": "392", + "m-cozy": "616", "s": "320", - "sCompact": "280", - "sCozy": "588", + "s-compact": "280", + "s-cozy": "588", }, "radius": { "0": "0", @@ -608,7 +608,7 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` "xxl": "24", }, "space": { - "betweenCheckboxes": { + "between-checkboxes": { "horizontal": "12", "vertical": "0", }, @@ -634,7 +634,7 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` }, }, "text": { - "fontSize": { + "font-size": { "body": "16", "caption": { "m": "14", @@ -653,11 +653,11 @@ exports[`createTokenObject test: light theme > should match snapshot 1`] = ` }, "paragraph": "16", }, - "fontWeight": { + "font-weight": { "bold": "700", "regular": "400", }, - "lineHeight": { + "line-height": { "body": "24", "caption": { "m": "20", diff --git a/packages/theme/src/themes/token-object.test.ts b/packages/theme/src/token-object/token-object.test.ts similarity index 97% rename from packages/theme/src/themes/token-object.test.ts rename to packages/theme/src/token-object/token-object.test.ts index 1c7428eb0..d5fb12938 100644 --- a/packages/theme/src/themes/token-object.test.ts +++ b/packages/theme/src/token-object/token-object.test.ts @@ -9,7 +9,6 @@ import darkToken from '../json/pixiv-dark.json' import baseToken from '../json/base.json' import deepmerge from 'deepmerge' import { Tokens } from './types' -import { camelCase } from 'change-case' describe.each([ ['light theme', lightToken], @@ -37,7 +36,7 @@ describe.each([ const tokenValue: { value: string } = tokens[key as keyof typeof tokens] expect(theme).toHaveProperty( - [_category, ...splitted].map((key) => camelCase(key)), + [_category, ...splitted], templateResolver(tokenValue.value) ) } diff --git a/packages/theme/src/themes/token-object.ts b/packages/theme/src/token-object/token-object.ts similarity index 82% rename from packages/theme/src/themes/token-object.ts rename to packages/theme/src/token-object/token-object.ts index fdd7eb6f9..ca5259b08 100644 --- a/packages/theme/src/themes/token-object.ts +++ b/packages/theme/src/token-object/token-object.ts @@ -4,9 +4,7 @@ import { Tokens, MappedTokenObject, TokenMap, - ChainCaseToCamelCase, } from './types' -import { camelCase } from 'change-case' const isNonEmptyArray = (arr: T[]): arr is [T, ...T[]] => arr.length > 0 @@ -30,7 +28,7 @@ export const mappedTokenObject = ( let result = {} for (const key in tokens) { const { value } = tokens[key] - const splitted = key.split('/').map((key) => camelCase(key)) + const splitted = key.split('/') if (!isNonEmptyArray(splitted)) continue const v = pathToObject(splitted, value) @@ -70,11 +68,9 @@ export const createTemplateResolver = ( export const createTokenObject = ( tokenMap: T, - baseToken: TokenMap -): { - [K in keyof T as ChainCaseToCamelCase]: MappedTokenObject -} => { - const result = {} as Record + baseToken: TokenMap, +): { [K in keyof T]: MappedTokenObject } => { + const result = {} as { [K in keyof T]: MappedTokenObject } const templateResolver = createTemplateResolver(tokenMap, baseToken) for (const category in tokenMap) { @@ -88,10 +84,8 @@ export const createTokenObject = ( ]) ) as typeof value - result[camelCase(category)] = mappedTokenObject(transformed) + result[category] = mappedTokenObject(transformed) } - return result as { - [K in keyof T as ChainCaseToCamelCase]: MappedTokenObject - } + return result } diff --git a/packages/theme/src/themes/types.ts b/packages/theme/src/token-object/types.ts similarity index 75% rename from packages/theme/src/themes/types.ts rename to packages/theme/src/token-object/types.ts index 990f62bf3..fabc97a01 100644 --- a/packages/theme/src/themes/types.ts +++ b/packages/theme/src/token-object/types.ts @@ -1,8 +1,8 @@ type UnionToIntersection = // eslint-disable-next-line @typescript-eslint/no-explicit-any (U extends any ? (k: U) => void : never) extends (k: infer I) => void - ? I - : never + ? I + : never type TokenValue = string export type Tokens = Record @@ -16,11 +16,11 @@ export type ChainCaseToCamelCase< : S type ToTokenObject = S extends `${infer F}/${infer R}` ? { - [K in F as ChainCaseToCamelCase]: ToTokenObject - } + [K in F]: ToTokenObject + } : { - [K in S as ChainCaseToCamelCase]: V - } + [K in S]: V + } export type MappedTokenObject = UnionToIntersection< { @@ -33,10 +33,10 @@ export type NestedObject

= P extends [ ...infer Tail ] ? Head extends string - ? Tail extends string[] - ? { - [K in Head]: NestedObject - } - : { [K in Head]: T } - : T + ? Tail extends string[] + ? { + [K in Head]: NestedObject + } + : { [K in Head]: T } + : T : T From 356f180fb7586aed2b630556bfe8b10fc4cc0da3 Mon Sep 17 00:00:00 2001 From: naporin0624 Date: Wed, 16 Oct 2024 19:22:36 +0000 Subject: [PATCH 05/15] chore(theme): refactor token-object --- .../changecase-keys.test.ts.snap | 681 ++++++++++++++++++ .../helpers/changecase-keys.test-d.ts | 32 + .../helpers/changecase-keys.test.ts | 52 ++ .../token-object/helpers/changecase-keys.ts | 42 ++ .../token-object/helpers/is-empty-array.ts | 2 + .../token-object/helpers/nest-object.test.ts | 39 + .../src/token-object/helpers/nest-object.ts | 28 + packages/theme/src/token-object/index.ts | 46 ++ .../src/token-object/reference-token.test.ts | 60 ++ .../theme/src/token-object/reference-token.ts | 32 + .../src/token-object/token-object.test.ts | 58 +- .../theme/src/token-object/token-object.ts | 91 --- packages/theme/src/token-object/types.ts | 49 +- packages/theme/vitest.config.ts | 3 + 14 files changed, 1039 insertions(+), 176 deletions(-) create mode 100644 packages/theme/src/token-object/helpers/__snapshots__/changecase-keys.test.ts.snap create mode 100644 packages/theme/src/token-object/helpers/changecase-keys.test-d.ts create mode 100644 packages/theme/src/token-object/helpers/changecase-keys.test.ts create mode 100644 packages/theme/src/token-object/helpers/changecase-keys.ts create mode 100644 packages/theme/src/token-object/helpers/is-empty-array.ts create mode 100644 packages/theme/src/token-object/helpers/nest-object.test.ts create mode 100644 packages/theme/src/token-object/helpers/nest-object.ts create mode 100644 packages/theme/src/token-object/index.ts create mode 100644 packages/theme/src/token-object/reference-token.test.ts create mode 100644 packages/theme/src/token-object/reference-token.ts delete mode 100644 packages/theme/src/token-object/token-object.ts diff --git a/packages/theme/src/token-object/helpers/__snapshots__/changecase-keys.test.ts.snap b/packages/theme/src/token-object/helpers/__snapshots__/changecase-keys.test.ts.snap new file mode 100644 index 000000000..0f9cf8453 --- /dev/null +++ b/packages/theme/src/token-object/helpers/__snapshots__/changecase-keys.test.ts.snap @@ -0,0 +1,681 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`changeCaseKeys snapshot test: dark theme > camelcaseKeys snapshot test 1`] = ` +{ + "borderWidth": { + "focus": { + "1": "1", + "2": "2", + }, + "l": "2", + "m": "1", + }, + "color": { + "background": { + "default": "rgba(31, 31, 31, 1)", + "secondary": "rgba(21, 21, 21, 1)", + "tertiary": "rgba(6, 6, 6, 1)", + }, + "border": { + "default": "rgba(228, 228, 228, 0.41)", + "disable": "rgba(228, 228, 228, 0.05)", + "focus": { + "1": "rgba(114, 181, 245, 1)", + "2": "rgba(39, 84, 126, 1)", + "legacy": "rgba(0, 150, 250, 0.32)", + }, + "hover": "rgba(228, 228, 228, 0.505)", + "hud": "rgba(31, 31, 31, 1)", + "negative": "rgba(136, 54, 46, 1)", + "press": "rgba(228, 228, 228, 0.61)", + "secondary": "rgba(228, 228, 228, 0.1)", + "selected": "rgba(8, 114, 190, 1)", + }, + "container": { + "default": "rgba(31, 31, 31, 1)", + "defaultA": "rgba(228, 228, 228, 0)", + "disable": "rgba(51, 51, 51, 1)", + "discovery": { + "default": "rgba(197, 60, 51, 1)", + "hover": "rgba(217, 88, 76, 1)", + "press": "rgba(233, 114, 102, 1)", + }, + "hover": "rgba(41, 41, 41, 1)", + "hoverA": "rgba(228, 228, 228, 0.05)", + "hud": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(202, 202, 202, 1)", + "press": "rgba(188, 188, 188, 1)", + }, + "negative": { + "default": "rgba(197, 60, 51, 1)", + "hover": "rgba(217, 88, 76, 1)", + "press": "rgba(233, 114, 102, 1)", + }, + "neutral": { + "default": "rgba(112, 112, 112, 1)", + "hover": "rgba(130, 130, 130, 1)", + "press": "rgba(151, 151, 151, 1)", + }, + "notice": { + "default": "rgba(222, 185, 7, 1)", + "hover": "rgba(231, 199, 80, 1)", + "press": "rgba(252, 227, 145, 1)", + }, + "onImg": { + "default": "rgba(31, 31, 31, 0.37)", + "hover": "rgba(31, 31, 31, 0.475)", + "press": "rgba(31, 31, 31, 0.635)", + }, + "positive": { + "default": "rgba(13, 129, 5, 1)", + "hover": "rgba(58, 150, 52, 1)", + "press": "rgba(86, 169, 79, 1)", + }, + "press": "rgba(51, 51, 51, 1)", + "pressA": "rgba(228, 228, 228, 0.1)", + "primary": { + "default": "rgba(8, 114, 190, 1)", + "hover": "rgba(55, 136, 208, 1)", + "press": "rgba(83, 156, 224, 1)", + }, + "secondary": { + "default": "rgba(41, 41, 41, 1)", + "defaultA": "rgba(228, 228, 228, 0.05)", + "hover": "rgba(51, 51, 51, 1)", + "hoverA": "rgba(228, 228, 228, 0.1)", + "press": "rgba(81, 81, 81, 1)", + "pressA": "rgba(228, 228, 228, 0.255)", + }, + "skeleton": "rgba(228, 228, 228, 0.05)", + "subtle": "rgba(228, 228, 228, 0.02)", + "tertiary": { + "default": "rgba(51, 51, 51, 1)", + "defaultA": "rgba(228, 228, 228, 0.1)", + "hover": "rgba(81, 81, 81, 1)", + "hoverA": "rgba(228, 228, 228, 0.255)", + "press": "rgba(112, 112, 112, 1)", + "pressA": "rgba(228, 228, 228, 0.41)", + }, + }, + "icon": { + "default": "rgba(228, 228, 228, 1)", + "disable": "rgba(130, 130, 130, 1)", + "hover": "rgba(202, 202, 202, 1)", + "negative": { + "default": "rgba(252, 147, 134, 1)", + "hover": "rgba(249, 186, 177, 1)", + "press": "rgba(254, 219, 214, 1)", + }, + "notice": { + "default": "rgba(222, 167, 29, 1)", + "hover": "rgba(231, 199, 80, 1)", + "press": "rgba(252, 227, 145, 1)", + }, + "onNegative": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "onNeutral": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "onNotice": { + "default": "rgba(41, 41, 41, 1)", + "hover": "rgba(41, 41, 41, 1)", + "press": "rgba(41, 41, 41, 1)", + }, + "onOnImg": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "onPositive": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "onPrimary": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "positive": { + "default": "rgba(120, 194, 113, 1)", + "hover": "rgba(161, 215, 155, 1)", + "press": "rgba(191, 241, 186, 1)", + }, + "press": "rgba(188, 188, 188, 1)", + "secondary": { + "default": "rgba(175, 175, 175, 1)", + "hover": "rgba(188, 188, 188, 1)", + "press": "rgba(202, 202, 202, 1)", + }, + "tertiary": { + "default": "rgba(130, 130, 130, 1)", + "hover": "rgba(175, 175, 175, 1)", + "press": "rgba(188, 188, 188, 1)", + }, + }, + "text": { + "brandPremium": { + "default": "rgba(253, 158, 22, 1)", + "hover": "rgba(243, 152, 21, 1)", + "press": "rgba(213, 133, 18, 1)", + }, + "default": "rgba(228, 228, 228, 1)", + "disable": "rgba(130, 130, 130, 1)", + "hover": "rgba(202, 202, 202, 1)", + "info": { + "default": "rgba(114, 181, 245, 1)", + "hover": "rgba(166, 205, 245, 1)", + "press": "rgba(207, 230, 253, 1)", + }, + "negative": { + "default": "rgba(252, 147, 134, 1)", + "hover": "rgba(249, 186, 177, 1)", + "press": "rgba(254, 219, 214, 1)", + }, + "notice": { + "default": "rgba(222, 167, 29, 1)", + "hover": "rgba(231, 199, 80, 1)", + "press": "rgba(252, 227, 145, 1)", + }, + "onDiscovery": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "onHud": { + "default": "rgba(31, 31, 31, 1)", + "hover": "rgba(31, 31, 31, 1)", + "press": "rgba(31, 31, 31, 1)", + }, + "onNegative": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "onNotice": { + "default": "rgba(41, 41, 41, 1)", + "hover": "rgba(41, 41, 41, 1)", + "press": "rgba(41, 41, 41, 1)", + }, + "onOnImg": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "onPositive": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "onPrimary": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "placeholder": { + "default": "rgba(112, 112, 112, 1)", + "hover": "rgba(112, 112, 112, 1)", + "press": "rgba(112, 112, 112, 1)", + }, + "positive": { + "default": "rgba(120, 194, 113, 1)", + "hover": "rgba(161, 215, 155, 1)", + "press": "rgba(191, 241, 186, 1)", + }, + "press": "rgba(188, 188, 188, 1)", + "secondary": { + "default": "rgba(175, 175, 175, 1)", + "hover": "rgba(188, 188, 188, 1)", + "press": "rgba(202, 202, 202, 1)", + }, + "tertiary": { + "default": "rgba(130, 130, 130, 1)", + "hover": "rgba(175, 175, 175, 1)", + "press": "rgba(188, 188, 188, 1)", + }, + "visited": { + "default": "rgba(191, 160, 246, 1)", + "hover": "rgba(210, 192, 245, 1)", + "press": "rgba(233, 223, 255, 1)", + }, + }, + }, + "paragraphWidth": { + "l": "672", + "lCompact": "588", + "lCozy": "924", + "m": "448", + "mCompact": "392", + "mCozy": "616", + "s": "320", + "sCompact": "280", + "sCozy": "588", + }, + "radius": { + "0": "0", + "l": "12", + "m": "8", + "oval": "999999", + "s": "4", + "xl": "16", + "xs": "2", + "xxl": "24", + }, + "space": { + "betweenCheckboxes": { + "horizontal": "12", + "vertical": "0", + }, + "layout": { + "0": "0", + "10": "4", + "100": "440", + "20": "8", + "25": "12", + "30": "16", + "40": "24", + "50": "40", + "60": "64", + "70": "104", + "80": "168", + "90": "272", + }, + "target": { + "l": "48", + "m": "40", + "s": "32", + "xs": "24", + }, + }, + "text": { + "fontSize": { + "body": "16", + "caption": { + "m": "14", + "s": "12", + }, + "heading": { + "l": "28", + "m": "25", + "s": "22", + "xl": "32", + "xs": "20", + "xxl": "36", + "xxs": "18", + "xxxl": "40", + "xxxs": "14", + }, + "paragraph": "16", + }, + "fontWeight": { + "bold": "700", + "regular": "400", + }, + "lineHeight": { + "body": "24", + "caption": { + "m": "20", + "s": "18", + }, + "heading": { + "l": "36", + "m": "32", + "s": "28", + "xl": "40", + "xs": "28", + "xxl": "44", + "xxs": "24", + "xxxl": "52", + "xxxs": "20", + }, + "paragraph": "28", + }, + }, +} +`; + +exports[`changeCaseKeys snapshot test: light theme > camelcaseKeys snapshot test 1`] = ` +{ + "borderWidth": { + "focus": { + "1": "1", + "2": "2", + }, + "l": "2", + "m": "1", + }, + "color": { + "background": { + "default": "rgba(255, 255, 255, 1)", + "secondary": "rgba(243, 243, 243, 1)", + "tertiary": "rgba(232, 232, 232, 1)", + }, + "border": { + "default": "rgba(31, 31, 31, 0.475)", + "disable": "rgba(31, 31, 31, 0.102)", + "focus": { + "1": "rgba(31, 117, 188, 1)", + "2": "rgba(188, 222, 252, 1)", + "legacy": "rgba(0, 150, 250, 0.32)", + }, + "hover": "rgba(31, 31, 31, 0.635)", + "hud": "rgba(255, 255, 255, 1)", + "negative": "rgba(253, 206, 199, 1)", + "press": "rgba(31, 31, 31, 0.775)", + "secondary": "rgba(31, 31, 31, 0.102)", + "selected": "rgba(0, 150, 250, 1)", + }, + "container": { + "default": "rgba(255, 255, 255, 1)", + "defaultA": "rgba(31, 31, 31, 0)", + "disable": "rgba(232, 232, 232, 1)", + "discovery": { + "default": "rgba(253, 91, 78, 1)", + "hover": "rgba(206, 54, 46, 1)", + "press": "rgba(147, 33, 28, 1)", + }, + "hover": "rgba(243, 243, 243, 1)", + "hoverA": "rgba(31, 31, 31, 0.055)", + "hud": { + "default": "rgba(56, 56, 56, 1)", + "hover": "rgba(81, 81, 81, 1)", + "press": "rgba(113, 113, 113, 1)", + }, + "negative": { + "default": "rgba(253, 91, 78, 1)", + "hover": "rgba(206, 54, 46, 1)", + "press": "rgba(147, 33, 28, 1)", + }, + "neutral": { + "default": "rgba(148, 148, 148, 1)", + "hover": "rgba(113, 113, 113, 1)", + "press": "rgba(81, 81, 81, 1)", + }, + "notice": { + "default": "rgba(254, 214, 61, 1)", + "hover": "rgba(245, 183, 17, 1)", + "press": "rgba(231, 157, 20, 1)", + }, + "onImg": { + "default": "rgba(31, 31, 31, 0.37)", + "hover": "rgba(31, 31, 31, 0.475)", + "press": "rgba(31, 31, 31, 0.635)", + }, + "positive": { + "default": "rgba(37, 170, 28, 1)", + "hover": "rgba(17, 131, 8, 1)", + "press": "rgba(4, 93, 0, 1)", + }, + "press": "rgba(232, 232, 232, 1)", + "pressA": "rgba(31, 31, 31, 0.102)", + "primary": { + "default": "rgba(0, 150, 250, 1)", + "hover": "rgba(31, 117, 188, 1)", + "press": "rgba(24, 81, 130, 1)", + }, + "secondary": { + "default": "rgba(243, 243, 243, 1)", + "defaultA": "rgba(31, 31, 31, 0.055)", + "hover": "rgba(232, 232, 232, 1)", + "hoverA": "rgba(31, 31, 31, 0.102)", + "press": "rgba(217, 217, 217, 1)", + "pressA": "rgba(31, 31, 31, 0.17)", + }, + "skeleton": "rgba(31, 31, 31, 0.055)", + "subtle": "rgba(31, 31, 31, 0.02)", + "tertiary": { + "default": "rgba(232, 232, 232, 1)", + "defaultA": "rgba(31, 31, 31, 0.102)", + "hover": "rgba(217, 217, 217, 1)", + "hoverA": "rgba(31, 31, 31, 0.17)", + "press": "rgba(194, 194, 194, 1)", + "pressA": "rgba(31, 31, 31, 0.27)", + }, + }, + "icon": { + "default": "rgba(31, 31, 31, 1)", + "disable": "rgba(194, 194, 194, 1)", + "hover": "rgba(56, 56, 56, 1)", + "negative": { + "default": "rgba(206, 54, 46, 1)", + "hover": "rgba(147, 33, 28, 1)", + "press": "rgba(103, 22, 17, 1)", + }, + "notice": { + "default": "rgba(161, 99, 9, 1)", + "hover": "rgba(110, 72, 5, 1)", + "press": "rgba(74, 51, 7, 1)", + }, + "onNegative": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "onNeutral": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(243, 243, 243, 1)", + "press": "rgba(232, 232, 232, 1)", + }, + "onNotice": { + "default": "rgba(31, 31, 31, 1)", + "hover": "rgba(31, 31, 31, 1)", + "press": "rgba(31, 31, 31, 1)", + }, + "onOnImg": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "onPositive": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "onPrimary": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "positive": { + "default": "rgba(17, 131, 8, 1)", + "hover": "rgba(4, 93, 0, 1)", + "press": "rgba(4, 93, 0, 1)", + }, + "press": "rgba(81, 81, 81, 1)", + "secondary": { + "default": "rgba(81, 81, 81, 1)", + "hover": "rgba(56, 56, 56, 1)", + "press": "rgba(31, 31, 31, 1)", + }, + "tertiary": { + "default": "rgba(113, 113, 113, 1)", + "hover": "rgba(81, 81, 81, 1)", + "press": "rgba(56, 56, 56, 1)", + }, + }, + "text": { + "brandPremium": { + "default": "rgba(253, 158, 22, 1)", + "hover": "rgba(243, 152, 21, 1)", + "press": "rgba(213, 133, 18, 1)", + }, + "default": "rgba(31, 31, 31, 1)", + "disable": "rgba(194, 194, 194, 1)", + "hover": "rgba(56, 56, 56, 1)", + "info": { + "default": "rgba(31, 117, 188, 1)", + "hover": "rgba(24, 81, 130, 1)", + "press": "rgba(19, 58, 93, 1)", + }, + "negative": { + "default": "rgba(206, 54, 46, 1)", + "hover": "rgba(147, 33, 28, 1)", + "press": "rgba(103, 22, 17, 1)", + }, + "notice": { + "default": "rgba(161, 99, 9, 1)", + "hover": "rgba(110, 72, 5, 1)", + "press": "rgba(74, 51, 7, 1)", + }, + "onDiscovery": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "onHud": { + "default": "rgba(228, 228, 228, 1)", + "hover": "rgba(228, 228, 228, 1)", + "press": "rgba(228, 228, 228, 1)", + }, + "onNegative": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "onNotice": { + "default": "rgba(31, 31, 31, 1)", + "hover": "rgba(31, 31, 31, 1)", + "press": "rgba(31, 31, 31, 1)", + }, + "onOnImg": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "onPositive": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "onPrimary": { + "default": "rgba(255, 255, 255, 1)", + "hover": "rgba(255, 255, 255, 1)", + "press": "rgba(255, 255, 255, 1)", + }, + "placeholder": { + "default": "rgba(148, 148, 148, 1)", + "hover": "rgba(148, 148, 148, 1)", + "press": "rgba(148, 148, 148, 1)", + }, + "positive": { + "default": "rgba(17, 131, 8, 1)", + "hover": "rgba(4, 93, 0, 1)", + "press": "rgba(7, 64, 4, 1)", + }, + "press": "rgba(81, 81, 81, 1)", + "secondary": { + "default": "rgba(81, 81, 81, 1)", + "hover": "rgba(56, 56, 56, 1)", + "press": "rgba(31, 31, 31, 1)", + }, + "tertiary": { + "default": "rgba(113, 113, 113, 1)", + "hover": "rgba(81, 81, 81, 1)", + "press": "rgba(56, 56, 56, 1)", + }, + "visited": { + "default": "rgba(103, 39, 171, 1)", + "hover": "rgba(70, 32, 115, 1)", + "press": "rgba(40, 16, 70, 1)", + }, + }, + }, + "paragraphWidth": { + "l": "672", + "lCompact": "588", + "lCozy": "924", + "m": "448", + "mCompact": "392", + "mCozy": "616", + "s": "320", + "sCompact": "280", + "sCozy": "588", + }, + "radius": { + "0": "0", + "l": "12", + "m": "8", + "oval": "999999", + "s": "4", + "xl": "16", + "xs": "2", + "xxl": "24", + }, + "space": { + "betweenCheckboxes": { + "horizontal": "12", + "vertical": "0", + }, + "layout": { + "0": "0", + "10": "4", + "100": "440", + "20": "8", + "25": "12", + "30": "16", + "40": "24", + "50": "40", + "60": "64", + "70": "104", + "80": "168", + "90": "272", + }, + "target": { + "l": "48", + "m": "40", + "s": "32", + "xs": "24", + }, + }, + "text": { + "fontSize": { + "body": "16", + "caption": { + "m": "14", + "s": "12", + }, + "heading": { + "l": "28", + "m": "25", + "s": "22", + "xl": "32", + "xs": "20", + "xxl": "36", + "xxs": "18", + "xxxl": "40", + "xxxs": "14", + }, + "paragraph": "16", + }, + "fontWeight": { + "bold": "700", + "regular": "400", + }, + "lineHeight": { + "body": "24", + "caption": { + "m": "20", + "s": "18", + }, + "heading": { + "l": "36", + "m": "32", + "s": "28", + "xl": "40", + "xs": "28", + "xxl": "44", + "xxs": "24", + "xxxl": "52", + "xxxs": "20", + }, + "paragraph": "28", + }, + }, +} +`; diff --git a/packages/theme/src/token-object/helpers/changecase-keys.test-d.ts b/packages/theme/src/token-object/helpers/changecase-keys.test-d.ts new file mode 100644 index 000000000..6ae1dc6ad --- /dev/null +++ b/packages/theme/src/token-object/helpers/changecase-keys.test-d.ts @@ -0,0 +1,32 @@ +import { camelCaseKeys } from './changecase-keys' + +describe('camelcaseKeys test', () => { + it('should convert snake_case keys to camelCase', () => { + const obj = { + snake_case: 'value', + nested: { + another_key: 'another_value', + }, + } + type Result = ReturnType> + expectTypeOf().toEqualTypeOf<{ + snakeCase: string + nested: { anotherKey: string } + }>() + }) + + it('should convert kebab-case keys to camelCase', () => { + const obj = { + 'kebab-case': 'value', + nested: { + 'another-key': 'another_value', + }, + } + type Result = ReturnType> + + expectTypeOf().toEqualTypeOf<{ + kebabCase: string + nested: { anotherKey: string } + }>() + }) +}) diff --git a/packages/theme/src/token-object/helpers/changecase-keys.test.ts b/packages/theme/src/token-object/helpers/changecase-keys.test.ts new file mode 100644 index 000000000..8d1e1f47f --- /dev/null +++ b/packages/theme/src/token-object/helpers/changecase-keys.test.ts @@ -0,0 +1,52 @@ +import { camelCaseKeys } from './changecase-keys' + +import { createTokenObject } from '..' +import lightToken from '../../json/pixiv-light.json' +import darkToken from '../../json/pixiv-dark.json' +import baseToken from '../../json/base.json' + +describe('camelcaseKeys test', () => { + it('should convert snake_case keys to camelCase', () => { + const obj = { + snake_case: 'value', + nested: { + another_key: 'another_value', + }, + } + const result = camelCaseKeys(obj) + expect(result).toEqual({ + snakeCase: 'value', + nested: { + anotherKey: 'another_value', + }, + }) + }) + + it('should convert kebab-case keys to camelCase', () => { + const obj = { + 'kebab-case': 'value', + nested: { + 'another-key': 'another_value', + }, + } + const result = camelCaseKeys(obj) + expect(result).toEqual({ + kebabCase: 'value', + nested: { + anotherKey: 'another_value', + }, + }) + }) +}) + +describe.each([ + ['light theme', lightToken], + ['dark theme', darkToken], +] as const)('changeCaseKeys snapshot test: %s', (description, token) => { + test('camelcaseKeys snapshot test', () => { + const theme = createTokenObject(token, baseToken) + const result = camelCaseKeys(theme) + + expect(result).toMatchSnapshot() + }) +}) diff --git a/packages/theme/src/token-object/helpers/changecase-keys.ts b/packages/theme/src/token-object/helpers/changecase-keys.ts new file mode 100644 index 000000000..e48b207fc --- /dev/null +++ b/packages/theme/src/token-object/helpers/changecase-keys.ts @@ -0,0 +1,42 @@ +import { camelCase } from 'change-case' + +const isObject = (value: unknown): value is Record => { + if (value instanceof RegExp) return false + if (value instanceof Date) return false + if (value instanceof Error) return false + + return typeof value === 'object' && value !== null +} + +type CamelCase< + T extends string, + D extends string = '-' +> = T extends `${infer A}${D}${infer B}` + ? `${Lowercase}${Capitalize>}` + : T + +type CamelCaseKeys< + T extends Record, + D extends string = '-' +> = { + [K in keyof T as CamelCase]: T[K] extends Record< + string, + unknown + > + ? CamelCaseKeys + : T[K] +} + +export const camelCaseKeys = < + T extends Record, + Delimiter extends string = '-' +>( + obj: T +): CamelCaseKeys => { + return Object.fromEntries( + Object.entries(obj).map(([key, value]) => [ + camelCase(key), + isObject(value) ? camelCaseKeys(value) : value, + ]) + ) as CamelCaseKeys +} diff --git a/packages/theme/src/token-object/helpers/is-empty-array.ts b/packages/theme/src/token-object/helpers/is-empty-array.ts new file mode 100644 index 000000000..f86786ee2 --- /dev/null +++ b/packages/theme/src/token-object/helpers/is-empty-array.ts @@ -0,0 +1,2 @@ +export const isNonEmptyArray = (arr: T[]): arr is [T, ...T[]] => + arr.length > 0 diff --git a/packages/theme/src/token-object/helpers/nest-object.test.ts b/packages/theme/src/token-object/helpers/nest-object.test.ts new file mode 100644 index 000000000..e12febe0c --- /dev/null +++ b/packages/theme/src/token-object/helpers/nest-object.test.ts @@ -0,0 +1,39 @@ +import { nestObject } from './nest-object' + +describe('nestObject test', () => { + it('should create a nested object from a single key path', () => { + const path: ['a'] = ['a'] + const value = 'value' + const result = nestObject(path, value) + expect(result).toEqual({ a: value }) + }) + + it('should create a nested object from a multi-level key path', () => { + const path: ['a', 'b', 'c'] = ['a', 'b', 'c'] + const value = 'value' + const result = nestObject(path, value) + expect(result).toEqual({ a: { b: { c: value } } }) + }) + + it('should handle nested paths correctly', () => { + const path: ['theme', 'color', 'background', 'default'] = [ + 'theme', + 'color', + 'background', + 'default', + ] + const value = '#ffffff' + const result = nestObject(path, value) + expect(result).toEqual({ + theme: { color: { background: { default: value } } }, + }) + }) + + it('should throw an error if an empty path is provided', () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + expect(() => nestObject([], 'value')).toThrowError( + 'Path must be a non-empty array' + ) + }) +}) diff --git a/packages/theme/src/token-object/helpers/nest-object.ts b/packages/theme/src/token-object/helpers/nest-object.ts new file mode 100644 index 000000000..d70718f95 --- /dev/null +++ b/packages/theme/src/token-object/helpers/nest-object.ts @@ -0,0 +1,28 @@ +import { isNonEmptyArray } from './is-empty-array' + +type MakeNestObject

= P extends [ + infer Head, + ...infer Tail +] + ? Head extends string + ? Tail extends string[] + ? { + [K in Head]: MakeNestObject + } + : { [K in Head]: T } + : T + : T + +export const nestObject =

( + path: P, + value: T +): MakeNestObject => { + if (!isNonEmptyArray(path)) throw new Error('Path must be a non-empty array') + + const [key, ...rest] = path + if (!isNonEmptyArray(rest)) return { [key]: value } as MakeNestObject + + return { + [key]: nestObject(rest, value), + } as MakeNestObject +} diff --git a/packages/theme/src/token-object/index.ts b/packages/theme/src/token-object/index.ts new file mode 100644 index 000000000..d048f61c1 --- /dev/null +++ b/packages/theme/src/token-object/index.ts @@ -0,0 +1,46 @@ +import deepmerge from 'deepmerge' +import { isNonEmptyArray } from './helpers/is-empty-array' +import { nestObject } from './helpers/nest-object' +import { createReferenceTokenResolver } from './reference-token' +import type { Tokens, TokenObject, TokenDictionary, TokenValue } from './types' + +export const toTokenObject = (tokens: T): TokenObject => { + let result = {} + for (const key in tokens) { + const { value } = tokens[key] + const splitted = key.split('/') + if (!isNonEmptyArray(splitted)) continue + + const v = nestObject(splitted, value) + result = deepmerge(result, v) + } + + return result as TokenObject +} + +export const createTokenObject = ( + tokenDictionary: T, + baseTokenDictionary: TokenDictionary +): { [K in keyof T]: TokenObject } => { + const result = {} as { [K in keyof T]: TokenObject } + const referenceTokenResolver = createReferenceTokenResolver( + tokenDictionary, + baseTokenDictionary + ) + + for (const category in tokenDictionary) { + const value = tokenDictionary[category] + + // category ごとに template を展開していく + const resolvedTokens = Object.fromEntries( + Object.entries(value).map(([key, value]) => [ + key, + { value: referenceTokenResolver(value.value) } satisfies TokenValue, + ]) + ) as typeof value + + result[category] = toTokenObject(resolvedTokens) + } + + return result +} diff --git a/packages/theme/src/token-object/reference-token.test.ts b/packages/theme/src/token-object/reference-token.test.ts new file mode 100644 index 000000000..020dc466b --- /dev/null +++ b/packages/theme/src/token-object/reference-token.test.ts @@ -0,0 +1,60 @@ +import { createReferenceTokenResolver } from './reference-token' + +describe('createReferenceTokenResolver', () => { + const target = { + colors: { + primary: { value: '{colors.accent}' }, + }, + fonts: { + header: { value: '{fonts.main}' }, + }, + } + + const origin = { + colors: { + accent: { value: '{colors.secondary}' }, + secondary: { value: '#00ff00' }, + }, + fonts: { + main: { value: 'Arial' }, + secondaryHeader: { value: '{fonts.main}' }, + }, + } + + const resolver = createReferenceTokenResolver(target, origin) + + it('should return the same value if not a reference token', () => { + const value = 'no-reference' + expect(resolver(value)).toBe(value) + }) + + it('should resolve a reference token using the reference', () => { + const value = '{colors.primary}' + expect(resolver(value)).toBe('#00ff00') + }) + + it('should resolve a reference token directly from reference', () => { + const value = '{colors.accent}' + expect(resolver(value)).toBe('#00ff00') + }) + + it('should resolve a nested reference token in source via reference', () => { + const value = '{fonts.header}' + expect(resolver(value)).toBe('Arial') + }) + + it('should resolve a reference directly from reference without template source', () => { + const value = '{colors.secondary}' + expect(resolver(value)).toBe('#00ff00') + }) + + it('should resolve a reference in valueReference that points to another valueReference', () => { + const value = '{fonts.secondaryHeader}' + expect(resolver(value)).toBe('Arial') + }) + + it('should throw error for unknown tokens', () => { + const value = '{unknown.category}' + expect(() => resolver(value)).toThrow() + }) +}) diff --git a/packages/theme/src/token-object/reference-token.ts b/packages/theme/src/token-object/reference-token.ts new file mode 100644 index 000000000..b2b12976a --- /dev/null +++ b/packages/theme/src/token-object/reference-token.ts @@ -0,0 +1,32 @@ +import { TokenDictionary, Tokens } from './types' + +export type ReferenceToken = `{${string}}` + +const isReferenceToken = (value: string): value is ReferenceToken => + value.startsWith('{') && value.endsWith('}') + +const parseReferenceToken = ( + value: ReferenceToken +): [category: string, key: string] => { + const [category, key] = value.slice(1, -1).split('.') + + return [category, key] +} + +export const createReferenceTokenResolver = ( + tokenDictionary: T, + baseTokenDictionary: TokenDictionary +) => { + const resolver = (value: string): string => { + if (!isReferenceToken(value)) return value + + const [category, tokenKey] = parseReferenceToken(value) + const baseTokens = baseTokenDictionary[category] as Tokens | undefined + + return resolver( + (baseTokens?.[tokenKey] ?? tokenDictionary[category][tokenKey]).value + ) + } + + return resolver +} diff --git a/packages/theme/src/token-object/token-object.test.ts b/packages/theme/src/token-object/token-object.test.ts index d5fb12938..18c46b615 100644 --- a/packages/theme/src/token-object/token-object.test.ts +++ b/packages/theme/src/token-object/token-object.test.ts @@ -1,9 +1,5 @@ -import { - createTemplateResolver, - createTokenObject, - mappedTokenObject, - pathToObject, -} from './token-object' +import { createTokenObject, toTokenObject } from '.' +import { createReferenceTokenResolver } from './reference-token' import lightToken from '../json/pixiv-light.json' import darkToken from '../json/pixiv-dark.json' import baseToken from '../json/base.json' @@ -18,7 +14,7 @@ describe.each([ const keys = Object.keys(token) // 結局実装で使ってるコードで検証してるので意味ないかも // とはいえほぼこの実装になると思うどうしよう - const templateResolver = createTemplateResolver(token, baseToken) + const templateResolver = createReferenceTokenResolver(token, baseToken) // スナップショット it('should match snapshot', () => { @@ -44,45 +40,7 @@ describe.each([ }) }) -describe('pathToObject', () => { - it('should create a nested object from a single key path', () => { - const path: ['a'] = ['a'] - const value = 'value' - const result = pathToObject(path, value) - expect(result).toEqual({ a: value }) - }) - - it('should create a nested object from a multi-level key path', () => { - const path: ['a', 'b', 'c'] = ['a', 'b', 'c'] - const value = 'value' - const result = pathToObject(path, value) - expect(result).toEqual({ a: { b: { c: value } } }) - }) - - it('should handle nested paths correctly', () => { - const path: ['theme', 'color', 'background', 'default'] = [ - 'theme', - 'color', - 'background', - 'default', - ] - const value = '#ffffff' - const result = pathToObject(path, value) - expect(result).toEqual({ - theme: { color: { background: { default: value } } }, - }) - }) - - it('should throw an error if an empty path is provided', () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - expect(() => pathToObject([], 'value')).toThrowError( - 'Path must be a non-empty array' - ) - }) -}) - -describe('mappedTokenObject', () => { +describe('toTokenObject test', () => { const tokens: Tokens = { 'color/background/default': { value: '#ffffff' }, 'color/background/secondary': { value: '#f2f2f2' }, @@ -91,7 +49,7 @@ describe('mappedTokenObject', () => { } it('should create a nested object from token paths', () => { - const result = mappedTokenObject(tokens) + const result = toTokenObject(tokens) expect(result).toEqual({ color: { @@ -116,8 +74,8 @@ describe('mappedTokenObject', () => { } const mergedResult = deepmerge( - mappedTokenObject(tokens), - mappedTokenObject(additionalTokens) + toTokenObject(tokens), + toTokenObject(additionalTokens) ) expect(mergedResult).toEqual({ @@ -138,7 +96,7 @@ describe('mappedTokenObject', () => { }) it('should return an empty object for empty tokens', () => { - const result = mappedTokenObject({}) + const result = toTokenObject({}) expect(result).toEqual({}) }) }) diff --git a/packages/theme/src/token-object/token-object.ts b/packages/theme/src/token-object/token-object.ts deleted file mode 100644 index ca5259b08..000000000 --- a/packages/theme/src/token-object/token-object.ts +++ /dev/null @@ -1,91 +0,0 @@ -import deepmerge from 'deepmerge' -import { - NestedObject, - Tokens, - MappedTokenObject, - TokenMap, -} from './types' - -const isNonEmptyArray = (arr: T[]): arr is [T, ...T[]] => arr.length > 0 - -export const pathToObject =

( - path: P, - value: T -): NestedObject => { - if (!isNonEmptyArray(path)) throw new Error('Path must be a non-empty array') - - const [key, ...rest] = path - if (!isNonEmptyArray(rest)) return { [key]: value } as NestedObject - - return { - [key]: pathToObject(rest, value), - } as NestedObject -} - -export const mappedTokenObject = ( - tokens: T -): MappedTokenObject => { - let result = {} - for (const key in tokens) { - const { value } = tokens[key] - const splitted = key.split('/') - if (!isNonEmptyArray(splitted)) continue - - const v = pathToObject(splitted, value) - result = deepmerge(result, v) - } - - return result as MappedTokenObject -} - -type Template = `{${string}}` -const isTemplate = (value: string): value is Template => - value.startsWith('{') && value.endsWith('}') - -const parseTemplate = (value: Template): [string, string] => { - const [category, key] = value.slice(1, -1).split('.') - - return [category, key] -} - -export const createTemplateResolver = ( - tokenMap: T, - baseToken: TokenMap -) => { - const resolver = (value: string): string => { - if (!isTemplate(value)) return value - - const [category, tokenKey] = parseTemplate(value) - const baseTokens = baseToken[category] as Tokens | undefined - - return resolver( - (baseTokens?.[tokenKey] ?? tokenMap[category][tokenKey]).value - ) - } - - return resolver -} - -export const createTokenObject = ( - tokenMap: T, - baseToken: TokenMap, -): { [K in keyof T]: MappedTokenObject } => { - const result = {} as { [K in keyof T]: MappedTokenObject } - const templateResolver = createTemplateResolver(tokenMap, baseToken) - - for (const category in tokenMap) { - const value = tokenMap[category] - - // category ごとに template を展開していく - const transformed = Object.fromEntries( - Object.entries(value).map(([key, value]) => [ - key, - { value: templateResolver(value.value) }, - ]) - ) as typeof value - - result[category] = mappedTokenObject(transformed) - } - - return result -} diff --git a/packages/theme/src/token-object/types.ts b/packages/theme/src/token-object/types.ts index fabc97a01..f314b166b 100644 --- a/packages/theme/src/token-object/types.ts +++ b/packages/theme/src/token-object/types.ts @@ -1,42 +1,21 @@ type UnionToIntersection = // eslint-disable-next-line @typescript-eslint/no-explicit-any (U extends any ? (k: U) => void : never) extends (k: infer I) => void - ? I - : never + ? I + : never -type TokenValue = string -export type Tokens = Record -export type TokenMap = Record +export type TokenValue = { value: string } +export type Tokens = { + [key: string]: TokenValue +} +export type TokenDictionary = { + [category: string]: Tokens +} -export type ChainCaseToCamelCase< - S extends string, - D extends string = '-' -> = S extends `${infer F}${D}${infer R}` - ? `${F}${Capitalize>}` - : S -type ToTokenObject = S extends `${infer F}/${infer R}` - ? { - [K in F]: ToTokenObject - } - : { - [K in S]: V - } +type TokenToObject = S extends `${infer F}/${infer R}` + ? { [K in F]: TokenToObject } + : { [K in S]: V } -export type MappedTokenObject = UnionToIntersection< - { - [K in keyof T]: ToTokenObject - }[keyof T] +export type TokenObject = UnionToIntersection< + { [K in keyof T]: TokenToObject }[keyof T] > - -export type NestedObject

= P extends [ - infer Head, - ...infer Tail -] - ? Head extends string - ? Tail extends string[] - ? { - [K in Head]: NestedObject - } - : { [K in Head]: T } - : T - : T diff --git a/packages/theme/vitest.config.ts b/packages/theme/vitest.config.ts index eec0e43b9..cbf425d24 100644 --- a/packages/theme/vitest.config.ts +++ b/packages/theme/vitest.config.ts @@ -5,6 +5,9 @@ import * as path from 'node:path' export default defineConfig({ test: { globals: true, + typecheck: { + enabled: true, + }, alias: [ { find: /@charcoal-ui\/(.*)/, From 51a5c46b3171afc1183e5899fd34ee0e8b6ee5c0 Mon Sep 17 00:00:00 2001 From: naporin0624 Date: Wed, 23 Oct 2024 06:59:11 +0000 Subject: [PATCH 06/15] chore(theme): add token object --- packages/theme/src/index.ts | 1 + packages/theme/src/token-object/index.ts | 20 +++---------------- .../theme/src/token-object/to-token-object.ts | 18 +++++++++++++++++ .../src/token-object/token-object.test.ts | 3 ++- 4 files changed, 24 insertions(+), 18 deletions(-) create mode 100644 packages/theme/src/token-object/to-token-object.ts diff --git a/packages/theme/src/index.ts b/packages/theme/src/index.ts index 50ae74590..02bc37f1e 100644 --- a/packages/theme/src/index.ts +++ b/packages/theme/src/index.ts @@ -1,3 +1,4 @@ export * from './theme' export * from './abstract-theme' export * from './default' +export * from './token-object' diff --git a/packages/theme/src/token-object/index.ts b/packages/theme/src/token-object/index.ts index d048f61c1..33eb02f00 100644 --- a/packages/theme/src/token-object/index.ts +++ b/packages/theme/src/token-object/index.ts @@ -1,22 +1,8 @@ -import deepmerge from 'deepmerge' -import { isNonEmptyArray } from './helpers/is-empty-array' -import { nestObject } from './helpers/nest-object' import { createReferenceTokenResolver } from './reference-token' -import type { Tokens, TokenObject, TokenDictionary, TokenValue } from './types' +import { toTokenObject } from './to-token-object' +import type { TokenObject, TokenDictionary, TokenValue } from './types' -export const toTokenObject = (tokens: T): TokenObject => { - let result = {} - for (const key in tokens) { - const { value } = tokens[key] - const splitted = key.split('/') - if (!isNonEmptyArray(splitted)) continue - - const v = nestObject(splitted, value) - result = deepmerge(result, v) - } - - return result as TokenObject -} +export { camelCaseKeys } from './helpers/changecase-keys' export const createTokenObject = ( tokenDictionary: T, diff --git a/packages/theme/src/token-object/to-token-object.ts b/packages/theme/src/token-object/to-token-object.ts new file mode 100644 index 000000000..622026298 --- /dev/null +++ b/packages/theme/src/token-object/to-token-object.ts @@ -0,0 +1,18 @@ +import deepmerge from 'deepmerge' +import { isNonEmptyArray } from './helpers/is-empty-array' +import { nestObject } from './helpers/nest-object' +import { Tokens, TokenObject } from './types' + +export const toTokenObject = (tokens: T): TokenObject => { + let result = {} + for (const key in tokens) { + const { value } = tokens[key] + const splitted = key.split('/') + if (!isNonEmptyArray(splitted)) continue + + const v = nestObject(splitted, value) + result = deepmerge(result, v) + } + + return result as TokenObject +} diff --git a/packages/theme/src/token-object/token-object.test.ts b/packages/theme/src/token-object/token-object.test.ts index 18c46b615..e174a52c4 100644 --- a/packages/theme/src/token-object/token-object.test.ts +++ b/packages/theme/src/token-object/token-object.test.ts @@ -1,4 +1,5 @@ -import { createTokenObject, toTokenObject } from '.' +import { createTokenObject } from '.' +import { toTokenObject } from './to-token-object' import { createReferenceTokenResolver } from './reference-token' import lightToken from '../json/pixiv-light.json' import darkToken from '../json/pixiv-dark.json' From 02bb4dcd24a42d8b7f59daab7b703f518384db0b Mon Sep 17 00:00:00 2001 From: naporin0624 Date: Wed, 23 Oct 2024 07:26:07 +0000 Subject: [PATCH 07/15] chore(theme): commonjs support --- packages/theme/package.json | 2 +- .../token-object/helpers/changecase-keys.ts | 2 +- yarn.lock | 39 ++++++++++++++++++- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/packages/theme/package.json b/packages/theme/package.json index bd8ad95a3..e032bd0b3 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -37,7 +37,7 @@ "dependencies": { "@charcoal-ui/foundation": "^4.0.0-beta.15", "@charcoal-ui/utils": "^4.0.0-beta.15", - "change-case": "^5.4.4", + "change-case-all": "^2.1.0", "deepmerge": "^4.3.1", "polished": "^4.1.4" }, diff --git a/packages/theme/src/token-object/helpers/changecase-keys.ts b/packages/theme/src/token-object/helpers/changecase-keys.ts index e48b207fc..9c6c1b4ca 100644 --- a/packages/theme/src/token-object/helpers/changecase-keys.ts +++ b/packages/theme/src/token-object/helpers/changecase-keys.ts @@ -1,4 +1,4 @@ -import { camelCase } from 'change-case' +import { camelCase } from 'change-case-all' const isObject = (value: unknown): value is Record => { if (value instanceof RegExp) return false diff --git a/yarn.lock b/yarn.lock index 9d7a14021..9cc3a4102 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2224,7 +2224,7 @@ __metadata: dependencies: "@charcoal-ui/foundation": ^4.0.0-beta.15 "@charcoal-ui/utils": ^4.0.0-beta.15 - change-case: ^5.4.4 + change-case-all: ^2.1.0 cpx: ^1.5.0 deepmerge: ^4.3.1 npm-run-all: ^4.1.5 @@ -12274,6 +12274,18 @@ __metadata: languageName: node linkType: hard +"change-case-all@npm:^2.1.0": + version: 2.1.0 + resolution: "change-case-all@npm:2.1.0" + dependencies: + change-case: ^5.2.0 + sponge-case: ^2.0.2 + swap-case: ^3.0.2 + title-case: ^3.0.3 + checksum: 0aadbdbde682976a9b34b05de930013404052a0eef75145405a99f7d59b06cbbad5a683d0c101b1823564e8d80b37da502884b9d9f6e7c36b8912fe5b2d3ac45 + languageName: node + linkType: hard + "change-case@npm:^4.1.2": version: 4.1.2 resolution: "change-case@npm:4.1.2" @@ -12294,7 +12306,7 @@ __metadata: languageName: node linkType: hard -"change-case@npm:^5.4.4": +"change-case@npm:^5.2.0": version: 5.4.4 resolution: "change-case@npm:5.4.4" checksum: a22a25a763719658424ffbcd41e931d2d19cc22399cc765dca447fbe1eaf13e179d5e8ab1677af75f2e814dbddf74e42ffdecb526cd5bc906cc859f62aa154b2 @@ -26932,6 +26944,13 @@ __metadata: languageName: node linkType: hard +"sponge-case@npm:^2.0.2": + version: 2.0.3 + resolution: "sponge-case@npm:2.0.3" + checksum: e66fd121e05c9414780283f286d78d487319cc678835bd47a173144261610329435a2a4b7752cd1c4df1531fce08951f5241a6235b291c6679ce1030932f5bc1 + languageName: node + linkType: hard + "sprintf-js@npm:~1.0.2": version: 1.0.3 resolution: "sprintf-js@npm:1.0.3" @@ -27593,6 +27612,13 @@ __metadata: languageName: node linkType: hard +"swap-case@npm:^3.0.2": + version: 3.0.3 + resolution: "swap-case@npm:3.0.3" + checksum: af210c61c9eecd8af539a214ba42c7f6ece7a9849dcd365bdd3e197200f909b134872b7ffa17f8227fed05215a1c46da343d08a8086f0b3400269ebd4676efe0 + languageName: node + linkType: hard + "swc-loader@npm:^0.2.3": version: 0.2.3 resolution: "swc-loader@npm:0.2.3" @@ -27990,6 +28016,15 @@ __metadata: languageName: node linkType: hard +"title-case@npm:^3.0.3": + version: 3.0.3 + resolution: "title-case@npm:3.0.3" + dependencies: + tslib: ^2.0.3 + checksum: e8b7ea006b53cf3208d278455d9f1e22c409459d7f9878da324fa3b18cc0aef8560924c19c744e870394a5d9cddfdbe029ebae9875909ee7f4fc562e7cbfc53e + languageName: node + linkType: hard + "tmp@npm:^0.0.33": version: 0.0.33 resolution: "tmp@npm:0.0.33" From 3305284b81719e7d82c71bcea9b3428df48e0931 Mon Sep 17 00:00:00 2001 From: naporin0624 Date: Thu, 24 Oct 2024 15:24:41 +0000 Subject: [PATCH 08/15] chore(theme): add benchmark test --- eslint.config.mjs | 4 ++-- packages/theme/package.json | 3 ++- .../src/token-object/token-object.bench.ts | 19 +++++++++++++++++++ packages/theme/vitest.config.ts | 1 + 4 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 packages/theme/src/token-object/token-object.bench.ts diff --git a/eslint.config.mjs b/eslint.config.mjs index 6e2de2681..8cc0500b2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -132,13 +132,13 @@ const config = [ }, { files: [ - '**/*.test.ts', - '**/*.test.tsx', + '**/*.test.{ts,tsx}', '**/__tests__/**', '**/*.config.*', '*.config.*', '**/*.story.*', '.storybook/**', + '**/*.bench.{ts,tsx}', ], languageOptions: { globals: { diff --git a/packages/theme/package.json b/packages/theme/package.json index e032bd0b3..fbc5138a9 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -24,7 +24,8 @@ "typecheck:main": "tsc --project tsconfig.build.json --pretty --noEmit", "typecheck:cli": "tsc --project cli/tsconfig.build.json --noEmit", "clean": "rimraf dist .tsbuildinfo", - "test": "vitest run --passWithNoTests" + "test": "vitest run --passWithNoTests", + "bench": "vitest bench --passWithNoTests" }, "devDependencies": { "cpx": "^1.5.0", diff --git a/packages/theme/src/token-object/token-object.bench.ts b/packages/theme/src/token-object/token-object.bench.ts new file mode 100644 index 000000000..c73fb7413 --- /dev/null +++ b/packages/theme/src/token-object/token-object.bench.ts @@ -0,0 +1,19 @@ +import { camelCaseKeys, createTokenObject } from '.' +import lightToken from '../json/pixiv-light.json' +import darkToken from '../json/pixiv-dark.json' +import baseToken from '../json/base.json' + +import { bench } from 'vitest' + +describe.each([ + ['light theme', lightToken], + ['dark theme', darkToken], +] as const)('createTokenObject test: %s', (description, token) => { + bench('benchmarks token object creation for the theme', () => { + createTokenObject(token, baseToken) + }) + + bench('benchmarks token object creation with camelCase formatting', () => { + camelCaseKeys(createTokenObject(token, baseToken)) + }) +}) diff --git a/packages/theme/vitest.config.ts b/packages/theme/vitest.config.ts index cbf425d24..5a1178071 100644 --- a/packages/theme/vitest.config.ts +++ b/packages/theme/vitest.config.ts @@ -8,6 +8,7 @@ export default defineConfig({ typecheck: { enabled: true, }, + benchmark: {}, alias: [ { find: /@charcoal-ui\/(.*)/, From a3f0d1975dddf3ed054998884a33bde0cf24e04c Mon Sep 17 00:00:00 2001 From: naporin0624 Date: Thu, 24 Oct 2024 15:32:12 +0000 Subject: [PATCH 09/15] chore(theme): remove token-object export --- packages/theme/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/theme/src/index.ts b/packages/theme/src/index.ts index 02bc37f1e..50ae74590 100644 --- a/packages/theme/src/index.ts +++ b/packages/theme/src/index.ts @@ -1,4 +1,3 @@ export * from './theme' export * from './abstract-theme' export * from './default' -export * from './token-object' From 5af6f0b5729d98532069b10493eafaa0b6ec4f5b Mon Sep 17 00:00:00 2001 From: naporin0624 Date: Thu, 24 Oct 2024 18:10:07 +0000 Subject: [PATCH 10/15] chore(theme): add token-object path --- package.json | 2 +- packages/theme/cli/token-object.ts | 64 +++++++ packages/theme/package.json | 27 ++- packages/theme/tsup.config.ts | 6 +- yarn.lock | 290 ++++++++++++++++++++++++++++- 5 files changed, 378 insertions(+), 11 deletions(-) create mode 100644 packages/theme/cli/token-object.ts diff --git a/package.json b/package.json index 0ccc58737..f8929b964 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "@types/eslint__js": "^8.42.3", "@types/jest-image-snapshot": "^6.4.0", "@types/jest-specific-snapshot": "^0.5.9", - "@types/node": "^17.0.13", + "@types/node": "^18.19.59", "@types/prettier": "^2.4.3", "@types/styled-components": "^5.1.21", "@types/webpack": "^5.28.0", diff --git a/packages/theme/cli/token-object.ts b/packages/theme/cli/token-object.ts new file mode 100644 index 000000000..1ac81536e --- /dev/null +++ b/packages/theme/cli/token-object.ts @@ -0,0 +1,64 @@ +/* eslint-disable no-console */ +import { readFileSync, writeFileSync } from 'fs' +import path from 'path' +import { createTokenObject } from '../src/token-object' +import type { TokenDictionary } from '../src/token-object/types' +import { parseArgs } from 'node:util' +import { camelCaseKeys } from '../src/token-object' + +// コマンドライン引数の解析 +const args = parseArgs({ + options: { + token: { type: 'string' }, + base: { type: 'string' }, + output: { type: 'string' }, + format: { type: 'string', short: 'f', default: 'kebab' }, + }, +}) + +const tokenFilePath = args.values.token +const baseFilePath = args.values.base +const outputFilePath = args.values.output +const format = args.values.format ?? 'camel' + +if ( + tokenFilePath === undefined || + baseFilePath === undefined || + outputFilePath === undefined +) { + console.error('Error: --token, --base, and --output options are required.') + process.exit(1) +} + +// ファイルの読み込み +const tokenJson = JSON.parse( + readFileSync(path.resolve(tokenFilePath), 'utf8') +) as TokenDictionary +const baseJson = JSON.parse( + readFileSync(path.resolve(baseFilePath), 'utf8') +) as TokenDictionary + +// トークンオブジェクトの生成 +let tokenObject = {} +switch (format) { + case 'kebab': { + tokenObject = createTokenObject(tokenJson, baseJson) + break + } + case 'camel': { + tokenObject = camelCaseKeys(createTokenObject(tokenJson, baseJson)) + break + } + default: { + console.error(`Error: Unknown format ${format}`) + process.exit(1) + } +} + +// 結果をファイルに書き込む +writeFileSync( + path.resolve(outputFilePath), + JSON.stringify(tokenObject, null, 2) +) + +console.log(`Token object generated and saved to ${outputFilePath}`) diff --git a/packages/theme/package.json b/packages/theme/package.json index fbc5138a9..0537830df 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -4,13 +4,21 @@ "license": "Apache-2.0", "main": "./dist/index.cjs.js", "module": "./dist/index.esm.js", + "types": "./dist/index.d.ts", "exports": { - "types": "./dist/index.d.ts", - "require": "./dist/index.cjs.js", - "import": "./dist/index.esm.js", - "default": "./dist/index.esm.js" + ".": { + "types": "./dist/index.d.ts", + "require": "./dist/index.cjs.js", + "import": "./dist/index.esm.js", + "default": "./dist/index.esm.js" + }, + "./token-object": { + "types": "./dist/token-object/index.d.ts", + "require": "./dist/token-object/index.cjs.js", + "import": "./dist/token-object/index.esm.js", + "default": "./dist/token-object/index.esm.js" + } }, - "types": "./dist/index.d.ts", "sideEffects": [ "*.css" ], @@ -18,20 +26,25 @@ "build": "npm-run-all --print-label --parallel 'build:*' --sequential serialize", "build:bundle": "FORCE_COLOR=1 tsup", "build:dts": "tsc --project tsconfig.build.json --pretty --emitDeclarationOnly", - "build:copy-css": "cpx 'src/css/**/*.css' dist/css && cpx 'src/json/**/*.json' dist/json", + "build:copy-css": "cpx 'src/css/**/*.css' dist/css", + "build:token-object": "npm-run-all token-object", "serialize": "node cli/index.js", "typecheck": "run-p --print-label 'typecheck:*'", "typecheck:main": "tsc --project tsconfig.build.json --pretty --noEmit", "typecheck:cli": "tsc --project cli/tsconfig.build.json --noEmit", "clean": "rimraf dist .tsbuildinfo", "test": "vitest run --passWithNoTests", - "bench": "vitest bench --passWithNoTests" + "bench": "vitest bench --passWithNoTests", + "token-object": "npm-run-all --print-label --parallel 'token-object:*'", + "token-object:light": "tsx cli/token-object --token ./src/json/pixiv-light.json --base ./src/json/base.json --output ./dist/json/pixiv-light.json", + "token-object:dark": "tsx cli/token-object --token ./src/json/pixiv-dark.json --base ./src/json/base.json --output ./dist/json/pixiv-dark.json" }, "devDependencies": { "cpx": "^1.5.0", "npm-run-all": "^4.1.5", "rimraf": "^3.0.2", "tsup": "^6.5.0", + "tsx": "^4.19.1", "typescript": "^4.9.5", "vitest": "^2.0.2" }, diff --git a/packages/theme/tsup.config.ts b/packages/theme/tsup.config.ts index 32353e14e..739eea7f8 100644 --- a/packages/theme/tsup.config.ts +++ b/packages/theme/tsup.config.ts @@ -1,7 +1,10 @@ import { defineConfig } from 'tsup' export default defineConfig({ - entry: ['src/index.ts'], + entry: { + index: 'src/index.ts', + 'token-object/index': 'src/token-object/index.ts', + }, format: ['esm', 'cjs'], outExtension({ format }) { return { @@ -10,4 +13,5 @@ export default defineConfig({ }, target: 'esnext', sourcemap: true, + clean: true, }) diff --git a/yarn.lock b/yarn.lock index 9cc3a4102..21396c436 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2231,6 +2231,7 @@ __metadata: polished: ^4.1.4 rimraf: ^3.0.2 tsup: ^6.5.0 + tsx: ^4.19.1 typescript: ^4.9.5 vitest: ^2.0.2 languageName: unknown @@ -2664,6 +2665,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/aix-ppc64@npm:0.23.1" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/android-arm64@npm:0.18.20" @@ -2692,6 +2700,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/android-arm64@npm:0.23.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.15.18": version: 0.15.18 resolution: "@esbuild/android-arm@npm:0.15.18" @@ -2727,6 +2742,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/android-arm@npm:0.23.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/android-x64@npm:0.18.20" @@ -2755,6 +2777,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/android-x64@npm:0.23.1" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/darwin-arm64@npm:0.18.20" @@ -2783,6 +2812,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/darwin-arm64@npm:0.23.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/darwin-x64@npm:0.18.20" @@ -2811,6 +2847,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/darwin-x64@npm:0.23.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/freebsd-arm64@npm:0.18.20" @@ -2839,6 +2882,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/freebsd-arm64@npm:0.23.1" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/freebsd-x64@npm:0.18.20" @@ -2867,6 +2917,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/freebsd-x64@npm:0.23.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-arm64@npm:0.18.20" @@ -2895,6 +2952,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-arm64@npm:0.23.1" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-arm@npm:0.18.20" @@ -2923,6 +2987,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-arm@npm:0.23.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-ia32@npm:0.18.20" @@ -2951,6 +3022,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-ia32@npm:0.23.1" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.14.54": version: 0.14.54 resolution: "@esbuild/linux-loong64@npm:0.14.54" @@ -2993,6 +3071,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-loong64@npm:0.23.1" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-mips64el@npm:0.18.20" @@ -3021,6 +3106,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-mips64el@npm:0.23.1" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-ppc64@npm:0.18.20" @@ -3049,6 +3141,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-ppc64@npm:0.23.1" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-riscv64@npm:0.18.20" @@ -3077,6 +3176,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-riscv64@npm:0.23.1" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-s390x@npm:0.18.20" @@ -3105,6 +3211,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-s390x@npm:0.23.1" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-x64@npm:0.18.20" @@ -3133,6 +3246,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-x64@npm:0.23.1" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/netbsd-x64@npm:0.18.20" @@ -3161,6 +3281,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/netbsd-x64@npm:0.23.1" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/openbsd-arm64@npm:0.23.1" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/openbsd-x64@npm:0.18.20" @@ -3189,6 +3323,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/openbsd-x64@npm:0.23.1" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/sunos-x64@npm:0.18.20" @@ -3217,6 +3358,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/sunos-x64@npm:0.23.1" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/win32-arm64@npm:0.18.20" @@ -3245,6 +3393,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/win32-arm64@npm:0.23.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/win32-ia32@npm:0.18.20" @@ -3273,6 +3428,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/win32-ia32@npm:0.23.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/win32-x64@npm:0.18.20" @@ -3301,6 +3463,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/win32-x64@npm:0.23.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0": version: 4.2.0 resolution: "@eslint-community/eslint-utils@npm:4.2.0" @@ -9192,7 +9361,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^17.0, @types/node@npm:^17.0.13": +"@types/node@npm:^17.0": version: 17.0.45 resolution: "@types/node@npm:17.0.45" checksum: aa04366b9103b7d6cfd6b2ef64182e0eaa7d4462c3f817618486ea0422984c51fc69fd0d436eae6c9e696ddfdbec9ccaa27a917f7c2e8c75c5d57827fe3d95e8 @@ -9208,6 +9377,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^18.19.59": + version: 18.19.59 + resolution: "@types/node@npm:18.19.59" + dependencies: + undici-types: ~5.26.4 + checksum: 8e45a05aa91437d3d11a346775ac16589353070c504f91a62cd91945a384231ccbbe92009a8e1ae653f954f9de5bf8ff106cc8e26721ecab98914dc019217aef + languageName: node + linkType: hard + "@types/normalize-package-data@npm:^2.4.0": version: 2.4.0 resolution: "@types/normalize-package-data@npm:2.4.0" @@ -12371,7 +12549,7 @@ __metadata: "@types/eslint__js": ^8.42.3 "@types/jest-image-snapshot": ^6.4.0 "@types/jest-specific-snapshot": ^0.5.9 - "@types/node": ^17.0.13 + "@types/node": ^18.19.59 "@types/prettier": ^2.4.3 "@types/styled-components": ^5.1.21 "@types/webpack": ^5.28.0 @@ -15623,6 +15801,89 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:~0.23.0": + version: 0.23.1 + resolution: "esbuild@npm:0.23.1" + dependencies: + "@esbuild/aix-ppc64": 0.23.1 + "@esbuild/android-arm": 0.23.1 + "@esbuild/android-arm64": 0.23.1 + "@esbuild/android-x64": 0.23.1 + "@esbuild/darwin-arm64": 0.23.1 + "@esbuild/darwin-x64": 0.23.1 + "@esbuild/freebsd-arm64": 0.23.1 + "@esbuild/freebsd-x64": 0.23.1 + "@esbuild/linux-arm": 0.23.1 + "@esbuild/linux-arm64": 0.23.1 + "@esbuild/linux-ia32": 0.23.1 + "@esbuild/linux-loong64": 0.23.1 + "@esbuild/linux-mips64el": 0.23.1 + "@esbuild/linux-ppc64": 0.23.1 + "@esbuild/linux-riscv64": 0.23.1 + "@esbuild/linux-s390x": 0.23.1 + "@esbuild/linux-x64": 0.23.1 + "@esbuild/netbsd-x64": 0.23.1 + "@esbuild/openbsd-arm64": 0.23.1 + "@esbuild/openbsd-x64": 0.23.1 + "@esbuild/sunos-x64": 0.23.1 + "@esbuild/win32-arm64": 0.23.1 + "@esbuild/win32-ia32": 0.23.1 + "@esbuild/win32-x64": 0.23.1 + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 0413c3b9257327fb598427688b7186ea335bf1693746fe5713cc93c95854d6388b8ed4ad643fddf5b5ace093f7dcd9038dd58e087bf2da1f04dfb4c5571660af + languageName: node + linkType: hard + "escalade@npm:^3.1.1": version: 3.1.1 resolution: "escalade@npm:3.1.1" @@ -17294,6 +17555,15 @@ __metadata: languageName: node linkType: hard +"get-tsconfig@npm:^4.7.5": + version: 4.8.1 + resolution: "get-tsconfig@npm:4.8.1" + dependencies: + resolve-pkg-maps: ^1.0.0 + checksum: 12df01672e691d2ff6db8cf7fed1ddfef90ed94a5f3d822c63c147a26742026d582acd86afcd6f65db67d809625d17dd7f9d34f4d3f38f69bc2f48e19b2bdd5b + languageName: node + linkType: hard + "get-value@npm:^2.0.3, get-value@npm:^2.0.6": version: 2.0.6 resolution: "get-value@npm:2.0.6" @@ -28417,6 +28687,22 @@ __metadata: languageName: node linkType: hard +"tsx@npm:^4.19.1": + version: 4.19.1 + resolution: "tsx@npm:4.19.1" + dependencies: + esbuild: ~0.23.0 + fsevents: ~2.3.3 + get-tsconfig: ^4.7.5 + dependenciesMeta: + fsevents: + optional: true + bin: + tsx: dist/cli.mjs + checksum: 31bfd2df62c1230f7c15f6e24d3790019ba7b2ad497221cb0cebcf5cf4f2c1ac971fac0d1283e3d80dc823652d2f9be946bd40ac65b640ff3f199b84a904a9c7 + languageName: node + linkType: hard + "tuf-js@npm:^1.1.7": version: 1.1.7 resolution: "tuf-js@npm:1.1.7" From 0216ce689568f0d1fee1c811020656496d1f5fca Mon Sep 17 00:00:00 2001 From: naporin0624 Date: Thu, 24 Oct 2024 20:06:25 +0000 Subject: [PATCH 11/15] feat(theme): add css variables json --- packages/theme/cli/token-object.ts | 10 +- packages/theme/package.json | 12 +- .../__snapshots__/token-object.test.ts.snap | 680 ++++++++++++++++++ packages/theme/src/token-object/index.ts | 28 + .../src/token-object/token-object.bench.ts | 6 +- .../src/token-object/token-object.test.ts | 73 +- yarn.lock | 9 + 7 files changed, 811 insertions(+), 7 deletions(-) diff --git a/packages/theme/cli/token-object.ts b/packages/theme/cli/token-object.ts index 1ac81536e..82942a16d 100644 --- a/packages/theme/cli/token-object.ts +++ b/packages/theme/cli/token-object.ts @@ -1,7 +1,7 @@ /* eslint-disable no-console */ -import { readFileSync, writeFileSync } from 'fs' +import { mkdirSync, readFileSync, writeFileSync } from 'fs' import path from 'path' -import { createTokenObject } from '../src/token-object' +import { createCSSTokenObject, createTokenObject } from '../src/token-object' import type { TokenDictionary } from '../src/token-object/types' import { parseArgs } from 'node:util' import { camelCaseKeys } from '../src/token-object' @@ -49,13 +49,17 @@ switch (format) { tokenObject = camelCaseKeys(createTokenObject(tokenJson, baseJson)) break } + case 'css': { + tokenObject = createCSSTokenObject(tokenJson, (x) => `charcoal-${x}`) + break + } default: { console.error(`Error: Unknown format ${format}`) process.exit(1) } } -// 結果をファイルに書き込む +mkdirSync(path.dirname(outputFilePath), { recursive: true }) writeFileSync( path.resolve(outputFilePath), JSON.stringify(tokenObject, null, 2) diff --git a/packages/theme/package.json b/packages/theme/package.json index 0537830df..19e675ce0 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -36,11 +36,19 @@ "test": "vitest run --passWithNoTests", "bench": "vitest bench --passWithNoTests", "token-object": "npm-run-all --print-label --parallel 'token-object:*'", - "token-object:light": "tsx cli/token-object --token ./src/json/pixiv-light.json --base ./src/json/base.json --output ./dist/json/pixiv-light.json", - "token-object:dark": "tsx cli/token-object --token ./src/json/pixiv-dark.json --base ./src/json/base.json --output ./dist/json/pixiv-dark.json" + "token-object:light": "npm-run-all --print-label --parallel 'token-object:light:*'", + "token-object:light:camel": "tsx cli/token-object --token ./src/json/pixiv-light.json --base ./src/json/base.json --output ./dist/json/camel/pixiv-light.json --format camel", + "token-object:light:kebab": "tsx cli/token-object --token ./src/json/pixiv-light.json --base ./src/json/base.json --output ./dist/json/kebab/pixiv-light.json --format kebab", + "token-object:light:css": "tsx cli/token-object --token ./src/json/pixiv-light.json --base ./src/json/base.json --output ./dist/json/css/pixiv-light.json --format css", + "token-object:dark": "npm-run-all --print-label --parallel 'token-object:dark:*'", + "token-object:dark:camel": "tsx cli/token-object --token ./src/json/pixiv-dark.json --base ./src/json/base.json --output ./dist/json/camel/pixiv-dark.json --format camel", + "token-object:dark:kebab": "tsx cli/token-object --token ./src/json/pixiv-dark.json --base ./src/json/base.json --output ./dist/json/kebab/pixiv-dark.json --format kebab", + "token-object:dark:css": "tsx cli/token-object --token ./src/json/pixiv-dark.json --base ./src/json/base.json --output ./dist/json/css/pixiv-dark.json --format css" }, "devDependencies": { + "@types/css": "^0.0.38", "cpx": "^1.5.0", + "css": "^3.0.0", "npm-run-all": "^4.1.5", "rimraf": "^3.0.2", "tsup": "^6.5.0", diff --git a/packages/theme/src/token-object/__snapshots__/token-object.test.ts.snap b/packages/theme/src/token-object/__snapshots__/token-object.test.ts.snap index fb26c460f..26cee6e93 100644 --- a/packages/theme/src/token-object/__snapshots__/token-object.test.ts.snap +++ b/packages/theme/src/token-object/__snapshots__/token-object.test.ts.snap @@ -1,5 +1,685 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`createCSSTokenObject test: dark theme > should match snapshot 1`] = ` +{ + "border-width": { + "focus": { + "1": "var(--charcoal-border-width-focus-1)", + "2": "var(--charcoal-border-width-focus-2)", + }, + "l": "var(--charcoal-border-width-l)", + "m": "var(--charcoal-border-width-m)", + }, + "color": { + "background": { + "default": "var(--charcoal-color-background-default)", + "secondary": "var(--charcoal-color-background-secondary)", + "tertiary": "var(--charcoal-color-background-tertiary)", + }, + "border": { + "default": "var(--charcoal-color-border-default)", + "disable": "var(--charcoal-color-border-disable)", + "focus": { + "1": "var(--charcoal-color-border-focus-1)", + "2": "var(--charcoal-color-border-focus-2)", + "legacy": "var(--charcoal-color-border-focus-legacy)", + }, + "hover": "var(--charcoal-color-border-hover)", + "hud": "var(--charcoal-color-border-hud)", + "negative": "var(--charcoal-color-border-negative)", + "press": "var(--charcoal-color-border-press)", + "secondary": "var(--charcoal-color-border-secondary)", + "selected": "var(--charcoal-color-border-selected)", + }, + "container": { + "default": "var(--charcoal-color-container-default)", + "default-a": "var(--charcoal-color-container-default-a)", + "disable": "var(--charcoal-color-container-disable)", + "discovery": { + "default": "var(--charcoal-color-container-discovery-default)", + "hover": "var(--charcoal-color-container-discovery-hover)", + "press": "var(--charcoal-color-container-discovery-press)", + }, + "hover": "var(--charcoal-color-container-hover)", + "hover-a": "var(--charcoal-color-container-hover-a)", + "hud": { + "default": "var(--charcoal-color-container-hud-default)", + "hover": "var(--charcoal-color-container-hud-hover)", + "press": "var(--charcoal-color-container-hud-press)", + }, + "negative": { + "default": "var(--charcoal-color-container-negative-default)", + "hover": "var(--charcoal-color-container-negative-hover)", + "press": "var(--charcoal-color-container-negative-press)", + }, + "neutral": { + "default": "var(--charcoal-color-container-neutral-default)", + "hover": "var(--charcoal-color-container-neutral-hover)", + "press": "var(--charcoal-color-container-neutral-press)", + }, + "notice": { + "default": "var(--charcoal-color-container-notice-default)", + "hover": "var(--charcoal-color-container-notice-hover)", + "press": "var(--charcoal-color-container-notice-press)", + }, + "on-img": { + "default": "var(--charcoal-color-container-on-img-default)", + "hover": "var(--charcoal-color-container-on-img-hover)", + "press": "var(--charcoal-color-container-on-img-press)", + }, + "positive": { + "default": "var(--charcoal-color-container-positive-default)", + "hover": "var(--charcoal-color-container-positive-hover)", + "press": "var(--charcoal-color-container-positive-press)", + }, + "press": "var(--charcoal-color-container-press)", + "press-a": "var(--charcoal-color-container-press-a)", + "primary": { + "default": "var(--charcoal-color-container-primary-default)", + "hover": "var(--charcoal-color-container-primary-hover)", + "press": "var(--charcoal-color-container-primary-press)", + }, + "secondary": { + "default": "var(--charcoal-color-container-secondary-default)", + "default-a": "var(--charcoal-color-container-secondary-default-a)", + "hover": "var(--charcoal-color-container-secondary-hover)", + "hover-a": "var(--charcoal-color-container-secondary-hover-a)", + "press": "var(--charcoal-color-container-secondary-press)", + "press-a": "var(--charcoal-color-container-secondary-press-a)", + }, + "skeleton": "var(--charcoal-color-container-skeleton)", + "subtle": "var(--charcoal-color-container-subtle)", + "tertiary": { + "default": "var(--charcoal-color-container-tertiary-default)", + "default-a": "var(--charcoal-color-container-tertiary-default-a)", + "hover": "var(--charcoal-color-container-tertiary-hover)", + "hover-a": "var(--charcoal-color-container-tertiary-hover-a)", + "press": "var(--charcoal-color-container-tertiary-press)", + "pressA": "var(--charcoal-color-container-tertiary-press-a)", + }, + }, + "icon": { + "default": "var(--charcoal-color-icon-default)", + "disable": "var(--charcoal-color-icon-disable)", + "hover": "var(--charcoal-color-icon-hover)", + "negative": { + "default": "var(--charcoal-color-icon-negative-default)", + "hover": "var(--charcoal-color-icon-negative-hover)", + "press": "var(--charcoal-color-icon-negative-press)", + }, + "notice": { + "default": "var(--charcoal-color-icon-notice-default)", + "hover": "var(--charcoal-color-icon-notice-hover)", + "press": "var(--charcoal-color-icon-notice-press)", + }, + "on-negative": { + "default": "var(--charcoal-color-icon-on-negative-default)", + "hover": "var(--charcoal-color-icon-on-negative-hover)", + "press": "var(--charcoal-color-icon-on-negative-press)", + }, + "on-neutral": { + "default": "var(--charcoal-color-icon-on-neutral-default)", + "hover": "var(--charcoal-color-icon-on-neutral-hover)", + "press": "var(--charcoal-color-icon-on-neutral-press)", + }, + "on-notice": { + "default": "var(--charcoal-color-icon-on-notice-default)", + "hover": "var(--charcoal-color-icon-on-notice-hover)", + "press": "var(--charcoal-color-icon-on-notice-press)", + }, + "on-on-img": { + "default": "var(--charcoal-color-icon-on-on-img-default)", + "hover": "var(--charcoal-color-icon-on-on-img-hover)", + "press": "var(--charcoal-color-icon-on-on-img-press)", + }, + "on-positive": { + "default": "var(--charcoal-color-icon-on-positive-default)", + "hover": "var(--charcoal-color-icon-on-positive-hover)", + "press": "var(--charcoal-color-icon-on-positive-press)", + }, + "on-primary": { + "default": "var(--charcoal-color-icon-on-primary-default)", + "hover": "var(--charcoal-color-icon-on-primary-hover)", + "press": "var(--charcoal-color-icon-on-primary-press)", + }, + "positive": { + "default": "var(--charcoal-color-icon-positive-default)", + "hover": "var(--charcoal-color-icon-positive-hover)", + "press": "var(--charcoal-color-icon-positive-press)", + }, + "press": "var(--charcoal-color-icon-press)", + "secondary": { + "default": "var(--charcoal-color-icon-secondary-default)", + "hover": "var(--charcoal-color-icon-secondary-hover)", + "press": "var(--charcoal-color-icon-secondary-press)", + }, + "tertiary": { + "default": "var(--charcoal-color-icon-tertiary-default)", + "hover": "var(--charcoal-color-icon-tertiary-hover)", + "press": "var(--charcoal-color-icon-tertiary-press)", + }, + }, + "text": { + "brand-premium": { + "default": "var(--charcoal-color-text-brand-premium-default)", + "hover": "var(--charcoal-color-text-brand-premium-hover)", + "press": "var(--charcoal-color-text-brand-premium-press)", + }, + "default": "var(--charcoal-color-text-default)", + "disable": "var(--charcoal-color-text-disable)", + "hover": "var(--charcoal-color-text-hover)", + "info": { + "default": "var(--charcoal-color-text-info-default)", + "hover": "var(--charcoal-color-text-info-hover)", + "press": "var(--charcoal-color-text-info-press)", + }, + "negative": { + "default": "var(--charcoal-color-text-negative-default)", + "hover": "var(--charcoal-color-text-negative-hover)", + "press": "var(--charcoal-color-text-negative-press)", + }, + "notice": { + "default": "var(--charcoal-color-text-notice-default)", + "hover": "var(--charcoal-color-text-notice-hover)", + "press": "var(--charcoal-color-text-notice-press)", + }, + "on-discovery": { + "default": "var(--charcoal-color-text-on-discovery-default)", + "hover": "var(--charcoal-color-text-on-discovery-hover)", + "press": "var(--charcoal-color-text-on-discovery-press)", + }, + "on-hud": { + "default": "var(--charcoal-color-text-on-hud-default)", + "hover": "var(--charcoal-color-text-on-hud-hover)", + "press": "var(--charcoal-color-text-on-hud-press)", + }, + "on-negative": { + "default": "var(--charcoal-color-text-on-negative-default)", + "hover": "var(--charcoal-color-text-on-negative-hover)", + "press": "var(--charcoal-color-text-on-negative-press)", + }, + "on-notice": { + "default": "var(--charcoal-color-text-on-notice-default)", + "hover": "var(--charcoal-color-text-on-notice-hover)", + "press": "var(--charcoal-color-text-on-notice-press)", + }, + "on-on-img": { + "default": "var(--charcoal-color-text-on-on-img-default)", + "hover": "var(--charcoal-color-text-on-on-img-hover)", + "press": "var(--charcoal-color-text-on-on-img-press)", + }, + "on-positive": { + "default": "var(--charcoal-color-text-on-positive-default)", + "hover": "var(--charcoal-color-text-on-positive-hover)", + "press": "var(--charcoal-color-text-on-positive-press)", + }, + "on-primary": { + "default": "var(--charcoal-color-text-on-primary-default)", + "hover": "var(--charcoal-color-text-on-primary-hover)", + "press": "var(--charcoal-color-text-on-primary-press)", + }, + "placeholder": { + "default": "var(--charcoal-color-text-placeholder-default)", + "hover": "var(--charcoal-color-text-placeholder-hover)", + "press": "var(--charcoal-color-text-placeholder-press)", + }, + "positive": { + "default": "var(--charcoal-color-text-positive-default)", + "hover": "var(--charcoal-color-text-positive-hover)", + "press": "var(--charcoal-color-text-positive-press)", + }, + "press": "var(--charcoal-color-text-press)", + "secondary": { + "default": "var(--charcoal-color-text-secondary-default)", + "hover": "var(--charcoal-color-text-secondary-hover)", + "press": "var(--charcoal-color-text-secondary-press)", + }, + "tertiary": { + "default": "var(--charcoal-color-text-tertiary-default)", + "hover": "var(--charcoal-color-text-tertiary-hover)", + "press": "var(--charcoal-color-text-tertiary-press)", + }, + "visited": { + "default": "var(--charcoal-color-text-visited-default)", + "hover": "var(--charcoal-color-text-visited-hover)", + "press": "var(--charcoal-color-text-visited-press)", + }, + }, + }, + "paragraph-width": { + "l": "var(--charcoal-paragraph-width-l)", + "l-compact": "var(--charcoal-paragraph-width-l-compact)", + "l-cozy": "var(--charcoal-paragraph-width-l-cozy)", + "m": "var(--charcoal-paragraph-width-m)", + "m-compact": "var(--charcoal-paragraph-width-m-compact)", + "m-cozy": "var(--charcoal-paragraph-width-m-cozy)", + "s": "var(--charcoal-paragraph-width-s)", + "s-compact": "var(--charcoal-paragraph-width-s-compact)", + "s-cozy": "var(--charcoal-paragraph-width-s-cozy)", + }, + "radius": { + "0": "var(--charcoal-radius-0)", + "l": "var(--charcoal-radius-l)", + "m": "var(--charcoal-radius-m)", + "oval": "var(--charcoal-radius-oval)", + "s": "var(--charcoal-radius-s)", + "xl": "var(--charcoal-radius-xl)", + "xs": "var(--charcoal-radius-xs)", + "xxl": "var(--charcoal-radius-xxl)", + }, + "space": { + "between-checkboxes": { + "horizontal": "var(--charcoal-space-between-checkboxes-horizontal)", + "vertical": "var(--charcoal-space-between-checkboxes-vertical)", + }, + "layout": { + "0": "var(--charcoal-space-layout-0)", + "10": "var(--charcoal-space-layout-10)", + "100": "var(--charcoal-space-layout-100)", + "20": "var(--charcoal-space-layout-20)", + "25": "var(--charcoal-space-layout-25)", + "30": "var(--charcoal-space-layout-30)", + "40": "var(--charcoal-space-layout-40)", + "50": "var(--charcoal-space-layout-50)", + "60": "var(--charcoal-space-layout-60)", + "70": "var(--charcoal-space-layout-70)", + "80": "var(--charcoal-space-layout-80)", + "90": "var(--charcoal-space-layout-90)", + }, + "target": { + "l": "var(--charcoal-space-target-l)", + "m": "var(--charcoal-space-target-m)", + "s": "var(--charcoal-space-target-s)", + "xs": "var(--charcoal-space-target-xs)", + }, + }, + "text": { + "font-size": { + "body": "var(--charcoal-text-font-size-body)", + "caption": { + "m": "var(--charcoal-text-font-size-caption-m)", + "s": "var(--charcoal-text-font-size-caption-s)", + }, + "heading": { + "l": "var(--charcoal-text-font-size-heading-l)", + "m": "var(--charcoal-text-font-size-heading-m)", + "s": "var(--charcoal-text-font-size-heading-s)", + "xl": "var(--charcoal-text-font-size-heading-xl)", + "xs": "var(--charcoal-text-font-size-heading-xs)", + "xxl": "var(--charcoal-text-font-size-heading-xxl)", + "xxs": "var(--charcoal-text-font-size-heading-xxs)", + "xxxl": "var(--charcoal-text-font-size-heading-xxxl)", + "xxxs": "var(--charcoal-text-font-size-heading-xxxs)", + }, + "paragraph": "var(--charcoal-text-font-size-paragraph)", + }, + "font-weight": { + "bold": "var(--charcoal-text-font-weight-bold)", + "regular": "var(--charcoal-text-font-weight-regular)", + }, + "line-height": { + "body": "var(--charcoal-text-line-height-body)", + "caption": { + "m": "var(--charcoal-text-line-height-caption-m)", + "s": "var(--charcoal-text-line-height-caption-s)", + }, + "heading": { + "l": "var(--charcoal-text-line-height-heading-l)", + "m": "var(--charcoal-text-line-height-heading-m)", + "s": "var(--charcoal-text-line-height-heading-s)", + "xl": "var(--charcoal-text-line-height-heading-xl)", + "xs": "var(--charcoal-text-line-height-heading-xs)", + "xxl": "var(--charcoal-text-line-height-heading-xxl)", + "xxs": "var(--charcoal-text-line-height-heading-xxs)", + "xxxl": "var(--charcoal-text-line-height-heading-xxxl)", + "xxxs": "var(--charcoal-text-line-height-heading-xxxs)", + }, + "paragraph": "var(--charcoal-text-line-height-paragraph)", + }, + }, +} +`; + +exports[`createCSSTokenObject test: light theme > should match snapshot 1`] = ` +{ + "border-width": { + "focus": { + "1": "var(--charcoal-border-width-focus-1)", + "2": "var(--charcoal-border-width-focus-2)", + }, + "l": "var(--charcoal-border-width-l)", + "m": "var(--charcoal-border-width-m)", + }, + "color": { + "background": { + "default": "var(--charcoal-color-background-default)", + "secondary": "var(--charcoal-color-background-secondary)", + "tertiary": "var(--charcoal-color-background-tertiary)", + }, + "border": { + "default": "var(--charcoal-color-border-default)", + "disable": "var(--charcoal-color-border-disable)", + "focus": { + "1": "var(--charcoal-color-border-focus-1)", + "2": "var(--charcoal-color-border-focus-2)", + "legacy": "var(--charcoal-color-border-focus-legacy)", + }, + "hover": "var(--charcoal-color-border-hover)", + "hud": "var(--charcoal-color-border-hud)", + "negative": "var(--charcoal-color-border-negative)", + "press": "var(--charcoal-color-border-press)", + "secondary": "var(--charcoal-color-border-secondary)", + "selected": "var(--charcoal-color-border-selected)", + }, + "container": { + "default": "var(--charcoal-color-container-default)", + "default-a": "var(--charcoal-color-container-default-a)", + "disable": "var(--charcoal-color-container-disable)", + "discovery": { + "default": "var(--charcoal-color-container-discovery-default)", + "hover": "var(--charcoal-color-container-discovery-hover)", + "press": "var(--charcoal-color-container-discovery-press)", + }, + "hover": "var(--charcoal-color-container-hover)", + "hover-a": "var(--charcoal-color-container-hover-a)", + "hud": { + "default": "var(--charcoal-color-container-hud-default)", + "hover": "var(--charcoal-color-container-hud-hover)", + "press": "var(--charcoal-color-container-hud-press)", + }, + "negative": { + "default": "var(--charcoal-color-container-negative-default)", + "hover": "var(--charcoal-color-container-negative-hover)", + "press": "var(--charcoal-color-container-negative-press)", + }, + "neutral": { + "default": "var(--charcoal-color-container-neutral-default)", + "hover": "var(--charcoal-color-container-neutral-hover)", + "press": "var(--charcoal-color-container-neutral-press)", + }, + "notice": { + "default": "var(--charcoal-color-container-notice-default)", + "hover": "var(--charcoal-color-container-notice-hover)", + "press": "var(--charcoal-color-container-notice-press)", + }, + "on-img": { + "default": "var(--charcoal-color-container-on-img-default)", + "hover": "var(--charcoal-color-container-on-img-hover)", + "press": "var(--charcoal-color-container-on-img-press)", + }, + "positive": { + "default": "var(--charcoal-color-container-positive-default)", + "hover": "var(--charcoal-color-container-positive-hover)", + "press": "var(--charcoal-color-container-positive-press)", + }, + "press": "var(--charcoal-color-container-press)", + "press-a": "var(--charcoal-color-container-press-a)", + "primary": { + "default": "var(--charcoal-color-container-primary-default)", + "hover": "var(--charcoal-color-container-primary-hover)", + "press": "var(--charcoal-color-container-primary-press)", + }, + "secondary": { + "default": "var(--charcoal-color-container-secondary-default)", + "default-a": "var(--charcoal-color-container-secondary-default-a)", + "hover": "var(--charcoal-color-container-secondary-hover)", + "hover-a": "var(--charcoal-color-container-secondary-hover-a)", + "press": "var(--charcoal-color-container-secondary-press)", + "press-a": "var(--charcoal-color-container-secondary-press-a)", + }, + "skeleton": "var(--charcoal-color-container-skeleton)", + "subtle": "var(--charcoal-color-container-subtle)", + "tertiary": { + "default": "var(--charcoal-color-container-tertiary-default)", + "default-a": "var(--charcoal-color-container-tertiary-default-a)", + "hover": "var(--charcoal-color-container-tertiary-hover)", + "hover-a": "var(--charcoal-color-container-tertiary-hover-a)", + "press": "var(--charcoal-color-container-tertiary-press)", + "pressA": "var(--charcoal-color-container-tertiary-press-a)", + }, + }, + "icon": { + "default": "var(--charcoal-color-icon-default)", + "disable": "var(--charcoal-color-icon-disable)", + "hover": "var(--charcoal-color-icon-hover)", + "negative": { + "default": "var(--charcoal-color-icon-negative-default)", + "hover": "var(--charcoal-color-icon-negative-hover)", + "press": "var(--charcoal-color-icon-negative-press)", + }, + "notice": { + "default": "var(--charcoal-color-icon-notice-default)", + "hover": "var(--charcoal-color-icon-notice-hover)", + "press": "var(--charcoal-color-icon-notice-press)", + }, + "on-negative": { + "default": "var(--charcoal-color-icon-on-negative-default)", + "hover": "var(--charcoal-color-icon-on-negative-hover)", + "press": "var(--charcoal-color-icon-on-negative-press)", + }, + "on-neutral": { + "default": "var(--charcoal-color-icon-on-neutral-default)", + "hover": "var(--charcoal-color-icon-on-neutral-hover)", + "press": "var(--charcoal-color-icon-on-neutral-press)", + }, + "on-notice": { + "default": "var(--charcoal-color-icon-on-notice-default)", + "hover": "var(--charcoal-color-icon-on-notice-hover)", + "press": "var(--charcoal-color-icon-on-notice-press)", + }, + "on-on-img": { + "default": "var(--charcoal-color-icon-on-on-img-default)", + "hover": "var(--charcoal-color-icon-on-on-img-hover)", + "press": "var(--charcoal-color-icon-on-on-img-press)", + }, + "on-positive": { + "default": "var(--charcoal-color-icon-on-positive-default)", + "hover": "var(--charcoal-color-icon-on-positive-hover)", + "press": "var(--charcoal-color-icon-on-positive-press)", + }, + "on-primary": { + "default": "var(--charcoal-color-icon-on-primary-default)", + "hover": "var(--charcoal-color-icon-on-primary-hover)", + "press": "var(--charcoal-color-icon-on-primary-press)", + }, + "positive": { + "default": "var(--charcoal-color-icon-positive-default)", + "hover": "var(--charcoal-color-icon-positive-hover)", + "press": "var(--charcoal-color-icon-positive-press)", + }, + "press": "var(--charcoal-color-icon-press)", + "secondary": { + "default": "var(--charcoal-color-icon-secondary-default)", + "hover": "var(--charcoal-color-icon-secondary-hover)", + "press": "var(--charcoal-color-icon-secondary-press)", + }, + "tertiary": { + "default": "var(--charcoal-color-icon-tertiary-default)", + "hover": "var(--charcoal-color-icon-tertiary-hover)", + "press": "var(--charcoal-color-icon-tertiary-press)", + }, + }, + "text": { + "brand-premium": { + "default": "var(--charcoal-color-text-brand-premium-default)", + "hover": "var(--charcoal-color-text-brand-premium-hover)", + "press": "var(--charcoal-color-text-brand-premium-press)", + }, + "default": "var(--charcoal-color-text-default)", + "disable": "var(--charcoal-color-text-disable)", + "hover": "var(--charcoal-color-text-hover)", + "info": { + "default": "var(--charcoal-color-text-info-default)", + "hover": "var(--charcoal-color-text-info-hover)", + "press": "var(--charcoal-color-text-info-press)", + }, + "negative": { + "default": "var(--charcoal-color-text-negative-default)", + "hover": "var(--charcoal-color-text-negative-hover)", + "press": "var(--charcoal-color-text-negative-press)", + }, + "notice": { + "default": "var(--charcoal-color-text-notice-default)", + "hover": "var(--charcoal-color-text-notice-hover)", + "press": "var(--charcoal-color-text-notice-press)", + }, + "on-discovery": { + "default": "var(--charcoal-color-text-on-discovery-default)", + "hover": "var(--charcoal-color-text-on-discovery-hover)", + "press": "var(--charcoal-color-text-on-discovery-press)", + }, + "on-hud": { + "default": "var(--charcoal-color-text-on-hud-default)", + "hover": "var(--charcoal-color-text-on-hud-hover)", + "press": "var(--charcoal-color-text-on-hud-press)", + }, + "on-negative": { + "default": "var(--charcoal-color-text-on-negative-default)", + "hover": "var(--charcoal-color-text-on-negative-hover)", + "press": "var(--charcoal-color-text-on-negative-press)", + }, + "on-notice": { + "default": "var(--charcoal-color-text-on-notice-default)", + "hover": "var(--charcoal-color-text-on-notice-hover)", + "press": "var(--charcoal-color-text-on-notice-press)", + }, + "on-on-img": { + "default": "var(--charcoal-color-text-on-on-img-default)", + "hover": "var(--charcoal-color-text-on-on-img-hover)", + "press": "var(--charcoal-color-text-on-on-img-press)", + }, + "on-positive": { + "default": "var(--charcoal-color-text-on-positive-default)", + "hover": "var(--charcoal-color-text-on-positive-hover)", + "press": "var(--charcoal-color-text-on-positive-press)", + }, + "on-primary": { + "default": "var(--charcoal-color-text-on-primary-default)", + "hover": "var(--charcoal-color-text-on-primary-hover)", + "press": "var(--charcoal-color-text-on-primary-press)", + }, + "placeholder": { + "default": "var(--charcoal-color-text-placeholder-default)", + "hover": "var(--charcoal-color-text-placeholder-hover)", + "press": "var(--charcoal-color-text-placeholder-press)", + }, + "positive": { + "default": "var(--charcoal-color-text-positive-default)", + "hover": "var(--charcoal-color-text-positive-hover)", + "press": "var(--charcoal-color-text-positive-press)", + }, + "press": "var(--charcoal-color-text-press)", + "secondary": { + "default": "var(--charcoal-color-text-secondary-default)", + "hover": "var(--charcoal-color-text-secondary-hover)", + "press": "var(--charcoal-color-text-secondary-press)", + }, + "tertiary": { + "default": "var(--charcoal-color-text-tertiary-default)", + "hover": "var(--charcoal-color-text-tertiary-hover)", + "press": "var(--charcoal-color-text-tertiary-press)", + }, + "visited": { + "default": "var(--charcoal-color-text-visited-default)", + "hover": "var(--charcoal-color-text-visited-hover)", + "press": "var(--charcoal-color-text-visited-press)", + }, + }, + }, + "paragraph-width": { + "l": "var(--charcoal-paragraph-width-l)", + "l-compact": "var(--charcoal-paragraph-width-l-compact)", + "l-cozy": "var(--charcoal-paragraph-width-l-cozy)", + "m": "var(--charcoal-paragraph-width-m)", + "m-compact": "var(--charcoal-paragraph-width-m-compact)", + "m-cozy": "var(--charcoal-paragraph-width-m-cozy)", + "s": "var(--charcoal-paragraph-width-s)", + "s-compact": "var(--charcoal-paragraph-width-s-compact)", + "s-cozy": "var(--charcoal-paragraph-width-s-cozy)", + }, + "radius": { + "0": "var(--charcoal-radius-0)", + "l": "var(--charcoal-radius-l)", + "m": "var(--charcoal-radius-m)", + "oval": "var(--charcoal-radius-oval)", + "s": "var(--charcoal-radius-s)", + "xl": "var(--charcoal-radius-xl)", + "xs": "var(--charcoal-radius-xs)", + "xxl": "var(--charcoal-radius-xxl)", + }, + "space": { + "between-checkboxes": { + "horizontal": "var(--charcoal-space-between-checkboxes-horizontal)", + "vertical": "var(--charcoal-space-between-checkboxes-vertical)", + }, + "layout": { + "0": "var(--charcoal-space-layout-0)", + "10": "var(--charcoal-space-layout-10)", + "100": "var(--charcoal-space-layout-100)", + "20": "var(--charcoal-space-layout-20)", + "25": "var(--charcoal-space-layout-25)", + "30": "var(--charcoal-space-layout-30)", + "40": "var(--charcoal-space-layout-40)", + "50": "var(--charcoal-space-layout-50)", + "60": "var(--charcoal-space-layout-60)", + "70": "var(--charcoal-space-layout-70)", + "80": "var(--charcoal-space-layout-80)", + "90": "var(--charcoal-space-layout-90)", + }, + "target": { + "l": "var(--charcoal-space-target-l)", + "m": "var(--charcoal-space-target-m)", + "s": "var(--charcoal-space-target-s)", + "xs": "var(--charcoal-space-target-xs)", + }, + }, + "text": { + "font-size": { + "body": "var(--charcoal-text-font-size-body)", + "caption": { + "m": "var(--charcoal-text-font-size-caption-m)", + "s": "var(--charcoal-text-font-size-caption-s)", + }, + "heading": { + "l": "var(--charcoal-text-font-size-heading-l)", + "m": "var(--charcoal-text-font-size-heading-m)", + "s": "var(--charcoal-text-font-size-heading-s)", + "xl": "var(--charcoal-text-font-size-heading-xl)", + "xs": "var(--charcoal-text-font-size-heading-xs)", + "xxl": "var(--charcoal-text-font-size-heading-xxl)", + "xxs": "var(--charcoal-text-font-size-heading-xxs)", + "xxxl": "var(--charcoal-text-font-size-heading-xxxl)", + "xxxs": "var(--charcoal-text-font-size-heading-xxxs)", + }, + "paragraph": "var(--charcoal-text-font-size-paragraph)", + }, + "font-weight": { + "bold": "var(--charcoal-text-font-weight-bold)", + "regular": "var(--charcoal-text-font-weight-regular)", + }, + "line-height": { + "body": "var(--charcoal-text-line-height-body)", + "caption": { + "m": "var(--charcoal-text-line-height-caption-m)", + "s": "var(--charcoal-text-line-height-caption-s)", + }, + "heading": { + "l": "var(--charcoal-text-line-height-heading-l)", + "m": "var(--charcoal-text-line-height-heading-m)", + "s": "var(--charcoal-text-line-height-heading-s)", + "xl": "var(--charcoal-text-line-height-heading-xl)", + "xs": "var(--charcoal-text-line-height-heading-xs)", + "xxl": "var(--charcoal-text-line-height-heading-xxl)", + "xxs": "var(--charcoal-text-line-height-heading-xxs)", + "xxxl": "var(--charcoal-text-line-height-heading-xxxl)", + "xxxs": "var(--charcoal-text-line-height-heading-xxxs)", + }, + "paragraph": "var(--charcoal-text-line-height-paragraph)", + }, + }, +} +`; + exports[`createTokenObject test: dark theme > should match snapshot 1`] = ` { "border-width": { diff --git a/packages/theme/src/token-object/index.ts b/packages/theme/src/token-object/index.ts index 33eb02f00..999300f4e 100644 --- a/packages/theme/src/token-object/index.ts +++ b/packages/theme/src/token-object/index.ts @@ -2,6 +2,7 @@ import { createReferenceTokenResolver } from './reference-token' import { toTokenObject } from './to-token-object' import type { TokenObject, TokenDictionary, TokenValue } from './types' +import { kebabCase } from 'change-case-all' export { camelCaseKeys } from './helpers/changecase-keys' export const createTokenObject = ( @@ -30,3 +31,30 @@ export const createTokenObject = ( return result } + +export const createCSSTokenObject = ( + tokenDictionary: T, + format: (value: string) => string = (value) => value +): { [K in keyof T]: TokenObject } => { + const result = {} as { [K in keyof T]: TokenObject } + + for (const category in tokenDictionary) { + const value = tokenDictionary[category] + + // category ごとに template を展開していく + const resolvedTokens = Object.fromEntries( + Object.entries(value).map(([key]) => [ + key, + { + value: `var(--${format( + [category, ...key.split('/')].map((x) => kebabCase(x)).join('-') + )})`, + } satisfies TokenValue, + ]) + ) as typeof value + + result[category] = toTokenObject(resolvedTokens) + } + + return result +} diff --git a/packages/theme/src/token-object/token-object.bench.ts b/packages/theme/src/token-object/token-object.bench.ts index c73fb7413..d3cd4904e 100644 --- a/packages/theme/src/token-object/token-object.bench.ts +++ b/packages/theme/src/token-object/token-object.bench.ts @@ -1,4 +1,4 @@ -import { camelCaseKeys, createTokenObject } from '.' +import { camelCaseKeys, createCSSTokenObject, createTokenObject } from '.' import lightToken from '../json/pixiv-light.json' import darkToken from '../json/pixiv-dark.json' import baseToken from '../json/base.json' @@ -16,4 +16,8 @@ describe.each([ bench('benchmarks token object creation with camelCase formatting', () => { camelCaseKeys(createTokenObject(token, baseToken)) }) + + bench('benchmarks css token object creation', () => { + createCSSTokenObject(token, (value) => `charcoal-${value}`) + }) }) diff --git a/packages/theme/src/token-object/token-object.test.ts b/packages/theme/src/token-object/token-object.test.ts index e174a52c4..e70a7b61b 100644 --- a/packages/theme/src/token-object/token-object.test.ts +++ b/packages/theme/src/token-object/token-object.test.ts @@ -1,4 +1,4 @@ -import { createTokenObject } from '.' +import { createCSSTokenObject, createTokenObject } from '.' import { toTokenObject } from './to-token-object' import { createReferenceTokenResolver } from './reference-token' import lightToken from '../json/pixiv-light.json' @@ -6,6 +6,17 @@ import darkToken from '../json/pixiv-dark.json' import baseToken from '../json/base.json' import deepmerge from 'deepmerge' import { Tokens } from './types' +import { readFile } from 'fs/promises' +import { join } from 'node:path' +import { parse } from 'css' + +const isObject = (value: unknown): value is Record => { + if (value instanceof RegExp) return false + if (value instanceof Date) return false + if (value instanceof Error) return false + + return typeof value === 'object' && value !== null +} describe.each([ ['light theme', lightToken], @@ -41,6 +52,66 @@ describe.each([ }) }) +describe.each([ + ['light theme', lightToken, '../css/_variables_light.css'], + ['dark theme', darkToken, '../css/_variables_dark.css'], +] as const)( + 'createCSSTokenObject test: %s', + async (description, token, cssFilePath) => { + const theme = createCSSTokenObject(token, (value) => `charcoal-${value}`) + const keys = Object.keys(token) + const css = await readFile(join(__dirname, cssFilePath), 'utf-8') + + // スナップショット + it('should match snapshot', () => { + expect(theme).toMatchSnapshot() + }) + + const cssVariables = new Map() + for (const rule of parse(css).stylesheet?.rules ?? []) { + if (rule.type !== 'rule') continue + + for (const declaration of rule.declarations ?? []) { + if ( + declaration.type !== 'declaration' || + declaration.property?.startsWith('--charcoal') !== true + ) + continue + cssVariables.set(declaration.property, declaration.value ?? '') + } + } + + describe.each(keys)(`[${description}] Category: %s`, (category) => { + const _category = category as keyof typeof token + const tokens = token[_category] + + it.each(Object.keys(tokens))( + `[${description}] ${category} should have %s`, + (key) => { + const splitted = key.split('/') + + expect(theme).toHaveProperty( + [_category, ...splitted], + expect.any(String) + ) + const variable = splitted.reduce((acc, key) => { + if (typeof acc === 'string') return acc + + const next = acc[key] + if (typeof next === 'string') return next + + return isObject(next) ? next : acc + }, theme[_category] as Record | string) + + expect( + Array.from(cssVariables.keys()).map((x) => `var(${x})`) + ).toContain(variable) + } + ) + }) + } +) + describe('toTokenObject test', () => { const tokens: Tokens = { 'color/background/default': { value: '#ffffff' }, diff --git a/yarn.lock b/yarn.lock index 21396c436..8c6f1163e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2224,8 +2224,10 @@ __metadata: dependencies: "@charcoal-ui/foundation": ^4.0.0-beta.15 "@charcoal-ui/utils": ^4.0.0-beta.15 + "@types/css": ^0.0.38 change-case-all: ^2.1.0 cpx: ^1.5.0 + css: ^3.0.0 deepmerge: ^4.3.1 npm-run-all: ^4.1.5 polished: ^4.1.4 @@ -8909,6 +8911,13 @@ __metadata: languageName: node linkType: hard +"@types/css@npm:^0.0.38": + version: 0.0.38 + resolution: "@types/css@npm:0.0.38" + checksum: c2275227be4af587485a0c1749601b0d96f13d8d430bff19c83242d39f17a764f9ea78c019b6e3b7372f43a35a3245518ad15fb57fdf3fada5f99eb40283c8d7 + languageName: node + linkType: hard + "@types/debug@npm:^4.0.0": version: 4.1.12 resolution: "@types/debug@npm:4.1.12" From 9d8abfe381b90b631d11924a9976577c0e8d3fc7 Mon Sep 17 00:00:00 2001 From: naporin0624 Date: Thu, 24 Oct 2024 20:09:23 +0000 Subject: [PATCH 12/15] chore(theme): change output dir --- packages/theme/cli/token-object.ts | 12 ++++-------- packages/theme/package.json | 4 ++-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/theme/cli/token-object.ts b/packages/theme/cli/token-object.ts index 82942a16d..0e8e9bf37 100644 --- a/packages/theme/cli/token-object.ts +++ b/packages/theme/cli/token-object.ts @@ -12,14 +12,14 @@ const args = parseArgs({ token: { type: 'string' }, base: { type: 'string' }, output: { type: 'string' }, - format: { type: 'string', short: 'f', default: 'kebab' }, + format: { type: 'string', short: 'f' }, }, }) const tokenFilePath = args.values.token const baseFilePath = args.values.base const outputFilePath = args.values.output -const format = args.values.format ?? 'camel' +const format = args.values.format if ( tokenFilePath === undefined || @@ -41,10 +41,6 @@ const baseJson = JSON.parse( // トークンオブジェクトの生成 let tokenObject = {} switch (format) { - case 'kebab': { - tokenObject = createTokenObject(tokenJson, baseJson) - break - } case 'camel': { tokenObject = camelCaseKeys(createTokenObject(tokenJson, baseJson)) break @@ -54,8 +50,8 @@ switch (format) { break } default: { - console.error(`Error: Unknown format ${format}`) - process.exit(1) + tokenObject = createTokenObject(tokenJson, baseJson) + break } } diff --git a/packages/theme/package.json b/packages/theme/package.json index 19e675ce0..89090f095 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -37,12 +37,12 @@ "bench": "vitest bench --passWithNoTests", "token-object": "npm-run-all --print-label --parallel 'token-object:*'", "token-object:light": "npm-run-all --print-label --parallel 'token-object:light:*'", + "token-object:light:default": "tsx cli/token-object --token ./src/json/pixiv-light.json --base ./src/json/base.json --output ./dist/json/pixiv-light.json", "token-object:light:camel": "tsx cli/token-object --token ./src/json/pixiv-light.json --base ./src/json/base.json --output ./dist/json/camel/pixiv-light.json --format camel", - "token-object:light:kebab": "tsx cli/token-object --token ./src/json/pixiv-light.json --base ./src/json/base.json --output ./dist/json/kebab/pixiv-light.json --format kebab", "token-object:light:css": "tsx cli/token-object --token ./src/json/pixiv-light.json --base ./src/json/base.json --output ./dist/json/css/pixiv-light.json --format css", "token-object:dark": "npm-run-all --print-label --parallel 'token-object:dark:*'", + "token-object:dark:default": "tsx cli/token-object --token ./src/json/pixiv-dark.json --base ./src/json/base.json --output ./dist/json/pixiv-dark.json", "token-object:dark:camel": "tsx cli/token-object --token ./src/json/pixiv-dark.json --base ./src/json/base.json --output ./dist/json/camel/pixiv-dark.json --format camel", - "token-object:dark:kebab": "tsx cli/token-object --token ./src/json/pixiv-dark.json --base ./src/json/base.json --output ./dist/json/kebab/pixiv-dark.json --format kebab", "token-object:dark:css": "tsx cli/token-object --token ./src/json/pixiv-dark.json --base ./src/json/base.json --output ./dist/json/css/pixiv-dark.json --format css" }, "devDependencies": { From 66e33c63916a1254f808ad6f532975c77d43ba57 Mon Sep 17 00:00:00 2001 From: naporin0624 Date: Fri, 25 Oct 2024 14:06:57 +0000 Subject: [PATCH 13/15] =?UTF-8?q?chore(theme):=20=E4=B8=80=E6=8B=AC?= =?UTF-8?q?=E3=81=A7=E7=94=9F=E6=88=90=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/theme/cli/token-object.ts | 164 ++++++++++++++++++++--------- packages/theme/package.json | 10 +- 2 files changed, 115 insertions(+), 59 deletions(-) diff --git a/packages/theme/cli/token-object.ts b/packages/theme/cli/token-object.ts index 0e8e9bf37..16fcf88c7 100644 --- a/packages/theme/cli/token-object.ts +++ b/packages/theme/cli/token-object.ts @@ -1,64 +1,128 @@ /* eslint-disable no-console */ -import { mkdirSync, readFileSync, writeFileSync } from 'fs' -import path from 'path' +import { readFileSync, mkdirSync, writeFileSync } from 'node:fs' +import path from 'node:path' import { createCSSTokenObject, createTokenObject } from '../src/token-object' import type { TokenDictionary } from '../src/token-object/types' -import { parseArgs } from 'node:util' import { camelCaseKeys } from '../src/token-object' -// コマンドライン引数の解析 -const args = parseArgs({ - options: { - token: { type: 'string' }, - base: { type: 'string' }, - output: { type: 'string' }, - format: { type: 'string', short: 'f' }, +type ValueStyle = 'cssVariable' +type KeyStyle = 'camelCase' + +type Config = { + tokenFile: string + baseFile: string + outputFile: string + keyStyle?: KeyStyle + valueStyle?: ValueStyle +} + +const configurations: Config[] = [ + { + tokenFile: './src/json/pixiv-light.json', + baseFile: './src/json/base.json', + outputFile: './dist/tokens/pixiv-light.json', + keyStyle: undefined, + valueStyle: undefined, + }, + { + tokenFile: './src/json/pixiv-dark.json', + baseFile: './src/json/base.json', + outputFile: './dist/tokens/pixiv-dark.json', + keyStyle: undefined, + valueStyle: undefined, }, -}) -const tokenFilePath = args.values.token -const baseFilePath = args.values.base -const outputFilePath = args.values.output -const format = args.values.format + { + tokenFile: './src/json/pixiv-light.json', + baseFile: './src/json/base.json', + outputFile: './dist/tokens/camel/pixiv-light.json', + keyStyle: 'camelCase', + valueStyle: undefined, + }, + { + tokenFile: './src/json/pixiv-dark.json', + baseFile: './src/json/base.json', + outputFile: './dist/tokens/camel/pixiv-dark.json', + keyStyle: 'camelCase', + valueStyle: undefined, + }, -if ( - tokenFilePath === undefined || - baseFilePath === undefined || - outputFilePath === undefined -) { - console.error('Error: --token, --base, and --output options are required.') - process.exit(1) -} + { + tokenFile: './src/json/pixiv-light.json', + baseFile: './src/json/base.json', + outputFile: './dist/tokens/css-variables/pixiv-light.json', + keyStyle: undefined, + valueStyle: 'cssVariable', + }, + { + tokenFile: './src/json/pixiv-dark.json', + baseFile: './src/json/base.json', + outputFile: './dist/tokens/css-variables/pixiv-dark.json', + keyStyle: undefined, + valueStyle: 'cssVariable', + }, -// ファイルの読み込み -const tokenJson = JSON.parse( - readFileSync(path.resolve(tokenFilePath), 'utf8') -) as TokenDictionary -const baseJson = JSON.parse( - readFileSync(path.resolve(baseFilePath), 'utf8') -) as TokenDictionary + { + tokenFile: './src/json/pixiv-light.json', + baseFile: './src/json/base.json', + outputFile: './dist/tokens/css-variables/camel/pixiv-light.json', + keyStyle: 'camelCase', + valueStyle: 'cssVariable', + }, + { + tokenFile: './src/json/pixiv-dark.json', + baseFile: './src/json/base.json', + outputFile: './dist/tokens/css-variables/camel/pixiv-dark.json', + keyStyle: 'camelCase', + valueStyle: 'cssVariable', + }, +] -// トークンオブジェクトの生成 -let tokenObject = {} -switch (format) { - case 'camel': { - tokenObject = camelCaseKeys(createTokenObject(tokenJson, baseJson)) - break - } - case 'css': { - tokenObject = createCSSTokenObject(tokenJson, (x) => `charcoal-${x}`) - break +for (const { + tokenFile, + baseFile, + outputFile, + keyStyle, + valueStyle, +} of configurations) { + const baseJson = JSON.parse( + readFileSync(path.resolve(baseFile), 'utf8') + ) as TokenDictionary + + const createToken = ( + value: T + ): Record => { + switch (valueStyle) { + case 'cssVariable': { + return createCSSTokenObject(value, (x) => `charcoal-${x}`) + } + default: { + return createTokenObject(value, baseJson) + } + } } - default: { - tokenObject = createTokenObject(tokenJson, baseJson) - break + const transformKey = >( + value: T + ): Record => { + switch (keyStyle) { + case 'camelCase': { + return camelCaseKeys(value) + } + default: { + return value + } + } } -} -mkdirSync(path.dirname(outputFilePath), { recursive: true }) -writeFileSync( - path.resolve(outputFilePath), - JSON.stringify(tokenObject, null, 2) -) + const tokenJson = JSON.parse( + readFileSync(path.resolve(tokenFile), 'utf8') + ) as TokenDictionary + const tokenObject = transformKey(createToken(tokenJson)) -console.log(`Token object generated and saved to ${outputFilePath}`) + mkdirSync(path.dirname(outputFile), { recursive: true }) + writeFileSync(path.resolve(outputFile), JSON.stringify(tokenObject, null, 2)) + + console.log( + `Generated ${outputFile} with keyStyle ${keyStyle} and valueStyle ${valueStyle}.` + ) +} diff --git a/packages/theme/package.json b/packages/theme/package.json index 89090f095..002c5c6aa 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -35,15 +35,7 @@ "clean": "rimraf dist .tsbuildinfo", "test": "vitest run --passWithNoTests", "bench": "vitest bench --passWithNoTests", - "token-object": "npm-run-all --print-label --parallel 'token-object:*'", - "token-object:light": "npm-run-all --print-label --parallel 'token-object:light:*'", - "token-object:light:default": "tsx cli/token-object --token ./src/json/pixiv-light.json --base ./src/json/base.json --output ./dist/json/pixiv-light.json", - "token-object:light:camel": "tsx cli/token-object --token ./src/json/pixiv-light.json --base ./src/json/base.json --output ./dist/json/camel/pixiv-light.json --format camel", - "token-object:light:css": "tsx cli/token-object --token ./src/json/pixiv-light.json --base ./src/json/base.json --output ./dist/json/css/pixiv-light.json --format css", - "token-object:dark": "npm-run-all --print-label --parallel 'token-object:dark:*'", - "token-object:dark:default": "tsx cli/token-object --token ./src/json/pixiv-dark.json --base ./src/json/base.json --output ./dist/json/pixiv-dark.json", - "token-object:dark:camel": "tsx cli/token-object --token ./src/json/pixiv-dark.json --base ./src/json/base.json --output ./dist/json/camel/pixiv-dark.json --format camel", - "token-object:dark:css": "tsx cli/token-object --token ./src/json/pixiv-dark.json --base ./src/json/base.json --output ./dist/json/css/pixiv-dark.json --format css" + "token-object": "tsx cli/token-object.ts" }, "devDependencies": { "@types/css": "^0.0.38", From 16eadc31ffda0dbce8674423cf0045d73b367743 Mon Sep 17 00:00:00 2001 From: naporin0624 Date: Fri, 25 Oct 2024 14:07:27 +0000 Subject: [PATCH 14/15] =?UTF-8?q?chore(theme):=20subpath=20import=20?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/theme/package.json | 17 ++++++++++++++++- packages/theme/src/css/_variables_dark.css.d.ts | 1 + .../theme/src/css/_variables_light.css.d.ts | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 packages/theme/src/css/_variables_dark.css.d.ts create mode 100644 packages/theme/src/css/_variables_light.css.d.ts diff --git a/packages/theme/package.json b/packages/theme/package.json index 002c5c6aa..d3e6506ad 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -17,6 +17,21 @@ "require": "./dist/token-object/index.cjs.js", "import": "./dist/token-object/index.esm.js", "default": "./dist/token-object/index.esm.js" + }, + "./css/*": "./dist/css/*", + "./tokens/*": "./dist/tokens/*" + }, + "typesVersions": { + "*": { + "token-object": [ + "./dist/token-object/index.d.ts" + ], + "tokens/*": [ + "./dist/tokens/*" + ], + "css/*": [ + "./dist/css/*" + ] } }, "sideEffects": [ @@ -26,7 +41,7 @@ "build": "npm-run-all --print-label --parallel 'build:*' --sequential serialize", "build:bundle": "FORCE_COLOR=1 tsup", "build:dts": "tsc --project tsconfig.build.json --pretty --emitDeclarationOnly", - "build:copy-css": "cpx 'src/css/**/*.css' dist/css", + "build:copy-css": "cpx 'src/css/**' dist/css", "build:token-object": "npm-run-all token-object", "serialize": "node cli/index.js", "typecheck": "run-p --print-label 'typecheck:*'", diff --git a/packages/theme/src/css/_variables_dark.css.d.ts b/packages/theme/src/css/_variables_dark.css.d.ts new file mode 100644 index 000000000..22db8578f --- /dev/null +++ b/packages/theme/src/css/_variables_dark.css.d.ts @@ -0,0 +1 @@ +declare module "*.css" { } \ No newline at end of file diff --git a/packages/theme/src/css/_variables_light.css.d.ts b/packages/theme/src/css/_variables_light.css.d.ts new file mode 100644 index 000000000..22db8578f --- /dev/null +++ b/packages/theme/src/css/_variables_light.css.d.ts @@ -0,0 +1 @@ +declare module "*.css" { } \ No newline at end of file From 001e9097d9be93d982e76afcd3668d664758d29c Mon Sep 17 00:00:00 2001 From: naporin0624 Date: Fri, 25 Oct 2024 16:11:13 +0000 Subject: [PATCH 15/15] chore(theme): fix build --- packages/theme/package.json | 9 ++++----- packages/theme/tsconfig.build.json | 3 ++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/theme/package.json b/packages/theme/package.json index d3e6506ad..bf255bcba 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -38,19 +38,18 @@ "*.css" ], "scripts": { - "build": "npm-run-all --print-label --parallel 'build:*' --sequential serialize", + "build": "npm-run-all --print-label -s 'build:*' --sequential serialize", "build:bundle": "FORCE_COLOR=1 tsup", "build:dts": "tsc --project tsconfig.build.json --pretty --emitDeclarationOnly", - "build:copy-css": "cpx 'src/css/**' dist/css", - "build:token-object": "npm-run-all token-object", + "build:copy-css": "cpx --clean 'src/css/**/*.{css,d.ts}' dist/css", + "build:token-object": "tsx cli/token-object.ts", "serialize": "node cli/index.js", "typecheck": "run-p --print-label 'typecheck:*'", "typecheck:main": "tsc --project tsconfig.build.json --pretty --noEmit", "typecheck:cli": "tsc --project cli/tsconfig.build.json --noEmit", "clean": "rimraf dist .tsbuildinfo", "test": "vitest run --passWithNoTests", - "bench": "vitest bench --passWithNoTests", - "token-object": "tsx cli/token-object.ts" + "bench": "vitest bench --passWithNoTests" }, "devDependencies": { "@types/css": "^0.0.38", diff --git a/packages/theme/tsconfig.build.json b/packages/theme/tsconfig.build.json index 49408884f..079dfe994 100644 --- a/packages/theme/tsconfig.build.json +++ b/packages/theme/tsconfig.build.json @@ -6,5 +6,6 @@ "outDir": "./dist", "tsBuildInfoFile": "./.tsbuildinfo" }, - "include": ["./src"] + "include": ["./src"], + "exclude": ["./src/docs/*"] }