@tamagui/create-theme 2.7.7 → 3.0.0-beta.1097.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/cjs/createThemes.cjs +99 -0
  2. package/dist/cjs/createThemes.native.cjs +125 -0
  3. package/dist/cjs/createThemes.native.js +127 -0
  4. package/dist/cjs/createThemes.native.js.map +1 -0
  5. package/dist/cjs/index.cjs +9 -16
  6. package/dist/cjs/index.native.cjs +20 -0
  7. package/dist/cjs/index.native.js +10 -16
  8. package/dist/cjs/index.native.js.map +1 -1
  9. package/dist/esm/createThemes.mjs +79 -0
  10. package/dist/esm/createThemes.mjs.map +1 -0
  11. package/dist/esm/createThemes.native.js +103 -0
  12. package/dist/esm/createThemes.native.js.map +1 -0
  13. package/dist/esm/index.js +1 -7
  14. package/dist/esm/index.mjs +1 -7
  15. package/dist/esm/index.native.js +1 -7
  16. package/package.json +7 -7
  17. package/src/createThemes.tsx +324 -0
  18. package/src/index.tsx +1 -6
  19. package/types/createThemes.d.ts +122 -0
  20. package/types/createThemes.d.ts.map +11 -0
  21. package/types/index.d.ts +1 -6
  22. package/types/index.d.ts.map +2 -2
  23. package/dist/cjs/applyMask.cjs +0 -67
  24. package/dist/cjs/applyMask.native.js +0 -76
  25. package/dist/cjs/applyMask.native.js.map +0 -1
  26. package/dist/cjs/combineMasks.cjs +0 -48
  27. package/dist/cjs/combineMasks.native.js +0 -73
  28. package/dist/cjs/combineMasks.native.js.map +0 -1
  29. package/dist/cjs/createTheme.cjs +0 -105
  30. package/dist/cjs/createTheme.native.js +0 -111
  31. package/dist/cjs/createTheme.native.js.map +0 -1
  32. package/dist/cjs/createThemeTypes.cjs +0 -18
  33. package/dist/cjs/createThemeTypes.native.js +0 -21
  34. package/dist/cjs/createThemeTypes.native.js.map +0 -1
  35. package/dist/cjs/helpers.cjs +0 -36
  36. package/dist/cjs/helpers.native.js +0 -41
  37. package/dist/cjs/helpers.native.js.map +0 -1
  38. package/dist/cjs/isMinusZero.cjs +0 -30
  39. package/dist/cjs/isMinusZero.native.js +0 -33
  40. package/dist/cjs/isMinusZero.native.js.map +0 -1
  41. package/dist/cjs/masks.cjs +0 -146
  42. package/dist/cjs/masks.native.js +0 -192
  43. package/dist/cjs/masks.native.js.map +0 -1
  44. package/dist/cjs/tests.cjs +0 -26
  45. package/dist/cjs/tests.native.js +0 -29
  46. package/dist/cjs/tests.native.js.map +0 -1
  47. package/dist/cjs/themeInfo.cjs +0 -40
  48. package/dist/cjs/themeInfo.native.js +0 -43
  49. package/dist/cjs/themeInfo.native.js.map +0 -1
  50. package/dist/esm/applyMask.mjs +0 -41
  51. package/dist/esm/applyMask.mjs.map +0 -1
  52. package/dist/esm/applyMask.native.js +0 -47
  53. package/dist/esm/applyMask.native.js.map +0 -1
  54. package/dist/esm/combineMasks.mjs +0 -23
  55. package/dist/esm/combineMasks.mjs.map +0 -1
  56. package/dist/esm/combineMasks.native.js +0 -45
  57. package/dist/esm/combineMasks.native.js.map +0 -1
  58. package/dist/esm/createTheme.mjs +0 -78
  59. package/dist/esm/createTheme.mjs.map +0 -1
  60. package/dist/esm/createTheme.native.js +0 -81
  61. package/dist/esm/createTheme.native.js.map +0 -1
  62. package/dist/esm/createThemeTypes.mjs +0 -2
  63. package/dist/esm/createThemeTypes.mjs.map +0 -1
  64. package/dist/esm/createThemeTypes.native.js +0 -2
  65. package/dist/esm/createThemeTypes.native.js.map +0 -1
  66. package/dist/esm/helpers.mjs +0 -9
  67. package/dist/esm/helpers.mjs.map +0 -1
  68. package/dist/esm/helpers.native.js +0 -11
  69. package/dist/esm/helpers.native.js.map +0 -1
  70. package/dist/esm/index.js.map +0 -1
  71. package/dist/esm/index.mjs.map +0 -1
  72. package/dist/esm/index.native.js.map +0 -1
  73. package/dist/esm/isMinusZero.mjs +0 -5
  74. package/dist/esm/isMinusZero.mjs.map +0 -1
  75. package/dist/esm/isMinusZero.native.js +0 -5
  76. package/dist/esm/isMinusZero.native.js.map +0 -1
  77. package/dist/esm/masks.mjs +0 -114
  78. package/dist/esm/masks.mjs.map +0 -1
  79. package/dist/esm/masks.native.js +0 -157
  80. package/dist/esm/masks.native.js.map +0 -1
  81. package/dist/esm/tests.mjs +0 -27
  82. package/dist/esm/tests.mjs.map +0 -1
  83. package/dist/esm/tests.native.js +0 -27
  84. package/dist/esm/tests.native.js.map +0 -1
  85. package/dist/esm/themeInfo.mjs +0 -14
  86. package/dist/esm/themeInfo.mjs.map +0 -1
  87. package/dist/esm/themeInfo.native.js +0 -14
  88. package/dist/esm/themeInfo.native.js.map +0 -1
  89. package/src/applyMask.tsx +0 -69
  90. package/src/combineMasks.tsx +0 -29
  91. package/src/createTheme.tsx +0 -127
  92. package/src/createThemeTypes.tsx +0 -85
  93. package/src/helpers.tsx +0 -44
  94. package/src/isMinusZero.tsx +0 -3
  95. package/src/masks.tsx +0 -137
  96. package/src/tests.tsx +0 -28
  97. package/src/themeInfo.tsx +0 -36
  98. package/types/applyMask.d.ts +0 -8
  99. package/types/applyMask.d.ts.map +0 -11
  100. package/types/combineMasks.d.ts +0 -4
  101. package/types/combineMasks.d.ts.map +0 -11
  102. package/types/createTheme.d.ts +0 -28
  103. package/types/createTheme.d.ts.map +0 -11
  104. package/types/createThemeTypes.d.ts +0 -67
  105. package/types/createThemeTypes.d.ts.map +0 -11
  106. package/types/helpers.d.ts +0 -18
  107. package/types/helpers.d.ts.map +0 -11
  108. package/types/isMinusZero.d.ts +0 -3
  109. package/types/isMinusZero.d.ts.map +0 -11
  110. package/types/masks.d.ts +0 -15
  111. package/types/masks.d.ts.map +0 -11
  112. package/types/tests.d.ts +0 -3
  113. package/types/tests.d.ts.map +0 -11
  114. package/types/themeInfo.d.ts +0 -13
  115. package/types/themeInfo.d.ts.map +0 -11
@@ -0,0 +1,103 @@
1
+ function createThemes(tokens, tree, options) {
2
+ var rootKeys = Object.keys(tree).filter(function(key) {
3
+ return key !== "children";
4
+ });
5
+ if (rootKeys.length !== 2 || !("light" in tree) || !("dark" in tree)) {
6
+ throw new Error("createThemes tree must have exactly the roots \"light\" and \"dark\"");
7
+ }
8
+ var themes = {};
9
+ var deduped = /* @__PURE__ */ new Map();
10
+ var add = function(name, definition, parent, inheritedChildren) {
11
+ var _options_getTheme;
12
+ var resolvedDefinition = typeof definition === "function" ? definition({ parent }) : definition;
13
+ if (resolvedDefinition === null) return;
14
+ var { values, children, ...recipeFields } = resolvedDefinition;
15
+ var recipe = {
16
+ ...parent,
17
+ ...recipeFields
18
+ };
19
+ var unresolved = {
20
+ ...options === null || options === void 0 ? void 0 : (_options_getTheme = options.getTheme) === null || _options_getTheme === void 0 ? void 0 : _options_getTheme.call(options, {
21
+ recipe,
22
+ name,
23
+ tokens
24
+ }),
25
+ ...values
26
+ };
27
+ var resolved = {};
28
+ for (var key in unresolved) {
29
+ var value = unresolved[key];
30
+ var token = tokens.color[value];
31
+ if (token !== void 0) {
32
+ resolved[key] = token;
33
+ continue;
34
+ }
35
+ var isColor = value === "transparent" || /^#(?:[\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/i.test(value) || /^(?:rgb|rgba|hsl|hsla)\(\s*[^)]+\)$/i.test(value);
36
+ if (isColor) {
37
+ resolved[key] = value;
38
+ continue;
39
+ }
40
+ var nearest = "";
41
+ var nearestDistance = Number.POSITIVE_INFINITY;
42
+ for (var tokenName in tokens.color) {
43
+ var previous = Array.from({ length: tokenName.length + 1 }, function(_, index) {
44
+ return index;
45
+ });
46
+ for (var valueIndex = 0; valueIndex < value.length; valueIndex++) {
47
+ var diagonal = previous[0];
48
+ previous[0] = valueIndex + 1;
49
+ for (var tokenIndex = 0; tokenIndex < tokenName.length; tokenIndex++) {
50
+ var above = previous[tokenIndex + 1];
51
+ previous[tokenIndex + 1] = Math.min(previous[tokenIndex] + 1, above + 1, diagonal + (value[valueIndex] === tokenName[tokenIndex] ? 0 : 1));
52
+ diagonal = above;
53
+ }
54
+ }
55
+ var distance = previous[tokenName.length];
56
+ if (distance < nearestDistance) {
57
+ nearest = tokenName;
58
+ nearestDistance = distance;
59
+ }
60
+ }
61
+ throw new Error(`Invalid color value "${value}" for theme "${name}" key "${key}".${nearest ? ` Did you mean token "${nearest}"?` : ""}`);
62
+ }
63
+ var identity = JSON.stringify(Object.keys(resolved).sort().map(function(key2) {
64
+ return [key2, resolved[key2]];
65
+ }));
66
+ themes[name] = deduped.get(identity) || resolved;
67
+ deduped.set(identity, themes[name]);
68
+ var childSets = [inheritedChildren, children];
69
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = void 0;
70
+ try {
71
+ for (var _iterator = childSets[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
72
+ var childSet = _step.value;
73
+ if (!childSet) continue;
74
+ for (var childName in childSet) {
75
+ var fullName = `${name}_${childName}`;
76
+ if (fullName in themes) {
77
+ throw new Error(`Duplicate generated theme name "${fullName}"`);
78
+ }
79
+ add(fullName, childSet[childName], recipe);
80
+ }
81
+ }
82
+ } catch (err) {
83
+ _didIteratorError = true;
84
+ _iteratorError = err;
85
+ } finally {
86
+ try {
87
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
88
+ _iterator.return();
89
+ }
90
+ } finally {
91
+ if (_didIteratorError) {
92
+ throw _iteratorError;
93
+ }
94
+ }
95
+ }
96
+ };
97
+ add("light", tree.light, {}, tree.children);
98
+ add("dark", tree.dark, {}, tree.children);
99
+ return themes;
100
+ }
101
+
102
+ export { createThemes };
103
+ //# sourceMappingURL=createThemes.native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createThemes.native.js","names":[],"sources":["esm/createThemes.native.js"],"sourcesContent":["function createThemes(tokens, tree, options) {\n var rootKeys = Object.keys(tree).filter(function(key) {\n return key !== \"children\";\n });\n if (rootKeys.length !== 2 || !(\"light\" in tree) || !(\"dark\" in tree)) {\n throw new Error('createThemes tree must have exactly the roots \"light\" and \"dark\"');\n }\n var themes = {};\n var deduped = /* @__PURE__ */ new Map();\n var add = function(name, definition, parent, inheritedChildren) {\n var _options_getTheme;\n var resolvedDefinition = typeof definition === \"function\" ? definition({\n parent\n }) : definition;\n if (resolvedDefinition === null) return;\n var { values, children, ...recipeFields } = resolvedDefinition;\n var recipe = {\n ...parent,\n ...recipeFields\n };\n var unresolved = {\n ...options === null || options === void 0 ? void 0 : (_options_getTheme = options.getTheme) === null || _options_getTheme === void 0 ? void 0 : _options_getTheme.call(options, {\n recipe,\n name,\n tokens\n }),\n ...values\n };\n var resolved = {};\n for (var key in unresolved) {\n var value = unresolved[key];\n var token = tokens.color[value];\n if (token !== void 0) {\n resolved[key] = token;\n continue;\n }\n var isColor = value === \"transparent\" || /^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$/i.test(value) || /^(?:rgb|rgba|hsl|hsla)\\(\\s*[^)]+\\)$/i.test(value);\n if (isColor) {\n resolved[key] = value;\n continue;\n }\n var nearest = \"\";\n var nearestDistance = Number.POSITIVE_INFINITY;\n for (var tokenName in tokens.color) {\n var previous = Array.from({\n length: tokenName.length + 1\n }, function(_, index) {\n return index;\n });\n for (var valueIndex = 0; valueIndex < value.length; valueIndex++) {\n var diagonal = previous[0];\n previous[0] = valueIndex + 1;\n for (var tokenIndex = 0; tokenIndex < tokenName.length; tokenIndex++) {\n var above = previous[tokenIndex + 1];\n previous[tokenIndex + 1] = Math.min(previous[tokenIndex] + 1, above + 1, diagonal + (value[valueIndex] === tokenName[tokenIndex] ? 0 : 1));\n diagonal = above;\n }\n }\n var distance = previous[tokenName.length];\n if (distance < nearestDistance) {\n nearest = tokenName;\n nearestDistance = distance;\n }\n }\n throw new Error(`Invalid color value \"${value}\" for theme \"${name}\" key \"${key}\".${nearest ? ` Did you mean token \"${nearest}\"?` : \"\"}`);\n }\n var identity = JSON.stringify(Object.keys(resolved).sort().map(function(key2) {\n return [\n key2,\n resolved[key2]\n ];\n }));\n themes[name] = deduped.get(identity) || resolved;\n deduped.set(identity, themes[name]);\n var childSets = [\n inheritedChildren,\n children\n ];\n var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = void 0;\n try {\n for (var _iterator = childSets[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var childSet = _step.value;\n if (!childSet) continue;\n for (var childName in childSet) {\n var fullName = `${name}_${childName}`;\n if (fullName in themes) {\n throw new Error(`Duplicate generated theme name \"${fullName}\"`);\n }\n add(fullName, childSet[childName], recipe);\n }\n }\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return != null) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n };\n add(\"light\", tree.light, {}, tree.children);\n add(\"dark\", tree.dark, {}, tree.children);\n return themes;\n}\nexport {\n createThemes\n};\n//# sourceMappingURL=createThemes.js.map\n"],"mappings":"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"}
package/dist/esm/index.js CHANGED
@@ -1,7 +1 @@
1
- export * from "./createTheme.mjs";
2
- export * from "./masks.mjs";
3
- export * from "./combineMasks.mjs";
4
- export * from "./applyMask.mjs";
5
- export * from "./createThemeTypes.mjs";
6
- export * from "./helpers.mjs";
7
- //# sourceMappingURL=index.js.map
1
+ export * from "./createThemes.mjs"
@@ -1,7 +1 @@
1
- export * from "./createTheme.mjs";
2
- export * from "./masks.mjs";
3
- export * from "./combineMasks.mjs";
4
- export * from "./applyMask.mjs";
5
- export * from "./createThemeTypes.mjs";
6
- export * from "./helpers.mjs";
7
- //# sourceMappingURL=index.mjs.map
1
+ export * from "./createThemes.mjs"
@@ -1,7 +1 @@
1
- export * from "./createTheme.native.js";
2
- export * from "./masks.native.js";
3
- export * from "./combineMasks.native.js";
4
- export * from "./applyMask.native.js";
5
- export * from "./createThemeTypes.native.js";
6
- export * from "./helpers.native.js";
7
- //# sourceMappingURL=index.native.js.map
1
+ export * from "./createThemes.native.js"
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@tamagui/create-theme",
3
- "version": "2.7.7",
3
+ "version": "3.0.0-beta.1097.1",
4
+ "license": "MIT",
4
5
  "files": [
5
6
  "src",
6
7
  "types",
7
- "dist",
8
- "theme-builder"
8
+ "dist"
9
9
  ],
10
10
  "type": "module",
11
11
  "sideEffects": false,
@@ -29,15 +29,15 @@
29
29
  },
30
30
  "scripts": {
31
31
  "build": "tamagui-build",
32
+ "test": "bun test tests/createThemes.test.ts",
33
+ "test:types": "vitest --typecheck --run tests/createThemes.test-d.ts",
32
34
  "watch": "tamagui-build --watch",
33
35
  "clean": "tamagui-build clean",
34
36
  "clean:build": "tamagui-build clean:build"
35
37
  },
36
- "dependencies": {
37
- "@tamagui/web": "2.7.7"
38
- },
39
38
  "devDependencies": {
40
- "@tamagui/build": "2.7.7"
39
+ "@tamagui/build": "3.0.0-beta.1097.1",
40
+ "vitest": "4.1.0"
41
41
  },
42
42
  "repository": {
43
43
  "type": "git",
@@ -0,0 +1,324 @@
1
+ export type ThemeValue = string
2
+
3
+ export type ThemeTokens = {
4
+ color: Record<string, ThemeValue>
5
+ }
6
+
7
+ export type ThemeRecipe = Record<string, unknown>
8
+
9
+ export type ThemeDefinitionContext<Parent extends ThemeRecipe = ThemeRecipe> = {
10
+ parent: Parent
11
+ }
12
+
13
+ export type ThemeDefinitionObject = {
14
+ values?: Record<string, ThemeValue>
15
+ children?: ThemeChildren
16
+ [key: string]: unknown
17
+ }
18
+
19
+ export type ThemeTreeDefinition<Parent extends ThemeRecipe = ThemeRecipe> =
20
+ | ThemeDefinitionObject
21
+ | ((context: ThemeDefinitionContext<Parent>) => ThemeDefinitionObject | null)
22
+
23
+ export type ThemeChildren = Record<string, ThemeTreeDefinition>
24
+
25
+ export type ThemeTree = {
26
+ light: ThemeTreeDefinition
27
+ dark: ThemeTreeDefinition
28
+ children?: ThemeChildren
29
+ }
30
+
31
+ type ColorLiteral = `#${string}` | `rgb${string}` | `hsl${string}` | 'transparent'
32
+
33
+ type ThemeInputValue<Tokens extends ThemeTokens> =
34
+ | Extract<keyof Tokens['color'], string>
35
+ | ColorLiteral
36
+
37
+ type ResolvedDefinition<Definition> = Definition extends (...args: any[]) => infer Result
38
+ ? Exclude<Result, null>
39
+ : Definition
40
+
41
+ type DefinitionChildren<Definition> =
42
+ ResolvedDefinition<Definition> extends {
43
+ children?: infer Children
44
+ }
45
+ ? Children
46
+ : never
47
+
48
+ type ChildThemeNames<Parent extends string, Children> =
49
+ Children extends Record<string, unknown>
50
+ ? {
51
+ [Name in Extract<keyof Children, string>]:
52
+ | `${Parent}_${Name}`
53
+ | ChildThemeNames<`${Parent}_${Name}`, DefinitionChildren<Children[Name]>>
54
+ }[Extract<keyof Children, string>]
55
+ : never
56
+
57
+ export type ThemeNames<Tree extends ThemeTree> =
58
+ | 'light'
59
+ | 'dark'
60
+ | ChildThemeNames<'light', Tree['children']>
61
+ | ChildThemeNames<'dark', Tree['children']>
62
+ | ChildThemeNames<'light', DefinitionChildren<Tree['light']>>
63
+ | ChildThemeNames<'dark', DefinitionChildren<Tree['dark']>>
64
+
65
+ type RecipeFields<Definition> = Omit<
66
+ ResolvedDefinition<Definition>,
67
+ 'children' | 'values'
68
+ >
69
+
70
+ type ChildDefinitions<
71
+ Children,
72
+ Depth extends readonly unknown[],
73
+ > = Depth extends readonly [unknown, ...infer Rest]
74
+ ? Children extends Record<string, unknown>
75
+ ? Children[keyof Children] | DefinitionsWithin<Children[keyof Children], Rest>
76
+ : never
77
+ : never
78
+
79
+ type DefinitionsWithin<Definition, Depth extends readonly unknown[]> =
80
+ | Definition
81
+ | ChildDefinitions<DefinitionChildren<Definition>, Depth>
82
+
83
+ type DefinitionsInTree<Tree extends ThemeTree> =
84
+ | DefinitionsWithin<Tree['light'], [1, 1, 1, 1, 1, 1]>
85
+ | DefinitionsWithin<Tree['dark'], [1, 1, 1, 1, 1, 1]>
86
+ | ChildDefinitions<Tree['children'], [1, 1, 1, 1, 1, 1]>
87
+
88
+ type KeysOfUnion<Union> = Union extends Union ? keyof Union : never
89
+
90
+ type ValueOfUnion<Union, Key extends PropertyKey> = Union extends Union
91
+ ? Key extends keyof Union
92
+ ? Union[Key]
93
+ : never
94
+ : never
95
+
96
+ type MergeRecipeUnion<Union> = {
97
+ [Key in KeysOfUnion<Union>]: ValueOfUnion<Union, Key>
98
+ }
99
+
100
+ type RootRecipe<Tree extends ThemeTree> =
101
+ | RecipeFields<Tree['light']>
102
+ | RecipeFields<Tree['dark']>
103
+
104
+ type RecipeForTree<Tree extends ThemeTree> = {
105
+ [Key in keyof RootRecipe<Tree>]: ValueOfUnion<RootRecipe<Tree>, Key>
106
+ } & Partial<MergeRecipeUnion<RecipeFields<DefinitionsInTree<Tree>>>>
107
+
108
+ export type GetThemeContext<
109
+ Tokens extends ThemeTokens = ThemeTokens,
110
+ Recipe extends ThemeRecipe = ThemeRecipe,
111
+ > = {
112
+ recipe: Recipe
113
+ name: string
114
+ tokens: Tokens
115
+ }
116
+
117
+ type InvalidValueKeys<Values, Theme> = Exclude<keyof Values, keyof Theme>
118
+
119
+ type ValidatedValues<Values, Theme, Tokens extends ThemeTokens> =
120
+ Values extends Record<string, unknown>
121
+ ? Values & {
122
+ [Key in InvalidValueKeys<Values, Theme>]: never
123
+ } & {
124
+ [Key in keyof Values]: Values[Key] extends ThemeInputValue<Tokens>
125
+ ? Values[Key]
126
+ : never
127
+ }
128
+ : never
129
+
130
+ type ValidatedChildren<Children, Theme, Tokens extends ThemeTokens> =
131
+ Children extends Record<string, unknown>
132
+ ? {
133
+ [Name in keyof Children]: ValidatedDefinition<Children[Name], Theme, Tokens>
134
+ }
135
+ : never
136
+
137
+ type ValidatedDefinition<
138
+ Definition,
139
+ Theme,
140
+ Tokens extends ThemeTokens,
141
+ > = Definition extends (...args: infer Args) => infer Result
142
+ ? (
143
+ ...args: Args
144
+ ) => Result extends null ? null : ValidatedDefinitionObject<Result, Theme, Tokens>
145
+ : ValidatedDefinitionObject<Definition, Theme, Tokens>
146
+
147
+ type ValidatedDefinitionObject<Definition, Theme, Tokens extends ThemeTokens> =
148
+ Definition extends Record<string, unknown>
149
+ ? Definition &
150
+ ('values' extends keyof Definition
151
+ ? {
152
+ values: ValidatedValues<Definition['values'], Theme, Tokens>
153
+ }
154
+ : unknown) &
155
+ ('children' extends keyof Definition
156
+ ? {
157
+ children: ValidatedChildren<Definition['children'], Theme, Tokens>
158
+ }
159
+ : unknown)
160
+ : never
161
+
162
+ type ValidatedTree<Tree extends ThemeTree, Theme, Tokens extends ThemeTokens> = Tree & {
163
+ light: ValidatedDefinition<Tree['light'], Theme, Tokens>
164
+ dark: ValidatedDefinition<Tree['dark'], Theme, Tokens>
165
+ children?: ValidatedChildren<Tree['children'], Theme, Tokens>
166
+ }
167
+
168
+ type ValuesInDefinition<Definition> =
169
+ ResolvedDefinition<Definition> extends {
170
+ values?: infer Values
171
+ }
172
+ ? Values
173
+ : never
174
+
175
+ type ValueDefinitionsWithin<Definition, Depth extends readonly unknown[]> =
176
+ | ValuesInDefinition<Definition>
177
+ | (Depth extends readonly [unknown, ...infer Rest]
178
+ ? DefinitionChildren<Definition> extends infer Children
179
+ ? Children extends Record<string, unknown>
180
+ ? ValueDefinitionsWithin<Children[keyof Children], Rest>
181
+ : never
182
+ : never
183
+ : never)
184
+
185
+ type ExplicitTheme<Tree extends ThemeTree> = MergeRecipeUnion<
186
+ | ValueDefinitionsWithin<Tree['light'], [1, 1, 1, 1, 1, 1]>
187
+ | ValueDefinitionsWithin<Tree['dark'], [1, 1, 1, 1, 1, 1]>
188
+ | (Tree['children'] extends infer Children
189
+ ? Children extends Record<string, unknown>
190
+ ? ValueDefinitionsWithin<Children[keyof Children], [1, 1, 1, 1, 1, 1]>
191
+ : never
192
+ : never)
193
+ >
194
+
195
+ export type CreatedThemes<Names extends string, Theme extends Record<string, string>> = {
196
+ [Name in Names]: { [Key in keyof Theme]: string }
197
+ }
198
+
199
+ export function createThemes<
200
+ const Tokens extends ThemeTokens,
201
+ const Tree extends ThemeTree,
202
+ const Theme extends Record<string, ThemeInputValue<Tokens>>,
203
+ >(
204
+ tokens: Tokens,
205
+ tree: ValidatedTree<Tree, Theme, Tokens>,
206
+ options: {
207
+ getTheme: (context: GetThemeContext<Tokens, RecipeForTree<Tree>>) => Theme
208
+ }
209
+ ): CreatedThemes<ThemeNames<Tree>, Theme>
210
+
211
+ export function createThemes<
212
+ const Tokens extends ThemeTokens,
213
+ const Tree extends ThemeTree,
214
+ >(
215
+ tokens: Tokens,
216
+ tree: ValidatedTree<Tree, Record<string, string>, Tokens>,
217
+ options?: { getTheme?: undefined }
218
+ ): CreatedThemes<ThemeNames<Tree>, ExplicitTheme<Tree>>
219
+
220
+ export function createThemes(
221
+ tokens: ThemeTokens,
222
+ tree: ThemeTree,
223
+ options?: {
224
+ getTheme?: (context: GetThemeContext) => Record<string, string>
225
+ }
226
+ ): Record<string, Record<string, string>> {
227
+ const rootKeys = Object.keys(tree).filter((key) => key !== 'children')
228
+ if (rootKeys.length !== 2 || !('light' in tree) || !('dark' in tree)) {
229
+ throw new Error('createThemes tree must have exactly the roots "light" and "dark"')
230
+ }
231
+
232
+ const themes: Record<string, Record<string, string>> = {}
233
+ const deduped = new Map<string, Record<string, string>>()
234
+
235
+ const add = (
236
+ name: string,
237
+ definition: ThemeTreeDefinition,
238
+ parent: ThemeRecipe,
239
+ inheritedChildren?: ThemeChildren
240
+ ): void => {
241
+ const resolvedDefinition =
242
+ typeof definition === 'function' ? definition({ parent }) : definition
243
+ if (resolvedDefinition === null) return
244
+
245
+ const { values, children, ...recipeFields } = resolvedDefinition
246
+ const recipe = { ...parent, ...recipeFields }
247
+ const unresolved = {
248
+ ...options?.getTheme?.({ recipe, name, tokens }),
249
+ ...values,
250
+ }
251
+ const resolved: Record<string, string> = {}
252
+
253
+ for (const key in unresolved) {
254
+ const value = unresolved[key]
255
+ const token = tokens.color[value]
256
+ if (token !== undefined) {
257
+ resolved[key] = token
258
+ continue
259
+ }
260
+
261
+ const isColor =
262
+ value === 'transparent' ||
263
+ /^#(?:[\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/i.test(value) ||
264
+ /^(?:rgb|rgba|hsl|hsla)\(\s*[^)]+\)$/i.test(value)
265
+ if (isColor) {
266
+ resolved[key] = value
267
+ continue
268
+ }
269
+
270
+ let nearest = ''
271
+ let nearestDistance = Number.POSITIVE_INFINITY
272
+ for (const tokenName in tokens.color) {
273
+ const previous = Array.from({ length: tokenName.length + 1 }, (_, index) => index)
274
+ for (let valueIndex = 0; valueIndex < value.length; valueIndex++) {
275
+ let diagonal = previous[0]!
276
+ previous[0] = valueIndex + 1
277
+ for (let tokenIndex = 0; tokenIndex < tokenName.length; tokenIndex++) {
278
+ const above = previous[tokenIndex + 1]!
279
+ previous[tokenIndex + 1] = Math.min(
280
+ previous[tokenIndex]! + 1,
281
+ above + 1,
282
+ diagonal + (value[valueIndex] === tokenName[tokenIndex] ? 0 : 1)
283
+ )
284
+ diagonal = above
285
+ }
286
+ }
287
+ const distance = previous[tokenName.length]!
288
+ if (distance < nearestDistance) {
289
+ nearest = tokenName
290
+ nearestDistance = distance
291
+ }
292
+ }
293
+
294
+ throw new Error(
295
+ `Invalid color value "${value}" for theme "${name}" key "${key}".${nearest ? ` Did you mean token "${nearest}"?` : ''}`
296
+ )
297
+ }
298
+
299
+ const identity = JSON.stringify(
300
+ Object.keys(resolved)
301
+ .sort()
302
+ .map((key) => [key, resolved[key]])
303
+ )
304
+ themes[name] = deduped.get(identity) || resolved
305
+ deduped.set(identity, themes[name]!)
306
+
307
+ const childSets = [inheritedChildren, children]
308
+ for (const childSet of childSets) {
309
+ if (!childSet) continue
310
+ for (const childName in childSet) {
311
+ const fullName = `${name}_${childName}`
312
+ if (fullName in themes) {
313
+ throw new Error(`Duplicate generated theme name "${fullName}"`)
314
+ }
315
+ add(fullName, childSet[childName]!, recipe)
316
+ }
317
+ }
318
+ }
319
+
320
+ add('light', tree.light, {}, tree.children)
321
+ add('dark', tree.dark, {}, tree.children)
322
+
323
+ return themes
324
+ }
package/src/index.tsx CHANGED
@@ -1,6 +1 @@
1
- export * from './createTheme'
2
- export * from './masks'
3
- export * from './combineMasks'
4
- export * from './applyMask'
5
- export * from './createThemeTypes'
6
- export * from './helpers'
1
+ export * from './createThemes'
@@ -0,0 +1,122 @@
1
+ export type ThemeValue = string;
2
+ export type ThemeTokens = {
3
+ color: Record<string, ThemeValue>;
4
+ };
5
+ export type ThemeRecipe = Record<string, unknown>;
6
+ export type ThemeDefinitionContext<Parent extends ThemeRecipe = ThemeRecipe> = {
7
+ parent: Parent;
8
+ };
9
+ export type ThemeDefinitionObject = {
10
+ values?: Record<string, ThemeValue>;
11
+ children?: ThemeChildren;
12
+ [key: string]: unknown;
13
+ };
14
+ export type ThemeTreeDefinition<Parent extends ThemeRecipe = ThemeRecipe> = ThemeDefinitionObject | ((context: ThemeDefinitionContext<Parent>) => ThemeDefinitionObject | null);
15
+ export type ThemeChildren = Record<string, ThemeTreeDefinition>;
16
+ export type ThemeTree = {
17
+ light: ThemeTreeDefinition;
18
+ dark: ThemeTreeDefinition;
19
+ children?: ThemeChildren;
20
+ };
21
+ type ColorLiteral = `#${string}` | `rgb${string}` | `hsl${string}` | "transparent";
22
+ type ThemeInputValue<Tokens extends ThemeTokens> = Extract<keyof Tokens["color"], string> | ColorLiteral;
23
+ type ResolvedDefinition<Definition> = Definition extends (...args: any[]) => infer Result ? Exclude<Result, null> : Definition;
24
+ type DefinitionChildren<Definition> = ResolvedDefinition<Definition> extends {
25
+ children?: infer Children;
26
+ } ? Children : never;
27
+ type ChildThemeNames<
28
+ Parent extends string,
29
+ Children
30
+ > = Children extends Record<string, unknown> ? { [Name in Extract<keyof Children, string>] : `${Parent}_${Name}` | ChildThemeNames<`${Parent}_${Name}`, DefinitionChildren<Children[Name]>> }[Extract<keyof Children, string>] : never;
31
+ export type ThemeNames<Tree extends ThemeTree> = "light" | "dark" | ChildThemeNames<"light", Tree["children"]> | ChildThemeNames<"dark", Tree["children"]> | ChildThemeNames<"light", DefinitionChildren<Tree["light"]>> | ChildThemeNames<"dark", DefinitionChildren<Tree["dark"]>>;
32
+ type RecipeFields<Definition> = Omit<ResolvedDefinition<Definition>, "children" | "values">;
33
+ type ChildDefinitions<
34
+ Children,
35
+ Depth extends readonly unknown[]
36
+ > = Depth extends readonly [unknown, ...infer Rest] ? Children extends Record<string, unknown> ? Children[keyof Children] | DefinitionsWithin<Children[keyof Children], Rest> : never : never;
37
+ type DefinitionsWithin<
38
+ Definition,
39
+ Depth extends readonly unknown[]
40
+ > = Definition | ChildDefinitions<DefinitionChildren<Definition>, Depth>;
41
+ type DefinitionsInTree<Tree extends ThemeTree> = DefinitionsWithin<Tree["light"], [1, 1, 1, 1, 1, 1]> | DefinitionsWithin<Tree["dark"], [1, 1, 1, 1, 1, 1]> | ChildDefinitions<Tree["children"], [1, 1, 1, 1, 1, 1]>;
42
+ type KeysOfUnion<Union> = Union extends Union ? keyof Union : never;
43
+ type ValueOfUnion<
44
+ Union,
45
+ Key extends PropertyKey
46
+ > = Union extends Union ? Key extends keyof Union ? Union[Key] : never : never;
47
+ type MergeRecipeUnion<Union> = { [Key in KeysOfUnion<Union>] : ValueOfUnion<Union, Key> };
48
+ type RootRecipe<Tree extends ThemeTree> = RecipeFields<Tree["light"]> | RecipeFields<Tree["dark"]>;
49
+ type RecipeForTree<Tree extends ThemeTree> = { [Key in keyof RootRecipe<Tree>] : ValueOfUnion<RootRecipe<Tree>, Key> } & Partial<MergeRecipeUnion<RecipeFields<DefinitionsInTree<Tree>>>>;
50
+ export type GetThemeContext<
51
+ Tokens extends ThemeTokens = ThemeTokens,
52
+ Recipe extends ThemeRecipe = ThemeRecipe
53
+ > = {
54
+ recipe: Recipe;
55
+ name: string;
56
+ tokens: Tokens;
57
+ };
58
+ type InvalidValueKeys<
59
+ Values,
60
+ Theme
61
+ > = Exclude<keyof Values, keyof Theme>;
62
+ type ValidatedValues<
63
+ Values,
64
+ Theme,
65
+ Tokens extends ThemeTokens
66
+ > = Values extends Record<string, unknown> ? Values & { [Key in InvalidValueKeys<Values, Theme>] : never } & { [Key in keyof Values] : Values[Key] extends ThemeInputValue<Tokens> ? Values[Key] : never } : never;
67
+ type ValidatedChildren<
68
+ Children,
69
+ Theme,
70
+ Tokens extends ThemeTokens
71
+ > = Children extends Record<string, unknown> ? { [Name in keyof Children] : ValidatedDefinition<Children[Name], Theme, Tokens> } : never;
72
+ type ValidatedDefinition<
73
+ Definition,
74
+ Theme,
75
+ Tokens extends ThemeTokens
76
+ > = Definition extends (...args: infer Args) => infer Result ? (...args: Args) => Result extends null ? null : ValidatedDefinitionObject<Result, Theme, Tokens> : ValidatedDefinitionObject<Definition, Theme, Tokens>;
77
+ type ValidatedDefinitionObject<
78
+ Definition,
79
+ Theme,
80
+ Tokens extends ThemeTokens
81
+ > = Definition extends Record<string, unknown> ? Definition & ("values" extends keyof Definition ? {
82
+ values: ValidatedValues<Definition["values"], Theme, Tokens>;
83
+ } : unknown) & ("children" extends keyof Definition ? {
84
+ children: ValidatedChildren<Definition["children"], Theme, Tokens>;
85
+ } : unknown) : never;
86
+ type ValidatedTree<
87
+ Tree extends ThemeTree,
88
+ Theme,
89
+ Tokens extends ThemeTokens
90
+ > = Tree & {
91
+ light: ValidatedDefinition<Tree["light"], Theme, Tokens>;
92
+ dark: ValidatedDefinition<Tree["dark"], Theme, Tokens>;
93
+ children?: ValidatedChildren<Tree["children"], Theme, Tokens>;
94
+ };
95
+ type ValuesInDefinition<Definition> = ResolvedDefinition<Definition> extends {
96
+ values?: infer Values;
97
+ } ? Values : never;
98
+ type ValueDefinitionsWithin<
99
+ Definition,
100
+ Depth extends readonly unknown[]
101
+ > = ValuesInDefinition<Definition> | (Depth extends readonly [unknown, ...infer Rest] ? DefinitionChildren<Definition> extends infer Children ? Children extends Record<string, unknown> ? ValueDefinitionsWithin<Children[keyof Children], Rest> : never : never : never);
102
+ type ExplicitTheme<Tree extends ThemeTree> = MergeRecipeUnion<ValueDefinitionsWithin<Tree["light"], [1, 1, 1, 1, 1, 1]> | ValueDefinitionsWithin<Tree["dark"], [1, 1, 1, 1, 1, 1]> | (Tree["children"] extends infer Children ? Children extends Record<string, unknown> ? ValueDefinitionsWithin<Children[keyof Children], [1, 1, 1, 1, 1, 1]> : never : never)>;
103
+ export type CreatedThemes<
104
+ Names extends string,
105
+ Theme extends Record<string, string>
106
+ > = { [Name in Names] : { [Key in keyof Theme] : string } };
107
+ export declare function createThemes<
108
+ const Tokens extends ThemeTokens,
109
+ const Tree extends ThemeTree,
110
+ const Theme extends Record<string, ThemeInputValue<Tokens>>
111
+ >(tokens: Tokens, tree: ValidatedTree<Tree, Theme, Tokens>, options: {
112
+ getTheme: (context: GetThemeContext<Tokens, RecipeForTree<Tree>>) => Theme;
113
+ }): CreatedThemes<ThemeNames<Tree>, Theme>;
114
+ export declare function createThemes<
115
+ const Tokens extends ThemeTokens,
116
+ const Tree extends ThemeTree
117
+ >(tokens: Tokens, tree: ValidatedTree<Tree, Record<string, string>, Tokens>, options?: {
118
+ getTheme?: undefined;
119
+ }): CreatedThemes<ThemeNames<Tree>, ExplicitTheme<Tree>>;
120
+ export {};
121
+
122
+ //# sourceMappingURL=createThemes.d.ts.map