@tamagui/cli 3.0.0-beta.1093.1 → 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.
- package/dist/cli.cjs +23 -4
- package/dist/generate-prompt.cjs +328 -186
- package/dist/migrate.cjs +11 -1
- package/dist/setup-prompt.cjs +73 -14
- package/dist/upgrade.cjs +29 -21
- package/package.json +9 -9
- package/src/cli.ts +18 -1
- package/src/generate-prompt.ts +426 -313
- package/src/migrate.ts +11 -1
- package/src/setup-prompt.ts +68 -15
- package/src/upgrade.ts +30 -33
- package/types/generate-prompt.d.ts +6 -2
- package/types/generate-prompt.d.ts.map +1 -1
- package/types/setup-prompt.d.ts +4 -2
- package/types/setup-prompt.d.ts.map +1 -1
- package/types/upgrade.d.ts.map +1 -1
package/dist/generate-prompt.cjs
CHANGED
|
@@ -25,7 +25,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
}) : target, mod));
|
|
26
26
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
27
|
var generate_prompt_exports = {};
|
|
28
|
-
__export(generate_prompt_exports, {
|
|
28
|
+
__export(generate_prompt_exports, {
|
|
29
|
+
generateMarkdown: () => generateMarkdown,
|
|
30
|
+
generatePrompt: () => generatePrompt
|
|
31
|
+
});
|
|
29
32
|
module.exports = __toCommonJS(generate_prompt_exports);
|
|
30
33
|
var import_node_path = require("node:path");
|
|
31
34
|
var FS = __toESM(require("fs-extra"));
|
|
@@ -36,20 +39,24 @@ async function generatePrompt(options) {
|
|
|
36
39
|
await loadTamagui({
|
|
37
40
|
...options.tamaguiOptions,
|
|
38
41
|
platform: "web"
|
|
39
|
-
});
|
|
42
|
+
}, true);
|
|
40
43
|
const configPath = (0, import_node_path.join)(paths.dotDir, "tamagui.config.json");
|
|
41
44
|
if (!FS.existsSync(configPath)) {
|
|
42
45
|
throw new Error(`Config file not found at ${configPath}. Please run 'tamagui generate' first.`);
|
|
43
46
|
}
|
|
44
47
|
const config = await FS.readJSON(configPath);
|
|
45
|
-
const
|
|
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 });
|
|
46
53
|
const outputPath = output || (0, import_node_path.join)(process.cwd(), "tamagui-prompt.md");
|
|
47
54
|
await FS.writeFile(outputPath, markdown, "utf-8");
|
|
48
55
|
console.info(`
|
|
49
56
|
\u2713 Generated prompt file at ${outputPath}
|
|
50
57
|
`);
|
|
51
58
|
}
|
|
52
|
-
function generateMarkdown(config) {
|
|
59
|
+
function generateMarkdown(config, options) {
|
|
53
60
|
const sections = [];
|
|
54
61
|
sections.push("# Tamagui Configuration\n\n");
|
|
55
62
|
sections.push("This document provides an overview of the Tamagui configuration for this project.\n\n");
|
|
@@ -60,196 +67,168 @@ function generateMarkdown(config) {
|
|
|
60
67
|
}
|
|
61
68
|
const getPropName = (fullProp) => {
|
|
62
69
|
const settings2 = config.tamaguiConfig?.settings || {};
|
|
63
|
-
if (settings2.onlyAllowShorthands
|
|
64
|
-
return reverseShorthands[fullProp];
|
|
70
|
+
if (settings2.onlyAllowShorthands) {
|
|
71
|
+
if (reverseShorthands[fullProp]) return reverseShorthands[fullProp];
|
|
72
|
+
if (fullProp === "backgroundColor" && reverseShorthands["background"]) {
|
|
73
|
+
return reverseShorthands["background"];
|
|
74
|
+
}
|
|
65
75
|
}
|
|
66
76
|
return fullProp;
|
|
67
77
|
};
|
|
68
78
|
const settings = config.tamaguiConfig?.settings || {};
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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}\`
|
|
74
84
|
|
|
75
85
|
`);
|
|
76
|
-
|
|
86
|
+
sections.push(`All text components will use the "${settings.defaultFont}" font family by default.
|
|
77
87
|
|
|
78
88
|
`);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
}
|
|
90
|
+
if (settings.onlyAllowShorthands !== void 0) {
|
|
91
|
+
sections.push(`### Only Allow Shorthands: \`${settings.onlyAllowShorthands}\`
|
|
82
92
|
|
|
83
93
|
`);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
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");
|
|
93
102
|
}
|
|
94
|
-
|
|
95
|
-
|
|
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}\`
|
|
96
116
|
|
|
97
117
|
`);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
118
|
+
if (settings.addThemeClassName === "html") {
|
|
119
|
+
sections.push("Theme classes are applied to the root HTML element.\n\n");
|
|
101
120
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
121
|
+
}
|
|
122
|
+
if (settings.allowedStyleValues) {
|
|
123
|
+
sections.push("### Allowed Style Values\n\n");
|
|
124
|
+
sections.push(`Type validation: \`${JSON.stringify(settings.allowedStyleValues)}\`.
|
|
105
125
|
|
|
106
126
|
`);
|
|
107
|
-
|
|
108
|
-
sections.push("This project is configured for **web only**.\n\n");
|
|
109
|
-
} else if (platform === "native") {
|
|
110
|
-
sections.push("This project is configured for **React Native only**.\n\n");
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
const configString = JSON.stringify(config.tamaguiConfig);
|
|
114
|
-
if (configString.includes("semi-strict-web")) {
|
|
115
|
-
sections.push("### Mode: `semi-strict-web`\n\n");
|
|
116
|
-
sections.push("This configuration uses semi-strict-web mode, which:\n");
|
|
117
|
-
sections.push("- Optimizes for web performance\n");
|
|
118
|
-
sections.push("- May have limited React Native API support\n");
|
|
119
|
-
sections.push("- Focuses on web-first development\n\n");
|
|
120
|
-
}
|
|
127
|
+
sections.push("Single-token values are type-checked. Run `tamagui check --strict` to also validate conditional payloads.\n\n");
|
|
121
128
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
const standaloneComponents = allComponents.filter((name) => !processed.has(name));
|
|
141
|
-
componentsSection.push("## Components\n\n");
|
|
142
|
-
componentsSection.push("The following components are available:\n\n");
|
|
143
|
-
const allBaseComponents = [...standaloneComponents, ...Array.from(componentGroups.keys())].sort();
|
|
144
|
-
for (const name of allBaseComponents) {
|
|
145
|
-
componentsSection.push(`- ${name}
|
|
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" />
|
|
146
145
|
`);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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' }} />
|
|
152
151
|
`);
|
|
153
|
-
|
|
154
|
-
|
|
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");
|
|
155
164
|
}
|
|
156
|
-
componentsSection.push("\n");
|
|
157
165
|
sections.push("## Shorthand Properties\n\n");
|
|
158
166
|
sections.push("These shorthand properties are available for styling:\n\n");
|
|
159
167
|
const shorthandEntries = Object.entries(shorthands).sort(([a], [b]) => a.localeCompare(b));
|
|
160
168
|
sections.push(shorthandEntries.map(([short, full]) => `- \`${short}\` \u2192 \`${full}\``).join("\n"));
|
|
161
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
|
+
}
|
|
162
187
|
sections.push("## Themes\n\n");
|
|
163
188
|
const themes = config.tamaguiConfig?.themes || {};
|
|
164
189
|
const themeNames = Object.keys(themes).sort();
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (parts[1][0] === parts[1][0].toLowerCase()) {
|
|
177
|
-
hierarchy.level2.add(parts[1]);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
for (const part of parts) {
|
|
181
|
-
if (part.startsWith("alt") || part === "active") {
|
|
182
|
-
hierarchy.level3.add(part);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
for (const part of parts) {
|
|
186
|
-
if (part[0] && part[0] === part[0].toUpperCase() && part[0] !== part[0].toLowerCase()) {
|
|
187
|
-
hierarchy.components.add(part);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
} else {
|
|
191
|
-
if (parts.length === 1) {
|
|
192
|
-
hierarchy.level1.add(themeName);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
sections.push("Themes are organized hierarchically and can be combined:\n\n");
|
|
197
|
-
if (hierarchy.level1.size > 0) {
|
|
198
|
-
sections.push("**Level 1 (Base):**\n\n");
|
|
199
|
-
sections.push(Array.from(hierarchy.level1).sort().map((name) => `- ${name}`).join("\n"));
|
|
200
|
-
sections.push("\n\n");
|
|
201
|
-
}
|
|
202
|
-
if (hierarchy.level2.size > 0) {
|
|
203
|
-
sections.push("**Level 2 (Color Schemes):**\n\n");
|
|
204
|
-
sections.push(Array.from(hierarchy.level2).sort().map((name) => `- ${name}`).join("\n"));
|
|
205
|
-
sections.push("\n\n");
|
|
206
|
-
}
|
|
207
|
-
if (hierarchy.level3.size > 0) {
|
|
208
|
-
sections.push("**Level 3 (Variants):**\n\n");
|
|
209
|
-
sections.push(Array.from(hierarchy.level3).sort().map((name) => `- ${name}`).join("\n"));
|
|
210
|
-
sections.push("\n\n");
|
|
211
|
-
}
|
|
212
|
-
if (hierarchy.components.size > 0) {
|
|
213
|
-
sections.push("**Component Themes:**\n\n");
|
|
214
|
-
sections.push(Array.from(hierarchy.components).sort().map((name) => `- ${name}`).join("\n"));
|
|
215
|
-
sections.push("\n\n");
|
|
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
|
+
`);
|
|
216
201
|
}
|
|
217
|
-
sections.push("### Theme Usage\n\n");
|
|
218
|
-
sections.push("Themes are combined hierarchically. For example, `light_blue_alt1_Button` combines:\n");
|
|
219
|
-
sections.push("- Base: `light`\n");
|
|
220
|
-
sections.push("- Color: `blue`\n");
|
|
221
|
-
sections.push("- Variant: `alt1`\n");
|
|
222
|
-
sections.push("- Component: `Button`\n\n");
|
|
223
|
-
sections.push("**Basic usage:**\n\n");
|
|
224
|
-
sections.push("```tsx\n");
|
|
225
|
-
sections.push("// Apply a theme to components\n");
|
|
226
|
-
sections.push("export default () => (\n");
|
|
227
|
-
sections.push(" <Theme name=\"dark\">\n");
|
|
228
|
-
sections.push(" <Button>I'm a dark button</Button>\n");
|
|
229
|
-
sections.push(" </Theme>\n");
|
|
230
|
-
sections.push(")\n\n");
|
|
231
|
-
sections.push("// Themes nest and combine automatically\n");
|
|
232
|
-
sections.push("export default () => (\n");
|
|
233
|
-
sections.push(" <Theme name=\"dark\">\n");
|
|
234
|
-
sections.push(" <Theme name=\"blue\">\n");
|
|
235
|
-
sections.push(" <Button>Uses dark_blue theme</Button>\n");
|
|
236
|
-
sections.push(" </Theme>\n");
|
|
237
|
-
sections.push(" </Theme>\n");
|
|
238
|
-
sections.push(")\n");
|
|
239
|
-
sections.push("```\n\n");
|
|
240
202
|
sections.push("**Accessing theme values:**\n\n");
|
|
241
203
|
sections.push("Components access theme values by their bare names:\n\n");
|
|
242
|
-
|
|
243
|
-
|
|
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" />
|
|
244
208
|
`);
|
|
245
|
-
|
|
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
|
+
}
|
|
246
226
|
sections.push("**Special props:**\n\n");
|
|
247
227
|
sections.push("- `theme=\"inverse\"`: Uses the opposite light or dark sub-theme\n");
|
|
248
|
-
sections.push("- `reset`: Reverts to grandparent theme\n\n");
|
|
249
228
|
sections.push("## Tokens\n\n");
|
|
250
229
|
sections.push("Tokens are design system values referenced by their bare names.\n\n");
|
|
251
230
|
const tokens = config.tamaguiConfig?.tokens || {};
|
|
252
|
-
if (tokens.space) {
|
|
231
|
+
if (tokens.space && Object.keys(tokens.space).length > 0) {
|
|
253
232
|
sections.push("### Space Tokens\n\n");
|
|
254
233
|
const spaceTokens = Object.entries(tokens.space).sort(([a], [b]) => {
|
|
255
234
|
const numA = parseFloat(a);
|
|
@@ -262,7 +241,7 @@ function generateMarkdown(config) {
|
|
|
262
241
|
sections.push(spaceTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
|
|
263
242
|
sections.push("\n\n");
|
|
264
243
|
}
|
|
265
|
-
if (tokens.size) {
|
|
244
|
+
if (tokens.size && Object.keys(tokens.size).length > 0) {
|
|
266
245
|
sections.push("### Size Tokens\n\n");
|
|
267
246
|
const sizeTokens = Object.entries(tokens.size).sort(([a], [b]) => {
|
|
268
247
|
const numA = parseFloat(a);
|
|
@@ -275,7 +254,7 @@ function generateMarkdown(config) {
|
|
|
275
254
|
sections.push(sizeTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
|
|
276
255
|
sections.push("\n\n");
|
|
277
256
|
}
|
|
278
|
-
if (tokens.radius) {
|
|
257
|
+
if (tokens.radius && Object.keys(tokens.radius).length > 0) {
|
|
279
258
|
sections.push("### Radius Tokens\n\n");
|
|
280
259
|
const radiusTokens = Object.entries(tokens.radius).sort(([a], [b]) => {
|
|
281
260
|
const numA = parseFloat(a);
|
|
@@ -288,7 +267,7 @@ function generateMarkdown(config) {
|
|
|
288
267
|
sections.push(radiusTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
|
|
289
268
|
sections.push("\n\n");
|
|
290
269
|
}
|
|
291
|
-
if (tokens.zIndex) {
|
|
270
|
+
if (tokens.zIndex && Object.keys(tokens.zIndex).length > 0) {
|
|
292
271
|
sections.push("### Z-Index Tokens\n\n");
|
|
293
272
|
const zIndexTokens = Object.entries(tokens.zIndex).sort(([a], [b]) => {
|
|
294
273
|
const numA = parseFloat(a);
|
|
@@ -301,60 +280,150 @@ function generateMarkdown(config) {
|
|
|
301
280
|
sections.push(zIndexTokens.map(([key, value]) => `- \`${key}\`: ${formatTokenValue(value)}`).join("\n"));
|
|
302
281
|
sections.push("\n\n");
|
|
303
282
|
}
|
|
304
|
-
|
|
283
|
+
let sampleColorBg = "blue-500";
|
|
284
|
+
let sampleColorText = "color";
|
|
285
|
+
if (tokens.color && Object.keys(tokens.color).length > 0) {
|
|
305
286
|
sections.push("### Color Tokens\n\n");
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
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";
|
|
309
323
|
}
|
|
310
324
|
sections.push("### Token Usage\n\n");
|
|
311
325
|
sections.push("Tokens can be used in component props by their bare names:\n\n");
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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" />
|
|
315
337
|
|
|
316
338
|
`);
|
|
317
|
-
|
|
318
|
-
|
|
339
|
+
sections.push(`// Size tokens - for width, height, dimensions
|
|
340
|
+
<View ${widthProp}="10 sm:12" ${heightProp}="6" />
|
|
319
341
|
|
|
320
342
|
`);
|
|
321
|
-
|
|
322
|
-
|
|
343
|
+
sections.push(`// Color tokens - for colors and backgrounds
|
|
344
|
+
<View ${bgProp}="${sampleColorBg} hover:${sampleColorBg}" ${colorProp}="${sampleColorText}" />
|
|
323
345
|
|
|
324
346
|
`);
|
|
325
|
-
|
|
326
|
-
|
|
347
|
+
sections.push(`// Radius tokens - for border-radius
|
|
348
|
+
<View ${radiusProp}="${sampleRadius}" />
|
|
327
349
|
`);
|
|
328
|
-
|
|
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}' }} />
|
|
363
|
+
|
|
364
|
+
`);
|
|
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}' }} />
|
|
377
|
+
|
|
378
|
+
`);
|
|
379
|
+
sections.push(`// Space and radius tokens
|
|
380
|
+
<View ${gapProp}="2" ${marginProp}="3" ${heightProp}="6" ${radiusProp}="${sampleRadius}" />
|
|
381
|
+
`);
|
|
382
|
+
sections.push("```\n\n");
|
|
383
|
+
}
|
|
329
384
|
if (config.tamaguiConfig?.media) {
|
|
330
385
|
sections.push("## Media Queries\n\n");
|
|
331
386
|
sections.push("Available responsive breakpoints:\n\n");
|
|
332
387
|
const media = config.tamaguiConfig.media;
|
|
333
388
|
const mediaEntries = Object.entries(media).sort(([a], [b]) => a.localeCompare(b));
|
|
334
389
|
for (const [name, query] of mediaEntries) {
|
|
335
|
-
sections.push(`- **${name}**: ${
|
|
390
|
+
sections.push(`- **${name}**: ${formatMediaQuery(query)}
|
|
336
391
|
`);
|
|
337
392
|
}
|
|
338
393
|
sections.push("\n");
|
|
339
394
|
sections.push("### Media Query Usage\n\n");
|
|
340
395
|
sections.push("Media queries can be used as style props or with the `useMedia` hook:\n\n");
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
sections.push(
|
|
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%" />
|
|
346
404
|
|
|
347
405
|
`);
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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}) {
|
|
353
422
|
`);
|
|
354
423
|
sections.push(" // Render for this breakpoint\n");
|
|
355
424
|
sections.push("}\n");
|
|
425
|
+
sections.push("```\n\n");
|
|
356
426
|
}
|
|
357
|
-
sections.push("```\n\n");
|
|
358
427
|
}
|
|
359
428
|
if (config.tamaguiConfig?.fonts) {
|
|
360
429
|
sections.push("## Fonts\n\n");
|
|
@@ -374,6 +443,48 @@ function generateMarkdown(config) {
|
|
|
374
443
|
sections.push("\n\n");
|
|
375
444
|
}
|
|
376
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");
|
|
377
488
|
sections.push(...componentsSection);
|
|
378
489
|
return sections.join("");
|
|
379
490
|
}
|
|
@@ -383,3 +494,34 @@ function formatTokenValue(value) {
|
|
|
383
494
|
}
|
|
384
495
|
return String(value);
|
|
385
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
|
+
}
|