@tamagui/cli 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 (46) hide show
  1. package/dist/add.cjs +76 -87
  2. package/dist/build.cjs +425 -278
  3. package/dist/cli.cjs +361 -340
  4. package/dist/generate-prompt.cjs +484 -362
  5. package/dist/generate.cjs +45 -54
  6. package/dist/index.cjs +1 -1
  7. package/dist/migrate.cjs +437 -0
  8. package/dist/setup-prompt.cjs +216 -0
  9. package/dist/to-tailwind-default-config.cjs +789 -0
  10. package/dist/to-tailwind.cjs +213 -0
  11. package/dist/update-template.cjs +43 -52
  12. package/dist/update.cjs +14 -18
  13. package/dist/upgrade.cjs +417 -401
  14. package/dist/utils.cjs +84 -101
  15. package/metro.cjs +1 -0
  16. package/metro.d.ts +1 -0
  17. package/metro.mjs +1 -0
  18. package/package.json +43 -13
  19. package/src/build.ts +594 -362
  20. package/src/cli.ts +113 -12
  21. package/src/generate-prompt.ts +428 -329
  22. package/src/migrate.ts +422 -0
  23. package/src/setup-prompt.ts +192 -0
  24. package/src/to-tailwind-default-config.ts +767 -0
  25. package/src/to-tailwind.ts +313 -0
  26. package/src/upgrade.ts +30 -33
  27. package/src/utils.ts +11 -9
  28. package/types/add.d.ts +1 -1
  29. package/types/add.d.ts.map +1 -1
  30. package/types/build.d.ts +2 -1
  31. package/types/build.d.ts.map +1 -1
  32. package/types/generate-prompt.d.ts +6 -2
  33. package/types/generate-prompt.d.ts.map +1 -1
  34. package/types/migrate.d.ts +7 -0
  35. package/types/migrate.d.ts.map +1 -0
  36. package/types/setup-prompt.d.ts +5 -0
  37. package/types/setup-prompt.d.ts.map +1 -0
  38. package/types/to-tailwind-default-config.d.ts +53 -0
  39. package/types/to-tailwind-default-config.d.ts.map +1 -0
  40. package/types/to-tailwind.d.ts +16 -0
  41. package/types/to-tailwind.d.ts.map +1 -0
  42. package/types/upgrade.d.ts.map +1 -1
  43. package/types/utils.d.ts.map +1 -1
  44. package/vite.cjs +1 -0
  45. package/vite.d.ts +1 -0
  46. package/vite.mjs +1 -0
@@ -5,401 +5,523 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __getProtoOf = Object.getPrototypeOf;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
7
  var __export = (target, all) => {
8
- for (var name in all) __defProp(target, name, {
9
- get: all[name],
10
- enumerable: true
11
- });
8
+ for (var name in all) __defProp(target, name, {
9
+ get: all[name],
10
+ enumerable: true
11
+ });
12
12
  };
13
13
  var __copyProps = (to, from, except, desc) => {
14
- if (from && typeof from === "object" || typeof from === "function") {
15
- for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
16
- get: () => from[key],
17
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
18
- });
19
- }
20
- return to;
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
16
+ get: () => from[key],
17
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
18
+ });
19
+ }
20
+ return to;
21
21
  };
22
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
- // If the importer is in node compatibility mode or this is not an ESM
24
- // file that has been converted to a CommonJS file using a Babel-
25
- // compatible transform (i.e. "__esModule" has not been set), then set
26
- // "default" to the CommonJS "module.exports" for node compatibility.
27
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
28
- value: mod,
29
- enumerable: true
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
+ value: mod,
24
+ enumerable: true
30
25
  }) : target, mod));
31
- var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
32
- value: true
33
- }), mod);
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
34
27
  var generate_prompt_exports = {};
35
28
  __export(generate_prompt_exports, {
36
- generatePrompt: () => generatePrompt
29
+ generateMarkdown: () => generateMarkdown,
30
+ generatePrompt: () => generatePrompt
37
31
  });
38
32
  module.exports = __toCommonJS(generate_prompt_exports);
39
33
  var import_node_path = require("node:path");
40
34
  var FS = __toESM(require("fs-extra"));
41
35
  async function generatePrompt(options) {
42
- const {
43
- paths,
44
- output
45
- } = options;
46
- const {
47
- loadTamagui
48
- } = require("@tamagui/static/loadTamagui");
49
- process.env.TAMAGUI_KEEP_THEMES = "1";
50
- await loadTamagui({
51
- ...options.tamaguiOptions,
52
- platform: "web"
53
- });
54
- const configPath = (0, import_node_path.join)(paths.dotDir, "tamagui.config.json");
55
- if (!FS.existsSync(configPath)) {
56
- throw new Error(`Config file not found at ${configPath}. Please run 'tamagui generate' first.`);
57
- }
58
- const config = await FS.readJSON(configPath);
59
- const markdown = generateMarkdown(config);
60
- const outputPath = output || (0, import_node_path.join)(process.cwd(), "tamagui-prompt.md");
61
- await FS.writeFile(outputPath, markdown, "utf-8");
62
- console.info(`
36
+ const { paths, output } = options;
37
+ const { loadTamagui } = require("@tamagui/static/loadTamagui");
38
+ process.env.TAMAGUI_KEEP_THEMES = "1";
39
+ await loadTamagui({
40
+ ...options.tamaguiOptions,
41
+ platform: "web"
42
+ }, true);
43
+ const configPath = (0, import_node_path.join)(paths.dotDir, "tamagui.config.json");
44
+ if (!FS.existsSync(configPath)) {
45
+ throw new Error(`Config file not found at ${configPath}. Please run 'tamagui generate' first.`);
46
+ }
47
+ const config = await FS.readJSON(configPath);
48
+ const configSetting = config.tamaguiConfig?.settings?.styleValueSyntax;
49
+ const explicitChoice = options.styleValueSyntax || process.env.TAMAGUI_STYLE_VALUE_SYNTAX || configSetting;
50
+ const { resolveStyleValueSyntax } = require("./setup-prompt.cjs");
51
+ const resolvedSyntax = await resolveStyleValueSyntax(explicitChoice);
52
+ const markdown = generateMarkdown(config, { styleValueSyntax: resolvedSyntax });
53
+ const outputPath = output || (0, import_node_path.join)(process.cwd(), "tamagui-prompt.md");
54
+ await FS.writeFile(outputPath, markdown, "utf-8");
55
+ console.info(`
63
56
  \u2713 Generated prompt file at ${outputPath}
64
57
  `);
65
58
  }
66
- function generateMarkdown(config) {
67
- const sections = [];
68
- sections.push("# Tamagui Configuration\n\n");
69
- sections.push("This document provides an overview of the Tamagui configuration for this project.\n\n");
70
- const shorthands = config.tamaguiConfig?.shorthands || {};
71
- const reverseShorthands = {};
72
- for (const [short, full] of Object.entries(shorthands)) {
73
- reverseShorthands[full] = short;
74
- }
75
- const getPropName = fullProp => {
76
- const settings2 = config.tamaguiConfig?.settings || {};
77
- if (settings2.onlyAllowShorthands && reverseShorthands[fullProp]) {
78
- return reverseShorthands[fullProp];
79
- }
80
- return fullProp;
81
- };
82
- const settings = config.tamaguiConfig?.settings || {};
83
- if (Object.keys(settings).length > 0) {
84
- sections.push("## Configuration Settings\n\n");
85
- sections.push("**IMPORTANT:** These settings affect how you write Tamagui code in this project.\n\n");
86
- if (settings.defaultFont) {
87
- sections.push(`### Default Font: \`${settings.defaultFont}\`
59
+ function generateMarkdown(config, options) {
60
+ const sections = [];
61
+ sections.push("# Tamagui Configuration\n\n");
62
+ sections.push("This document provides an overview of the Tamagui configuration for this project.\n\n");
63
+ const shorthands = config.tamaguiConfig?.shorthands || {};
64
+ const reverseShorthands = {};
65
+ for (const [short, full] of Object.entries(shorthands)) {
66
+ reverseShorthands[full] = short;
67
+ }
68
+ const getPropName = (fullProp) => {
69
+ const settings2 = config.tamaguiConfig?.settings || {};
70
+ if (settings2.onlyAllowShorthands) {
71
+ if (reverseShorthands[fullProp]) return reverseShorthands[fullProp];
72
+ if (fullProp === "backgroundColor" && reverseShorthands["background"]) {
73
+ return reverseShorthands["background"];
74
+ }
75
+ }
76
+ return fullProp;
77
+ };
78
+ const settings = config.tamaguiConfig?.settings || {};
79
+ const syntaxChoice = options?.styleValueSyntax || settings.styleValueSyntax || "both";
80
+ sections.push("## Configuration Settings\n\n");
81
+ sections.push("**IMPORTANT:** These settings affect how you write Tamagui code in this project.\n\n");
82
+ if (settings.defaultFont) {
83
+ sections.push(`### Default Font: \`${settings.defaultFont}\`
88
84
 
89
85
  `);
90
- sections.push(`All text components will use the "${settings.defaultFont}" font family by default.
86
+ sections.push(`All text components will use the "${settings.defaultFont}" font family by default.
91
87
 
92
88
  `);
93
- }
94
- if (settings.onlyAllowShorthands !== void 0) {
95
- sections.push(`### Only Allow Shorthands: \`${settings.onlyAllowShorthands}\`
89
+ }
90
+ if (settings.onlyAllowShorthands !== void 0) {
91
+ sections.push(`### Only Allow Shorthands: \`${settings.onlyAllowShorthands}\`
96
92
 
97
93
  `);
98
- if (settings.onlyAllowShorthands) {
99
- sections.push("**You MUST use shorthand properties in this project.**\n\n");
100
- sections.push("Full property names are not allowed. For example:\n");
101
- sections.push('- \u2705 `<View w="$10" />` (correct)\n');
102
- sections.push('- \u274C `<View width="$10" />` (will error)\n\n');
103
- sections.push("See the Shorthand Properties section below for all available shorthands.\n\n");
104
- } else {
105
- sections.push("You can use either shorthand or full property names.\n\n");
106
- }
107
- }
108
- if (settings.themeClassNameOnRoot !== void 0) {
109
- sections.push(`### Theme Class Name on Root: \`${settings.themeClassNameOnRoot}\`
94
+ if (settings.onlyAllowShorthands) {
95
+ sections.push("**You MUST use shorthand properties in this project.**\n\n");
96
+ sections.push("Full property names are not allowed. For example:\n");
97
+ sections.push("- `<View w=\"10\" />` (correct)\n");
98
+ sections.push("- `<View width=\"10\" />` (will error)\n\n");
99
+ sections.push("See the Shorthand Properties section below for all available shorthands.\n\n");
100
+ } else {
101
+ sections.push("You can use either shorthand or full property names.\n\n");
102
+ }
103
+ }
104
+ if (syntaxChoice === "string") {
105
+ sections.push("### Style value syntax: `string`\n\n");
106
+ sections.push("Only the string form is allowed in this project (e.g. `bg=\"red hover:blue\"`).\n\n");
107
+ } else if (syntaxChoice === "object") {
108
+ sections.push("### Style value syntax: `object`\n\n");
109
+ sections.push("Only the object form is allowed in this project (e.g. `bg={{ default: 'red', hover: 'blue' }}`).\n\n");
110
+ } else {
111
+ sections.push("### Style value syntax\n\n");
112
+ sections.push("Both string and object style value syntax are allowed.\n\n");
113
+ }
114
+ if (settings.addThemeClassName !== void 0) {
115
+ sections.push(`### Theme Class Name: \`${settings.addThemeClassName}\`
110
116
 
111
117
  `);
112
- if (settings.themeClassNameOnRoot) {
113
- sections.push("Theme classes are applied to the root HTML element.\n\n");
114
- }
115
- }
116
- const platform = settings.platform || settings.defaultProps?.platform;
117
- if (platform) {
118
- sections.push(`### Platform Mode: \`${platform}\`
118
+ if (settings.addThemeClassName === "html") {
119
+ sections.push("Theme classes are applied to the root HTML element.\n\n");
120
+ }
121
+ }
122
+ if (settings.allowedStyleValues) {
123
+ sections.push("### Allowed Style Values\n\n");
124
+ sections.push(`Type validation: \`${JSON.stringify(settings.allowedStyleValues)}\`.
119
125
 
120
126
  `);
121
- if (platform === "web") {
122
- sections.push("This project is configured for **web only**.\n\n");
123
- } else if (platform === "native") {
124
- sections.push("This project is configured for **React Native only**.\n\n");
125
- }
126
- }
127
- if (settings.webContainerType) {
128
- sections.push(`### Web Container Type: \`${settings.webContainerType}\`
127
+ sections.push("Single-token values are type-checked. Run `tamagui check --strict` to also validate conditional payloads.\n\n");
128
+ }
129
+ sections.push("## Flat Value Grammar\n\n");
130
+ sections.push("Conditional style values follow this grammar:\n\n");
131
+ sections.push("```txt\n");
132
+ sections.push("value := base? clause*\n");
133
+ sections.push("clause := modifier(:modifier)*:payload\n");
134
+ sections.push("```\n\n");
135
+ sections.push("- No `$` sigils: bare token names (`bg=\"background\"`, not `bg=\"$background\"`).\n");
136
+ sections.push("- Kebab-case theme names: `background-hover`, `border-color`, `shadow-color`.\n");
137
+ sections.push("- Numbers are px: `p={4}` is 4px, while `p=\"4\"` is space token 4.\n");
138
+ sections.push("- Specificity precedence: platform (`ios:` > `native:` > bare) > condition count > category (media < container < theme < group < state).\n\n");
139
+ const bgProp = getPropName("background");
140
+ const pProp = getPropName("padding");
141
+ if (syntaxChoice === "string") {
142
+ sections.push("**String form:**\n\n");
143
+ sections.push("```tsx\n");
144
+ sections.push(`<View ${bgProp}="background hover:background-hover dark:blue-500" ${pProp}="4 sm:6 max-sm:2" />
145
+ `);
146
+ sections.push("```\n\n");
147
+ } else if (syntaxChoice === "object") {
148
+ sections.push("**Object form:**\n\n");
149
+ sections.push("```tsx\n");
150
+ sections.push(`<View ${bgProp}={{ default: 'background', hover: 'background-hover', dark: 'blue-500' }} ${pProp}={{ default: '4', sm: '6', 'max-sm': '2' }} />
151
+ `);
152
+ sections.push("```\n\n");
153
+ } else {
154
+ sections.push("Both forms are allowed:\n\n");
155
+ sections.push("```tsx\n");
156
+ sections.push("// string form\n");
157
+ sections.push(`<View ${bgProp}="background hover:background-hover dark:blue-500" ${pProp}="4 sm:6 max-sm:2" />
158
+
159
+ `);
160
+ sections.push("// object form\n");
161
+ sections.push(`<View ${bgProp}={{ default: 'background', hover: 'background-hover', dark: 'blue-500' }} ${pProp}={{ default: '4', sm: '6', 'max-sm': '2' }} />
162
+ `);
163
+ sections.push("```\n\n");
164
+ }
165
+ sections.push("## Shorthand Properties\n\n");
166
+ sections.push("These shorthand properties are available for styling:\n\n");
167
+ const shorthandEntries = Object.entries(shorthands).sort(([a], [b]) => a.localeCompare(b));
168
+ sections.push(shorthandEntries.map(([short, full]) => `- \`${short}\` \u2192 \`${full}\``).join("\n"));
169
+ sections.push("\n\n");
170
+ const sizes = config.tamaguiConfig?.sizes;
171
+ if (sizes && typeof sizes === "object") {
172
+ sections.push("## Named Control Sizes\n\n");
173
+ sections.push(`Control components (Button, Input, etc.) use the configured names below. Default is \`${sizes.default ?? "md"}\`.
174
+
175
+ `);
176
+ sections.push("| Size | Configuration |\n");
177
+ sections.push("|---|---|\n");
178
+ for (const [sizeKey, val] of Object.entries(sizes)) {
179
+ if (sizeKey === "default") continue;
180
+ const valStr = typeof val === "object" && val !== null ? JSON.stringify(val) : String(val);
181
+ const isDefault = sizes.default === sizeKey ? " (default)" : "";
182
+ sections.push(`| \`${sizeKey}\`${isDefault} | ${valStr} |
183
+ `);
184
+ }
185
+ sections.push("\n");
186
+ }
187
+ sections.push("## Themes\n\n");
188
+ const themes = config.tamaguiConfig?.themes || {};
189
+ const themeNames = Object.keys(themes).sort();
190
+ sections.push(themeNames.map((name) => `- \`${name}\``).join("\n"));
191
+ sections.push("\n\nTheme names above are exact configured names. Nested themes resolve relative to their parent. Use an explicit theme boundary in a component skin.\n\n");
192
+ if (themeNames.length) {
193
+ sections.push("### Theme Usage\n\n");
194
+ sections.push(`\`\`\`tsx
195
+ <Theme name=${JSON.stringify(themeNames[0])}>
196
+ <Button>Uses this theme</Button>
197
+ </Theme>
198
+ \`\`\`
199
+
200
+ `);
201
+ }
202
+ sections.push("**Accessing theme values:**\n\n");
203
+ sections.push("Components access theme values by their bare names:\n\n");
204
+ const colorProp = getPropName("color");
205
+ if (syntaxChoice === "string") {
206
+ sections.push("```tsx\n");
207
+ sections.push(`<View ${bgProp}="background hover:background-hover" ${colorProp}="color" />
208
+ `);
209
+ sections.push("```\n\n");
210
+ } else if (syntaxChoice === "object") {
211
+ sections.push("```tsx\n");
212
+ sections.push(`<View ${bgProp}={{ default: 'background', hover: 'background-hover' }} ${colorProp}={{ default: 'color' }} />
213
+ `);
214
+ sections.push("```\n\n");
215
+ } else {
216
+ sections.push("```tsx\n");
217
+ sections.push("// string form\n");
218
+ sections.push(`<View ${bgProp}="background hover:background-hover" ${colorProp}="color" />
219
+
220
+ `);
221
+ sections.push("// object form\n");
222
+ sections.push(`<View ${bgProp}={{ default: 'background', hover: 'background-hover' }} ${colorProp}={{ default: 'color' }} />
223
+ `);
224
+ sections.push("```\n\n");
225
+ }
226
+ sections.push("**Special props:**\n\n");
227
+ sections.push("- `theme=\"inverse\"`: Uses the opposite light or dark sub-theme\n");
228
+ sections.push("## Tokens\n\n");
229
+ sections.push("Tokens are design system values referenced by their bare names.\n\n");
230
+ const tokens = config.tamaguiConfig?.tokens || {};
231
+ if (tokens.space && Object.keys(tokens.space).length > 0) {
232
+ sections.push("### Space Tokens\n\n");
233
+ const spaceTokens = Object.entries(tokens.space).sort(([a], [b]) => {
234
+ const numA = parseFloat(a);
235
+ const numB = parseFloat(b);
236
+ if (!isNaN(numA) && !isNaN(numB)) {
237
+ return numA - numB;
238
+ }
239
+ return a.localeCompare(b);
240
+ });
241
+ sections.push(spaceTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
242
+ sections.push("\n\n");
243
+ }
244
+ if (tokens.size && Object.keys(tokens.size).length > 0) {
245
+ sections.push("### Size Tokens\n\n");
246
+ const sizeTokens = Object.entries(tokens.size).sort(([a], [b]) => {
247
+ const numA = parseFloat(a);
248
+ const numB = parseFloat(b);
249
+ if (!isNaN(numA) && !isNaN(numB)) {
250
+ return numA - numB;
251
+ }
252
+ return a.localeCompare(b);
253
+ });
254
+ sections.push(sizeTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
255
+ sections.push("\n\n");
256
+ }
257
+ if (tokens.radius && Object.keys(tokens.radius).length > 0) {
258
+ sections.push("### Radius Tokens\n\n");
259
+ const radiusTokens = Object.entries(tokens.radius).sort(([a], [b]) => {
260
+ const numA = parseFloat(a);
261
+ const numB = parseFloat(b);
262
+ if (!isNaN(numA) && !isNaN(numB)) {
263
+ return numA - numB;
264
+ }
265
+ return a.localeCompare(b);
266
+ });
267
+ sections.push(radiusTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
268
+ sections.push("\n\n");
269
+ }
270
+ if (tokens.zIndex && Object.keys(tokens.zIndex).length > 0) {
271
+ sections.push("### Z-Index Tokens\n\n");
272
+ const zIndexTokens = Object.entries(tokens.zIndex).sort(([a], [b]) => {
273
+ const numA = parseFloat(a);
274
+ const numB = parseFloat(b);
275
+ if (!isNaN(numA) && !isNaN(numB)) {
276
+ return numA - numB;
277
+ }
278
+ return a.localeCompare(b);
279
+ });
280
+ sections.push(zIndexTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
281
+ sections.push("\n\n");
282
+ }
283
+ let sampleColorBg = "blue-500";
284
+ let sampleColorText = "color";
285
+ if (tokens.color && Object.keys(tokens.color).length > 0) {
286
+ sections.push("### Color Tokens\n\n");
287
+ const colorKeys = Object.keys(tokens.color).sort();
288
+ const paletteMap = /* @__PURE__ */ new Map();
289
+ const standaloneColors = [];
290
+ for (const key of colorKeys) {
291
+ const match = key.match(/^([a-z]+)-(\d+)$/);
292
+ if (match) {
293
+ const [, palette, step] = match;
294
+ if (!paletteMap.has(palette)) {
295
+ paletteMap.set(palette, /* @__PURE__ */ new Set());
296
+ }
297
+ paletteMap.get(palette).add(step);
298
+ } else {
299
+ standaloneColors.push([key, tokens.color[key]]);
300
+ }
301
+ }
302
+ if (paletteMap.size >= 4) {
303
+ const palettes = Array.from(paletteMap.keys()).sort().join(", ");
304
+ sections.push(`**Tailwind Palettes (\`<name>-<50..950>\`):** ${palettes}
305
+
306
+ `);
307
+ if (standaloneColors.length > 0) {
308
+ sections.push("**Named Colors:**\n\n");
309
+ sections.push(standaloneColors.map(([k, v]) => `- \`${k}\`: ${formatTokenValue(v)}`).join("\n"));
310
+ sections.push("\n\n");
311
+ }
312
+ sampleColorBg = colorKeys.find((k) => k.startsWith("blue-")) || colorKeys[0];
313
+ sampleColorText = colorKeys.find((k) => k.startsWith("gray-") || k.startsWith("zinc-")) || colorKeys[1] || "color";
314
+ } else {
315
+ sections.push(colorKeys.map((key) => `- \`${key}\`: ${formatTokenValue(tokens.color[key])}`).join("\n"));
316
+ sections.push("\n\n");
317
+ sampleColorBg = colorKeys[0] || "background";
318
+ sampleColorText = colorKeys[1] || "color";
319
+ }
320
+ } else {
321
+ sampleColorBg = "background";
322
+ sampleColorText = "color";
323
+ }
324
+ sections.push("### Token Usage\n\n");
325
+ sections.push("Tokens can be used in component props by their bare names:\n\n");
326
+ const paddingProp = getPropName("padding");
327
+ const gapProp = getPropName("gap");
328
+ const marginProp = getPropName("margin");
329
+ const widthProp = getPropName("width");
330
+ const heightProp = getPropName("height");
331
+ const radiusProp = getPropName("borderRadius");
332
+ const sampleRadius = tokens.radius?.md ? "md" : Object.keys(tokens.radius || {})[0] || "0px";
333
+ if (syntaxChoice === "string") {
334
+ sections.push("```tsx\n");
335
+ sections.push(`// Space tokens - for margin, padding, gap
336
+ <View ${paddingProp}="4 sm:6" ${gapProp}="2" ${marginProp}="3" />
337
+
338
+ `);
339
+ sections.push(`// Size tokens - for width, height, dimensions
340
+ <View ${widthProp}="10 sm:12" ${heightProp}="6" />
129
341
 
130
342
  `);
131
- sections.push("Enables web-specific container query optimizations.\n\n");
132
- }
133
- const configString = JSON.stringify(config.tamaguiConfig);
134
- if (configString.includes("semi-strict-web")) {
135
- sections.push("### Mode: `semi-strict-web`\n\n");
136
- sections.push("This configuration uses semi-strict-web mode, which:\n");
137
- sections.push("- Optimizes for web performance\n");
138
- sections.push("- May have limited React Native API support\n");
139
- sections.push("- Focuses on web-first development\n\n");
140
- }
141
- }
142
- const componentsSection = [];
143
- const allComponents = [];
144
- for (const componentModule of config.components) {
145
- const componentNames = Object.keys(componentModule.nameToInfo);
146
- allComponents.push(...componentNames);
147
- }
148
- const componentGroups = /* @__PURE__ */new Map();
149
- const processed = /* @__PURE__ */new Set();
150
- const sortedComponents = [...allComponents].sort((a, b) => a.length - b.length);
151
- for (const name of sortedComponents) {
152
- if (processed.has(name)) continue;
153
- const children = allComponents.filter(other => other !== name && other.startsWith(name) && other[name.length]?.match(/[A-Z]/));
154
- if (children.length > 0) {
155
- componentGroups.set(name, new Set(children));
156
- processed.add(name);
157
- children.forEach(child => processed.add(child));
158
- }
159
- }
160
- const standaloneComponents = allComponents.filter(name => !processed.has(name));
161
- componentsSection.push("## Components\n\n");
162
- componentsSection.push("The following components are available:\n\n");
163
- const allBaseComponents = [...standaloneComponents, ...Array.from(componentGroups.keys())].sort();
164
- for (const name of allBaseComponents) {
165
- componentsSection.push(`- ${name}
166
- `);
167
- if (componentGroups.has(name)) {
168
- const children = Array.from(componentGroups.get(name)).sort();
169
- for (const child of children) {
170
- const suffix = child.slice(name.length);
171
- componentsSection.push(` - ${name}.${suffix}
172
- `);
173
- }
174
- }
175
- }
176
- componentsSection.push("\n");
177
- sections.push("## Shorthand Properties\n\n");
178
- sections.push("These shorthand properties are available for styling:\n\n");
179
- const shorthandEntries = Object.entries(shorthands).sort(([a], [b]) => a.localeCompare(b));
180
- sections.push(shorthandEntries.map(([short, full]) => `- \`${short}\` \u2192 \`${full}\``).join("\n"));
181
- sections.push("\n\n");
182
- sections.push("## Themes\n\n");
183
- const themes = config.tamaguiConfig?.themes || {};
184
- const themeNames = Object.keys(themes).sort();
185
- const hierarchy = {
186
- level1: /* @__PURE__ */new Set(),
187
- level2: /* @__PURE__ */new Set(),
188
- level3: /* @__PURE__ */new Set(),
189
- components: /* @__PURE__ */new Set()
190
- };
191
- for (const themeName of themeNames) {
192
- const parts = themeName.split("_");
193
- if (parts[0] === "light" || parts[0] === "dark") {
194
- hierarchy.level1.add(parts[0]);
195
- if (parts.length > 1 && parts[1] && !parts[1].startsWith("alt") && parts[1] !== "active") {
196
- if (parts[1][0] === parts[1][0].toLowerCase()) {
197
- hierarchy.level2.add(parts[1]);
198
- }
199
- }
200
- for (const part of parts) {
201
- if (part.startsWith("alt") || part === "active") {
202
- hierarchy.level3.add(part);
203
- }
204
- }
205
- for (const part of parts) {
206
- if (part[0] && part[0] === part[0].toUpperCase() && part[0] !== part[0].toLowerCase()) {
207
- hierarchy.components.add(part);
208
- }
209
- }
210
- } else {
211
- if (parts.length === 1) {
212
- hierarchy.level1.add(themeName);
213
- }
214
- }
215
- }
216
- sections.push("Themes are organized hierarchically and can be combined:\n\n");
217
- if (hierarchy.level1.size > 0) {
218
- sections.push("**Level 1 (Base):**\n\n");
219
- sections.push(Array.from(hierarchy.level1).sort().map(name => `- ${name}`).join("\n"));
220
- sections.push("\n\n");
221
- }
222
- if (hierarchy.level2.size > 0) {
223
- sections.push("**Level 2 (Color Schemes):**\n\n");
224
- sections.push(Array.from(hierarchy.level2).sort().map(name => `- ${name}`).join("\n"));
225
- sections.push("\n\n");
226
- }
227
- if (hierarchy.level3.size > 0) {
228
- sections.push("**Level 3 (Variants):**\n\n");
229
- sections.push(Array.from(hierarchy.level3).sort().map(name => `- ${name}`).join("\n"));
230
- sections.push("\n\n");
231
- }
232
- if (hierarchy.components.size > 0) {
233
- sections.push("**Component Themes:**\n\n");
234
- sections.push(Array.from(hierarchy.components).sort().map(name => `- ${name}`).join("\n"));
235
- sections.push("\n\n");
236
- }
237
- sections.push("### Theme Usage\n\n");
238
- sections.push("Themes are combined hierarchically. For example, `light_blue_alt1_Button` combines:\n");
239
- sections.push("- Base: `light`\n");
240
- sections.push("- Color: `blue`\n");
241
- sections.push("- Variant: `alt1`\n");
242
- sections.push("- Component: `Button`\n\n");
243
- sections.push("**Basic usage:**\n\n");
244
- sections.push("```tsx\n");
245
- sections.push("// Apply a theme to components\n");
246
- sections.push("export default () => (\n");
247
- sections.push(' <Theme name="dark">\n');
248
- sections.push(" <Button>I'm a dark button</Button>\n");
249
- sections.push(" </Theme>\n");
250
- sections.push(")\n\n");
251
- sections.push("// Themes nest and combine automatically\n");
252
- sections.push("export default () => (\n");
253
- sections.push(' <Theme name="dark">\n');
254
- sections.push(' <Theme name="blue">\n');
255
- sections.push(" <Button>Uses dark_blue theme</Button>\n");
256
- sections.push(" </Theme>\n");
257
- sections.push(" </Theme>\n");
258
- sections.push(")\n");
259
- sections.push("```\n\n");
260
- sections.push("**Accessing theme values:**\n\n");
261
- sections.push("Components can access theme values using `$` token syntax:\n\n");
262
- sections.push("```tsx\n");
263
- sections.push(`<View ${getPropName("backgroundColor")}="$background" ${getPropName("color")}="$color" />
264
- `);
265
- sections.push("```\n\n");
266
- sections.push("**Special props:**\n\n");
267
- sections.push("- `inverse`: Automatically swaps light \u2194 dark themes\n");
268
- sections.push("- `reset`: Reverts to grandparent theme\n\n");
269
- sections.push("## Tokens\n\n");
270
- sections.push("Tokens are design system values that can be referenced using the `$` prefix.\n\n");
271
- const tokens = config.tamaguiConfig?.tokens || {};
272
- if (tokens.space) {
273
- sections.push("### Space Tokens\n\n");
274
- const spaceTokens = Object.entries(tokens.space).sort(([a], [b]) => {
275
- const numA = parseFloat(a);
276
- const numB = parseFloat(b);
277
- if (!isNaN(numA) && !isNaN(numB)) {
278
- return numA - numB;
279
- }
280
- return a.localeCompare(b);
281
- });
282
- sections.push(spaceTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
283
- sections.push("\n\n");
284
- }
285
- if (tokens.size) {
286
- sections.push("### Size Tokens\n\n");
287
- const sizeTokens = Object.entries(tokens.size).sort(([a], [b]) => {
288
- const numA = parseFloat(a);
289
- const numB = parseFloat(b);
290
- if (!isNaN(numA) && !isNaN(numB)) {
291
- return numA - numB;
292
- }
293
- return a.localeCompare(b);
294
- });
295
- sections.push(sizeTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
296
- sections.push("\n\n");
297
- }
298
- if (tokens.radius) {
299
- sections.push("### Radius Tokens\n\n");
300
- const radiusTokens = Object.entries(tokens.radius).sort(([a], [b]) => {
301
- const numA = parseFloat(a);
302
- const numB = parseFloat(b);
303
- if (!isNaN(numA) && !isNaN(numB)) {
304
- return numA - numB;
305
- }
306
- return a.localeCompare(b);
307
- });
308
- sections.push(radiusTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
309
- sections.push("\n\n");
310
- }
311
- if (tokens.zIndex) {
312
- sections.push("### Z-Index Tokens\n\n");
313
- const zIndexTokens = Object.entries(tokens.zIndex).sort(([a], [b]) => {
314
- const numA = parseFloat(a);
315
- const numB = parseFloat(b);
316
- if (!isNaN(numA) && !isNaN(numB)) {
317
- return numA - numB;
318
- }
319
- return a.localeCompare(b);
320
- });
321
- sections.push(zIndexTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
322
- sections.push("\n\n");
323
- }
324
- if (tokens.color) {
325
- sections.push("### Color Tokens\n\n");
326
- const colorTokens = Object.entries(tokens.color).sort(([a], [b]) => a.localeCompare(b));
327
- sections.push(colorTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
328
- sections.push("\n\n");
329
- }
330
- sections.push("### Token Usage\n\n");
331
- sections.push("Tokens can be used in component props with the `$` prefix:\n\n");
332
- sections.push("```tsx\n");
333
- sections.push("// Space tokens - for margin, padding, gap\n");
334
- sections.push(`<View ${getPropName("padding")}="$4" ${getPropName("gap")}="$2" ${getPropName("margin")}="$3" />
343
+ sections.push(`// Color tokens - for colors and backgrounds
344
+ <View ${bgProp}="${sampleColorBg} hover:${sampleColorBg}" ${colorProp}="${sampleColorText}" />
335
345
 
336
346
  `);
337
- sections.push("// Size tokens - for width, height, dimensions\n");
338
- sections.push(`<View ${getPropName("width")}="$10" ${getPropName("height")}="$6" />
347
+ sections.push(`// Radius tokens - for border-radius
348
+ <View ${radiusProp}="${sampleRadius}" />
349
+ `);
350
+ sections.push("```\n\n");
351
+ } else if (syntaxChoice === "object") {
352
+ sections.push("```tsx\n");
353
+ sections.push(`// Space tokens - for margin, padding, gap
354
+ <View ${paddingProp}={{ default: '4', sm: '6' }} ${gapProp}="2" ${marginProp}="3" />
355
+
356
+ `);
357
+ sections.push(`// Size tokens - for width, height, dimensions
358
+ <View ${widthProp}={{ default: '10', sm: '12' }} ${heightProp}="6" />
359
+
360
+ `);
361
+ sections.push(`// Color tokens - for colors and backgrounds
362
+ <View ${bgProp}={{ default: '${sampleColorBg}', hover: '${sampleColorBg}' }} ${colorProp}={{ default: '${sampleColorText}' }} />
339
363
 
340
364
  `);
341
- sections.push("// Color tokens - for colors and backgrounds\n");
342
- sections.push(`<View ${getPropName("backgroundColor")}="$blue5" ${getPropName("color")}="$gray12" />
365
+ sections.push(`// Radius tokens - for border-radius
366
+ <View ${radiusProp}="${sampleRadius}" />
367
+ `);
368
+ sections.push("```\n\n");
369
+ } else {
370
+ sections.push("```tsx\n");
371
+ sections.push("// String form\n");
372
+ sections.push(`<View ${paddingProp}="4 sm:6" ${widthProp}="10 sm:12" ${bgProp}="${sampleColorBg}" />
373
+
374
+ `);
375
+ sections.push("// Object form\n");
376
+ sections.push(`<View ${paddingProp}={{ default: '4', sm: '6' }} ${widthProp}={{ default: '10', sm: '12' }} ${bgProp}={{ default: '${sampleColorBg}' }} />
343
377
 
344
378
  `);
345
- sections.push("// Radius tokens - for border-radius\n");
346
- sections.push(`<View ${getPropName("borderRadius")}="$4" />
347
- `);
348
- sections.push("```\n\n");
349
- if (config.tamaguiConfig?.media) {
350
- sections.push("## Media Queries\n\n");
351
- sections.push("Available responsive breakpoints:\n\n");
352
- const media = config.tamaguiConfig.media;
353
- const mediaEntries = Object.entries(media).sort(([a], [b]) => a.localeCompare(b));
354
- for (const [name, query] of mediaEntries) {
355
- sections.push(`- **${name}**: ${JSON.stringify(query)}
356
- `);
357
- }
358
- sections.push("\n");
359
- sections.push("### Media Query Usage\n\n");
360
- sections.push("Media queries can be used as style props or with the `useMedia` hook:\n\n");
361
- sections.push("```tsx\n");
362
- sections.push("// As style props (prefix with $)\n");
363
- const firstMediaName = mediaEntries[0]?.[0];
364
- if (firstMediaName) {
365
- sections.push(`<View ${getPropName("width")}="100%" $${firstMediaName}={{ ${getPropName("width")}: "50%" }} />
379
+ sections.push(`// Space and radius tokens
380
+ <View ${gapProp}="2" ${marginProp}="3" ${heightProp}="6" ${radiusProp}="${sampleRadius}" />
381
+ `);
382
+ sections.push("```\n\n");
383
+ }
384
+ if (config.tamaguiConfig?.media) {
385
+ sections.push("## Media Queries\n\n");
386
+ sections.push("Available responsive breakpoints:\n\n");
387
+ const media = config.tamaguiConfig.media;
388
+ const mediaEntries = Object.entries(media).sort(([a], [b]) => a.localeCompare(b));
389
+ for (const [name, query] of mediaEntries) {
390
+ sections.push(`- **${name}**: ${formatMediaQuery(query)}
391
+ `);
392
+ }
393
+ sections.push("\n");
394
+ sections.push("### Media Query Usage\n\n");
395
+ sections.push("Media queries can be used as style props or with the `useMedia` hook:\n\n");
396
+ const representative = mediaEntries.find(([n]) => n === "sm" || n === "md")?.[0] || mediaEntries.find(([n]) => !n.includes("-"))?.[0] || mediaEntries[0]?.[0];
397
+ if (representative) {
398
+ const isIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(representative);
399
+ const mediaAccess = isIdentifier ? `media.${representative}` : `media['${representative}']`;
400
+ sections.push("```tsx\n");
401
+ if (syntaxChoice === "string") {
402
+ sections.push("// As a clause in the same style value\n");
403
+ sections.push(`<View ${widthProp}="100% ${representative}:50%" />
366
404
 
367
405
  `);
368
- }
369
- sections.push("// Using the useMedia hook\n");
370
- sections.push("const media = useMedia()\n");
371
- if (firstMediaName) {
372
- sections.push(`if (media.${firstMediaName}) {
373
- `);
374
- sections.push(" // Render for this breakpoint\n");
375
- sections.push("}\n");
376
- }
377
- sections.push("```\n\n");
378
- }
379
- if (config.tamaguiConfig?.fonts) {
380
- sections.push("## Fonts\n\n");
381
- sections.push("Available font families:\n\n");
382
- const fonts = config.tamaguiConfig.fonts;
383
- const fontNames = Object.keys(fonts).sort();
384
- sections.push(fontNames.map(name => `- ${name}`).join("\n"));
385
- sections.push("\n\n");
386
- }
387
- if (config.tamaguiConfig?.animations) {
388
- sections.push("## Animations\n\n");
389
- sections.push("Available animation presets:\n\n");
390
- const animations = config.tamaguiConfig.animations;
391
- if (animations.animations) {
392
- const animationNames = Object.keys(animations.animations).sort();
393
- sections.push(animationNames.map(name => `- ${name}`).join("\n"));
394
- sections.push("\n\n");
395
- }
396
- }
397
- sections.push(...componentsSection);
398
- return sections.join("");
406
+ } else if (syntaxChoice === "object") {
407
+ sections.push("// Using the object form\n");
408
+ sections.push(`<View ${widthProp}={{ default: '100%', '${representative}': '50%' }} />
409
+
410
+ `);
411
+ } else {
412
+ sections.push("// String form and object form\n");
413
+ sections.push(`<View ${widthProp}="100% ${representative}:50%" />
414
+ `);
415
+ sections.push(`<View ${widthProp}={{ default: '100%', '${representative}': '50%' }} />
416
+
417
+ `);
418
+ }
419
+ sections.push("// Using the useMedia hook\n");
420
+ sections.push("const media = useMedia()\n");
421
+ sections.push(`if (${mediaAccess}) {
422
+ `);
423
+ sections.push(" // Render for this breakpoint\n");
424
+ sections.push("}\n");
425
+ sections.push("```\n\n");
426
+ }
427
+ }
428
+ if (config.tamaguiConfig?.fonts) {
429
+ sections.push("## Fonts\n\n");
430
+ sections.push("Available font families:\n\n");
431
+ const fonts = config.tamaguiConfig.fonts;
432
+ const fontNames = Object.keys(fonts).sort();
433
+ sections.push(fontNames.map((name) => `- ${name}`).join("\n"));
434
+ sections.push("\n\n");
435
+ }
436
+ if (config.tamaguiConfig?.animations) {
437
+ sections.push("## Animations\n\n");
438
+ sections.push("Available animation presets:\n\n");
439
+ const animations = config.tamaguiConfig.animations;
440
+ if (animations.animations) {
441
+ const animationNames = Object.keys(animations.animations).sort();
442
+ sections.push(animationNames.map((name) => `- ${name}`).join("\n"));
443
+ sections.push("\n\n");
444
+ }
445
+ }
446
+ const componentsSection = [];
447
+ const componentSet = /* @__PURE__ */ new Set();
448
+ let barrelExported = false;
449
+ const candidateModules = ["tamagui", ...(config.components || []).map((c) => c.moduleName)];
450
+ for (const modName of candidateModules) {
451
+ if (!modName) continue;
452
+ try {
453
+ const mod = require(modName);
454
+ for (const [key, val] of Object.entries(mod)) {
455
+ if (/^[A-Z]/.test(key) && (typeof val === "function" || typeof val === "object" && val !== null)) {
456
+ if (!key.endsWith("Context") && !key.endsWith("Provider") && key !== "Fragment") {
457
+ componentSet.add(key);
458
+ barrelExported = true;
459
+ }
460
+ }
461
+ }
462
+ if (barrelExported) break;
463
+ } catch {}
464
+ }
465
+ if (!barrelExported && config.components) {
466
+ for (const componentModule of config.components) {
467
+ for (const name of Object.keys(componentModule.nameToInfo || {})) {
468
+ componentSet.add(name);
469
+ }
470
+ }
471
+ }
472
+ const allComponents = Array.from(componentSet).filter((name) => {
473
+ if (name.endsWith("Frame")) {
474
+ const base = name.replace(/Frame$/, "");
475
+ if (componentSet.has(base) || name.startsWith("Popper") || name.startsWith("DialogPortal") || name.startsWith("SelectScrollButton")) {
476
+ return false;
477
+ }
478
+ }
479
+ return true;
480
+ });
481
+ componentsSection.push("## Components\n\n");
482
+ componentsSection.push("Available named exports (import these names directly):\n\n");
483
+ for (const name of allComponents.sort()) {
484
+ componentsSection.push(`- ${name}
485
+ `);
486
+ }
487
+ componentsSection.push("\n");
488
+ sections.push(...componentsSection);
489
+ return sections.join("");
399
490
  }
400
491
  function formatTokenValue(value) {
401
- if (typeof value === "object" && value !== null && "val" in value) {
402
- return String(value.val);
403
- }
404
- return String(value);
405
- }
492
+ if (typeof value === "object" && value !== null && "val" in value) {
493
+ return String(value.val);
494
+ }
495
+ return String(value);
496
+ }
497
+ function formatMediaQuery(query) {
498
+ if (typeof query !== "object" || query === null) {
499
+ return String(query);
500
+ }
501
+ const parts = [];
502
+ if (query.minWidth !== void 0) {
503
+ parts.push(`min-width: ${query.minWidth}px (screens >= ${query.minWidth}px wide)`);
504
+ }
505
+ if (query.maxWidth !== void 0) {
506
+ parts.push(`max-width: ${query.maxWidth}px (screens <= ${query.maxWidth}px wide)`);
507
+ }
508
+ if (query.minHeight !== void 0) {
509
+ parts.push(`min-height: ${query.minHeight}px (screens >= ${query.minHeight}px tall)`);
510
+ }
511
+ if (query.maxHeight !== void 0) {
512
+ parts.push(`max-height: ${query.maxHeight}px (screens <= ${query.maxHeight}px tall)`);
513
+ }
514
+ if (query.hover !== void 0) {
515
+ parts.push(`pointer: hover (${query.hover})`);
516
+ }
517
+ if (query.pointer !== void 0) {
518
+ parts.push(`pointer: ${query.pointer}`);
519
+ }
520
+ if (query.prefersReducedMotion !== void 0) {
521
+ parts.push(`prefers-reduced-motion: ${query.prefersReducedMotion}`);
522
+ }
523
+ if (parts.length === 0) {
524
+ return JSON.stringify(query);
525
+ }
526
+ return parts.join(", ");
527
+ }