stampd 0.1.0

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 (130) hide show
  1. package/README.md +611 -0
  2. package/dist/analyzer/analyzeStyle.d.ts +13 -0
  3. package/dist/analyzer/analyzeStyle.d.ts.map +1 -0
  4. package/dist/analyzer/analyzeStyle.js +43 -0
  5. package/dist/analyzer/analyzeStyle.js.map +1 -0
  6. package/dist/analyzer/classifyValue.d.ts +7 -0
  7. package/dist/analyzer/classifyValue.d.ts.map +1 -0
  8. package/dist/analyzer/classifyValue.js +30 -0
  9. package/dist/analyzer/classifyValue.js.map +1 -0
  10. package/dist/analyzer/isThemeAccess.d.ts +7 -0
  11. package/dist/analyzer/isThemeAccess.d.ts.map +1 -0
  12. package/dist/analyzer/isThemeAccess.js +17 -0
  13. package/dist/analyzer/isThemeAccess.js.map +1 -0
  14. package/dist/cli/runTransform.d.ts +2 -0
  15. package/dist/cli/runTransform.d.ts.map +1 -0
  16. package/dist/cli/runTransform.js +55 -0
  17. package/dist/cli/runTransform.js.map +1 -0
  18. package/dist/config/createTheme.d.ts +101 -0
  19. package/dist/config/createTheme.d.ts.map +1 -0
  20. package/dist/config/createTheme.js +61 -0
  21. package/dist/config/createTheme.js.map +1 -0
  22. package/dist/config/generateTypes.d.ts +2 -0
  23. package/dist/config/generateTypes.d.ts.map +1 -0
  24. package/dist/config/generateTypes.js +103 -0
  25. package/dist/config/generateTypes.js.map +1 -0
  26. package/dist/config/loadStyledConfig.d.ts +13 -0
  27. package/dist/config/loadStyledConfig.d.ts.map +1 -0
  28. package/dist/config/loadStyledConfig.js +175 -0
  29. package/dist/config/loadStyledConfig.js.map +1 -0
  30. package/dist/context/Provider.d.ts +40 -0
  31. package/dist/context/Provider.d.ts.map +1 -0
  32. package/dist/context/Provider.js +141 -0
  33. package/dist/context/Provider.js.map +1 -0
  34. package/dist/core/shouldTransform.d.ts +6 -0
  35. package/dist/core/shouldTransform.d.ts.map +1 -0
  36. package/dist/core/shouldTransform.js +28 -0
  37. package/dist/core/shouldTransform.js.map +1 -0
  38. package/dist/detectors/isStyledCall.d.ts +10 -0
  39. package/dist/detectors/isStyledCall.d.ts.map +1 -0
  40. package/dist/detectors/isStyledCall.js +20 -0
  41. package/dist/detectors/isStyledCall.js.map +1 -0
  42. package/dist/detectors/isStyledImported.d.ts +9 -0
  43. package/dist/detectors/isStyledImported.d.ts.map +1 -0
  44. package/dist/detectors/isStyledImported.js +27 -0
  45. package/dist/detectors/isStyledImported.js.map +1 -0
  46. package/dist/extractors/extractAttrs.d.ts +13 -0
  47. package/dist/extractors/extractAttrs.d.ts.map +1 -0
  48. package/dist/extractors/extractAttrs.js +40 -0
  49. package/dist/extractors/extractAttrs.js.map +1 -0
  50. package/dist/extractors/extractStyle.d.ts +24 -0
  51. package/dist/extractors/extractStyle.d.ts.map +1 -0
  52. package/dist/extractors/extractStyle.js +57 -0
  53. package/dist/extractors/extractStyle.js.map +1 -0
  54. package/dist/extractors/extractVariants.d.ts +11 -0
  55. package/dist/extractors/extractVariants.d.ts.map +1 -0
  56. package/dist/extractors/extractVariants.js +31 -0
  57. package/dist/extractors/extractVariants.js.map +1 -0
  58. package/dist/extractors/getStyledConfig.d.ts +9 -0
  59. package/dist/extractors/getStyledConfig.d.ts.map +1 -0
  60. package/dist/extractors/getStyledConfig.js +17 -0
  61. package/dist/extractors/getStyledConfig.js.map +1 -0
  62. package/dist/index.d.ts +7 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +36 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/runtime/Styled.d.ts +48 -0
  67. package/dist/runtime/Styled.d.ts.map +1 -0
  68. package/dist/runtime/Styled.js +17 -0
  69. package/dist/runtime/Styled.js.map +1 -0
  70. package/dist/theme/astFromValue.d.ts +3 -0
  71. package/dist/theme/astFromValue.d.ts.map +1 -0
  72. package/dist/theme/astFromValue.js +22 -0
  73. package/dist/theme/astFromValue.js.map +1 -0
  74. package/dist/theme/extractThemePath.d.ts +7 -0
  75. package/dist/theme/extractThemePath.d.ts.map +1 -0
  76. package/dist/theme/extractThemePath.js +21 -0
  77. package/dist/theme/extractThemePath.js.map +1 -0
  78. package/dist/theme/resolveExpression.d.ts +7 -0
  79. package/dist/theme/resolveExpression.d.ts.map +1 -0
  80. package/dist/theme/resolveExpression.js +46 -0
  81. package/dist/theme/resolveExpression.js.map +1 -0
  82. package/dist/theme/resolveThemeInStyle.d.ts +6 -0
  83. package/dist/theme/resolveThemeInStyle.d.ts.map +1 -0
  84. package/dist/theme/resolveThemeInStyle.js +26 -0
  85. package/dist/theme/resolveThemeInStyle.js.map +1 -0
  86. package/dist/theme/resolveThemePath.d.ts +8 -0
  87. package/dist/theme/resolveThemePath.d.ts.map +1 -0
  88. package/dist/theme/resolveThemePath.js +22 -0
  89. package/dist/theme/resolveThemePath.js.map +1 -0
  90. package/dist/transformers/transformStyled.d.ts +7 -0
  91. package/dist/transformers/transformStyled.d.ts.map +1 -0
  92. package/dist/transformers/transformStyled.js +104 -0
  93. package/dist/transformers/transformStyled.js.map +1 -0
  94. package/dist/transformers/transformStyledWithVariants.d.ts +22 -0
  95. package/dist/transformers/transformStyledWithVariants.d.ts.map +1 -0
  96. package/dist/transformers/transformStyledWithVariants.js +197 -0
  97. package/dist/transformers/transformStyledWithVariants.js.map +1 -0
  98. package/dist/utils/buildDefaultVariantsAST.d.ts +3 -0
  99. package/dist/utils/buildDefaultVariantsAST.d.ts.map +1 -0
  100. package/dist/utils/buildDefaultVariantsAST.js +41 -0
  101. package/dist/utils/buildDefaultVariantsAST.js.map +1 -0
  102. package/dist/utils/buildVariantsObjectAST.d.ts +3 -0
  103. package/dist/utils/buildVariantsObjectAST.d.ts.map +1 -0
  104. package/dist/utils/buildVariantsObjectAST.js +43 -0
  105. package/dist/utils/buildVariantsObjectAST.js.map +1 -0
  106. package/dist/utils/defaultFontStyle.d.ts +8 -0
  107. package/dist/utils/defaultFontStyle.d.ts.map +1 -0
  108. package/dist/utils/defaultFontStyle.js +32 -0
  109. package/dist/utils/defaultFontStyle.js.map +1 -0
  110. package/dist/utils/ensureImport.d.ts +13 -0
  111. package/dist/utils/ensureImport.d.ts.map +1 -0
  112. package/dist/utils/ensureImport.js +76 -0
  113. package/dist/utils/ensureImport.js.map +1 -0
  114. package/dist/utils/getProgramPath.d.ts +6 -0
  115. package/dist/utils/getProgramPath.d.ts.map +1 -0
  116. package/dist/utils/getProgramPath.js +10 -0
  117. package/dist/utils/getProgramPath.js.map +1 -0
  118. package/dist/utils/nodeHasPlatformSelect.d.ts +3 -0
  119. package/dist/utils/nodeHasPlatformSelect.d.ts.map +1 -0
  120. package/dist/utils/nodeHasPlatformSelect.js +37 -0
  121. package/dist/utils/nodeHasPlatformSelect.js.map +1 -0
  122. package/dist/utils/nodeHasThemeAccess.d.ts +7 -0
  123. package/dist/utils/nodeHasThemeAccess.d.ts.map +1 -0
  124. package/dist/utils/nodeHasThemeAccess.js +36 -0
  125. package/dist/utils/nodeHasThemeAccess.js.map +1 -0
  126. package/dist/utils/replaceParamWithProps.d.ts +7 -0
  127. package/dist/utils/replaceParamWithProps.d.ts.map +1 -0
  128. package/dist/utils/replaceParamWithProps.js +34 -0
  129. package/dist/utils/replaceParamWithProps.js.map +1 -0
  130. package/package.json +95 -0
package/README.md ADDED
@@ -0,0 +1,611 @@
1
+ # stampd
2
+
3
+ React Native styling, stamped at compile time.
4
+
5
+ Design tokens resolved to literal values in your bundle — zero runtime overhead, full TypeScript, light / dark / high-contrast built in.
6
+
7
+ ```tsx
8
+ const Card = Styled.View({
9
+ style: ({ theme }) => ({
10
+ backgroundColor: theme.colors.surface,
11
+ padding: theme.spacing.md, // → 16
12
+ borderRadius: theme.radius.md, // → 8
13
+ }),
14
+ });
15
+ // ↓ Babel output
16
+ const Card = (props) => (
17
+ <View style={{ backgroundColor: theme.colors.surface, padding: 16, borderRadius: 8 }} {...props} />
18
+ );
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Table of Contents
24
+
25
+ - [Quick Start](#quick-start)
26
+ - [How it works](#how-it-works)
27
+ - [Installation](#installation)
28
+ - [Setup](#setup)
29
+ - [1. babel.config.js](#1-babelconfigjs)
30
+ - [2. stampd.config.ts](#2-stampdconfigts)
31
+ - [3. tsconfig.json](#3-tsconfigjson)
32
+ - [4. StampdUIProvider](#4-wrap-your-app-with-stampdprovider)
33
+ - [Usage](#usage)
34
+ - [Simple component](#simple-component)
35
+ - [Static component](#static-component-no-runtime-cost)
36
+ - [Text with default font](#text-with-default-font-auto-injected)
37
+ - [Variants](#variants)
38
+ - [Dynamic props](#dynamic-props)
39
+ - [attrs — default props](#attrs--default-props)
40
+ - [theme.fonts](#using-themefont)
41
+ - [StampdUIProvider & useStampdUI](#stampdprovider--usestampd)
42
+ - [Switching themes](#switching-themes)
43
+ - [High contrast](#high-contrast)
44
+ - [Font scale](#font-scale)
45
+ - [Accessing the theme](#accessing-the-theme-directly)
46
+ - [createTheme API](#createtheme-api)
47
+ - [Style priority](#style-priority)
48
+ - [Generated types](#generated-types--stampd-typesd-ts)
49
+ - [CLI — inspect output](#cli--inspect-generated-output)
50
+ - [Supported components](#supported-components)
51
+ - [License](#license)
52
+
53
+ ---
54
+
55
+ ## Quick Start
56
+
57
+ ```bash
58
+ npm install stampd
59
+ ```
60
+
61
+ **1.** Add the plugin to `babel.config.js`:
62
+ ```js
63
+ module.exports = {
64
+ presets: ['babel-preset-expo'],
65
+ plugins: ['module:stampd'],
66
+ };
67
+ ```
68
+
69
+ **2.** Create `stampd.config.ts` at your project root:
70
+ ```ts
71
+ import { createTheme } from 'stampd/theme';
72
+
73
+ const light = { primary: '#2563EB', background: '#F8FAFC', text: '#0F172A' };
74
+ const dark = { primary: '#3B82F6', background: '#0F172A', text: '#F8FAFC' };
75
+
76
+ export const config = createTheme({
77
+ tokens: {
78
+ spacing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 },
79
+ radius: { sm: 4, md: 8, lg: 16, full: 9999 },
80
+ },
81
+ theme: {
82
+ light: { colors: light },
83
+ dark: { colors: dark },
84
+ },
85
+ fonts: {
86
+ default: { size: 14, family: 'Inter' },
87
+ },
88
+ });
89
+ ```
90
+
91
+ **3.** Add `stampd-types.d.ts` to `tsconfig.json`:
92
+ ```json
93
+ { "include": ["**/*.ts", "**/*.tsx", "stampd-types.d.ts"] }
94
+ ```
95
+
96
+ **4.** Wrap your app:
97
+ ```tsx
98
+ import { StampdUIProvider } from 'stampd/context';
99
+ import { config } from './stampd.config';
100
+
101
+ export default function App() {
102
+ return <StampdUIProvider config={config}><RootNavigator /></StampdUIProvider>;
103
+ }
104
+ ```
105
+
106
+ **5.** Start building:
107
+ ```tsx
108
+ import { Styled } from 'stampd/styled';
109
+
110
+ const Button = Styled.TouchableOpacity({
111
+ style: ({ theme }) => ({
112
+ backgroundColor: theme.colors.primary, // runtime — light/dark aware
113
+ padding: theme.spacing.md, // compile time → 16
114
+ borderRadius: theme.radius.sm, // compile time → 4
115
+ }),
116
+ });
117
+ ```
118
+
119
+ ---
120
+
121
+ ## How it works
122
+
123
+ stampd runs as a **Babel plugin** during your build:
124
+
125
+ 1. Reads `stampd.config.ts` from your project root at compile time.
126
+ 2. Resolves every `theme.spacing.md`, `theme.radius.sm`, `theme.fonts.sizes.lg`, etc. to its **literal value** and hardcodes it in the bundle.
127
+ 3. Colors and other values that differ between light / dark are kept as runtime references — `useStampdUI()` is injected **only when actually needed**.
128
+ 4. Variants are hoisted to module-level constants, allocated once.
129
+ 5. Generates `stampd-types.d.ts` with inline literal types for full IDE autocomplete.
130
+
131
+ ---
132
+
133
+ ## Installation
134
+
135
+ ```bash
136
+ npm install stampd
137
+ ```
138
+
139
+ ---
140
+
141
+ ## Setup
142
+
143
+ ### 1. babel.config.js
144
+
145
+ ```js
146
+ module.exports = function (api) {
147
+ // Re-run Babel when stampd.config.ts changes
148
+ const configPath = require('path').join(__dirname, 'stampd.config.ts');
149
+ try {
150
+ api.cache.using(() => require('fs').statSync(configPath).mtimeMs);
151
+ } catch {
152
+ api.cache(true);
153
+ }
154
+
155
+ return {
156
+ presets: ['babel-preset-expo'],
157
+ plugins: ['module:stampd'],
158
+ };
159
+ };
160
+ ```
161
+
162
+ Enable debug output to inspect generated code:
163
+
164
+ ```js
165
+ plugins: [['module:stampd', { debug: true }]],
166
+ ```
167
+
168
+ ---
169
+
170
+ ### 2. stampd.config.ts
171
+
172
+ Create `stampd.config.ts` at the **root of your project** (next to `package.json`):
173
+
174
+ ```ts
175
+ import { createTheme } from 'stampd/theme';
176
+
177
+ const lightColors = {
178
+ primary: '#2563EB',
179
+ background: '#F8FAFC',
180
+ surface: '#FFFFFF',
181
+ text: '#0F172A',
182
+ error: '#DC2626',
183
+ };
184
+
185
+ const darkColors = {
186
+ primary: '#3B82F6',
187
+ background: '#0F172A',
188
+ surface: '#1E293B',
189
+ text: '#F8FAFC',
190
+ error: '#EF4444',
191
+ };
192
+
193
+ export const config = createTheme({
194
+ // Resolved at compile time — hardcoded in the bundle
195
+ tokens: {
196
+ spacing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 },
197
+ radius: { sm: 4, md: 8, lg: 16, full: 9999 },
198
+ fontSize: { sm: 12, md: 14, lg: 16, xl: 20, xxl: 24 },
199
+ },
200
+
201
+ // Resolved at runtime — switch between light / dark / high-contrast
202
+ theme: {
203
+ light: { colors: lightColors },
204
+ dark: { colors: darkColors },
205
+ highContrast: { colors: { background: '#000000', text: '#FFFFFF' } }, // partial overrides
206
+ },
207
+
208
+ // Static font config — auto-injected into Text / TextInput
209
+ fonts: {
210
+ default: { size: 14, family: 'Inter' },
211
+ sizes: { sm: 12, md: 14, lg: 16, xl: 20 },
212
+ family: { inter: 'Inter', mono: 'JetBrainsMono' },
213
+ },
214
+ });
215
+ ```
216
+
217
+ `createTheme` validates at startup that `dark` has the same keys as `light`, warning if they diverge.
218
+
219
+ ---
220
+
221
+ ### 3. tsconfig.json
222
+
223
+ Include the auto-generated type file:
224
+
225
+ ```json
226
+ {
227
+ "include": ["**/*.ts", "**/*.tsx", "stampd-types.d.ts"]
228
+ }
229
+ ```
230
+
231
+ ---
232
+
233
+ ### 4. Wrap your app with StampdUIProvider
234
+
235
+ ```tsx
236
+ // App.tsx
237
+ import { StampdUIProvider } from 'stampd/context';
238
+ import { config } from './stampd.config';
239
+
240
+ export default function App() {
241
+ return (
242
+ <StampdUIProvider config={config}>
243
+ <RootNavigator />
244
+ </StampdUIProvider>
245
+ );
246
+ }
247
+ ```
248
+
249
+ ---
250
+
251
+ ## Usage
252
+
253
+ ### Simple component
254
+
255
+ ```tsx
256
+ import { Styled } from 'stampd';
257
+
258
+ const Card = Styled.View({
259
+ style: ({ theme }) => ({
260
+ backgroundColor: theme.colors.surface, // runtime — stays as ref
261
+ padding: theme.spacing.md, // compile time → 16
262
+ borderRadius: theme.radius.md, // compile time → 8
263
+ }),
264
+ });
265
+ ```
266
+
267
+ **Compile-time output:**
268
+
269
+ ```tsx
270
+ import { View } from 'react-native';
271
+
272
+ const Card = (props) => (
273
+ <View
274
+ style={[{ backgroundColor: theme.colors.surface }, { padding: 16, borderRadius: 8 }]}
275
+ {...props}
276
+ />
277
+ );
278
+ ```
279
+
280
+ ---
281
+
282
+ ### Static component (no runtime cost)
283
+
284
+ When there are no dynamic values, the output is a plain component — no context, no hook:
285
+
286
+ ```tsx
287
+ const Divider = Styled.View({
288
+ style: {
289
+ height: 1,
290
+ backgroundColor: '#E2E8F0',
291
+ marginVertical: 8,
292
+ },
293
+ });
294
+ ```
295
+
296
+ ---
297
+
298
+ ### Text with default font auto-injected
299
+
300
+ `Styled.Text` and `Styled.TextInput` automatically receive `fontFamily` and `fontSize` from `fonts.default` as the base layer. Your component styles take precedence:
301
+
302
+ ```tsx
303
+ const Label = Styled.Text({
304
+ style: ({ theme }) => ({
305
+ color: theme.colors.text,
306
+ fontWeight: '600',
307
+ }),
308
+ });
309
+ ```
310
+
311
+ **Output:**
312
+
313
+ ```tsx
314
+ <Text
315
+ style={[
316
+ { fontFamily: 'Inter', fontSize: 14 }, // ← auto from fonts.default
317
+ { color: theme.colors.text, fontWeight: '600' },
318
+ ]}
319
+ {...props}
320
+ />
321
+ ```
322
+
323
+ Override per-component by setting `fontFamily` / `fontSize` in your style — the last item wins.
324
+
325
+ ---
326
+
327
+ ### Variants
328
+
329
+ ```tsx
330
+ const Button = Styled.TouchableOpacity({
331
+ style: ({ theme }) => ({
332
+ backgroundColor: theme.colors.primary,
333
+ paddingVertical: theme.spacing.sm,
334
+ paddingHorizontal: theme.spacing.md,
335
+ borderRadius: theme.radius.md,
336
+ alignItems: 'center',
337
+ }),
338
+ variants: {
339
+ variant: {
340
+ outline: ({ theme }) => ({
341
+ backgroundColor: 'transparent',
342
+ borderWidth: 1,
343
+ borderColor: theme.colors.primary,
344
+ }),
345
+ ghost: () => ({
346
+ backgroundColor: 'transparent',
347
+ }),
348
+ danger: ({ theme }) => ({
349
+ backgroundColor: theme.colors.error,
350
+ }),
351
+ },
352
+ size: {
353
+ sm: ({ theme }) => ({
354
+ paddingVertical: theme.spacing.xs,
355
+ paddingHorizontal: theme.spacing.sm,
356
+ }),
357
+ full: () => ({ width: '100%' as const }),
358
+ },
359
+ },
360
+ });
361
+
362
+ // Usage — types are fully inferred:
363
+ <Button variant="outline" size="sm" onPress={handlePress} />
364
+ ```
365
+
366
+ Passing an invalid variant value (`variant="invalid"`) is a TypeScript error.
367
+
368
+ ---
369
+
370
+ ### Dynamic props
371
+
372
+ Any prop that is not a variant key is automatically forwarded and available in the style function:
373
+
374
+ ```tsx
375
+ const Tag = Styled.View({
376
+ style: ({ theme, selected }) => ({
377
+ backgroundColor: selected ? theme.colors.primary : theme.colors.surface,
378
+ borderWidth: selected ? 0 : 1,
379
+ paddingHorizontal: theme.spacing.sm,
380
+ borderRadius: theme.radius.full,
381
+ }),
382
+ });
383
+
384
+ <Tag selected={isActive} />
385
+ ```
386
+
387
+ ---
388
+
389
+ ### attrs — default props
390
+
391
+ ```tsx
392
+ const Input = Styled.TextInput({
393
+ style: ({ theme }) => ({
394
+ color: theme.colors.text,
395
+ padding: theme.spacing.sm,
396
+ }),
397
+ attrs: {
398
+ placeholderTextColor: '#94A3B8',
399
+ autoCapitalize: 'none',
400
+ },
401
+ });
402
+ ```
403
+
404
+ ---
405
+
406
+ ### Using theme.fonts
407
+
408
+ ```tsx
409
+ const Heading = Styled.Text({
410
+ style: ({ theme }) => ({
411
+ fontFamily: theme.fonts.family.inter, // compile time → "Inter"
412
+ fontSize: theme.fonts.sizes.xl, // compile time → 20
413
+ fontWeight: '700',
414
+ color: theme.colors.text,
415
+ }),
416
+ });
417
+ ```
418
+
419
+ `theme.fonts.*` is resolved at compile time — literal values are hardcoded in the output.
420
+
421
+ ---
422
+
423
+ ## StampdUIProvider & useStampdUI
424
+
425
+ ### Switching themes
426
+
427
+ ```tsx
428
+ import { useStampdUI, ThemeMode } from 'stampd/context';
429
+
430
+ function ThemeToggle() {
431
+ const { themeMode, setThemeMode } = useStampdUI();
432
+
433
+ return (
434
+ <Pressable onPress={() =>
435
+ setThemeMode(themeMode === ThemeMode.DARK ? ThemeMode.LIGHT : ThemeMode.DARK)
436
+ }>
437
+ <Text>Toggle theme</Text>
438
+ </Pressable>
439
+ );
440
+ }
441
+ ```
442
+
443
+ | `ThemeMode` | Behavior |
444
+ |------------------------|------------------------------------|
445
+ | `ThemeMode.SYSTEM` | Follows device setting *(default)* |
446
+ | `ThemeMode.LIGHT` | Always light |
447
+ | `ThemeMode.DARK` | Always dark |
448
+
449
+ ---
450
+
451
+ ### High contrast
452
+
453
+ ```tsx
454
+ const { highContrast, setHighContrast } = useStampdUI();
455
+
456
+ <Switch value={highContrast} onValueChange={setHighContrast} />
457
+ ```
458
+
459
+ When enabled, the `highContrast` colors from `stampd.config.ts` are **deep-merged** on top of the active light / dark theme. Only the keys you define are overridden — everything else stays unchanged.
460
+
461
+ ---
462
+
463
+ ### Font scale
464
+
465
+ ```tsx
466
+ import { useStampdUI, FontScaleMode } from 'stampd/context';
467
+
468
+ const { fontScale, fontScaleMode, setFontScaleMode } = useStampdUI();
469
+ ```
470
+
471
+ | `FontScaleMode` | Behavior |
472
+ |---------------------------|-----------------------------------------------|
473
+ | `FontScaleMode.SYSTEM` | Follows device accessibility setting *(default)* |
474
+ | `FontScaleMode.FIXED_1` | Always 1× |
475
+ | `FontScaleMode.FIXED_1_5` | Always 1.5× |
476
+ | `FontScaleMode.FIXED_2` | Always 2× |
477
+
478
+ ---
479
+
480
+ ### Accessing the theme directly
481
+
482
+ ```tsx
483
+ const { theme } = useStampdUI();
484
+
485
+ // theme.colors.primary, theme.spacing.md, theme.fonts.family.inter, ...
486
+ ```
487
+
488
+ ---
489
+
490
+ ## createTheme API
491
+
492
+ ```ts
493
+ import { createTheme, InferTheme } from 'stampd/theme';
494
+
495
+ export const config = createTheme({
496
+ // ── Compile-time tokens ──────────────────────────────────────────────────
497
+ tokens: {
498
+ spacing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 },
499
+ radius: { sm: 4, md: 8, lg: 16, full: 9999 },
500
+ fontSize: { sm: 12, md: 14, lg: 16, xl: 20, xxl: 24 },
501
+ },
502
+
503
+ // ── Runtime theme ────────────────────────────────────────────────────────
504
+ theme: {
505
+ light: { colors: lightColors },
506
+ dark: { colors: darkColors },
507
+ highContrast: { colors: { background: '#000' } }, // DeepPartial — only overrides what's set
508
+ },
509
+
510
+ // ── Fonts ────────────────────────────────────────────────────────────────
511
+ fonts: {
512
+ default: { size: 14, family: 'Inter' }, // auto-injected in Text / TextInput
513
+ sizes: { sm: 12, md: 14, lg: 16, xl: 20 },
514
+ family: { inter: 'Inter', mono: 'JetBrainsMono' },
515
+ },
516
+ });
517
+
518
+ // Infer the resolved theme type:
519
+ export type AppTheme = InferTheme<typeof config>;
520
+ ```
521
+
522
+ ---
523
+
524
+ ## Style priority
525
+
526
+ When multiple layers are applied, the **last item wins** (React Native style array behavior):
527
+
528
+ ```
529
+ fonts.default < base style < variants < style prop passed by user
530
+ ```
531
+
532
+ The user's `style` prop always wins over everything defined at component level.
533
+
534
+ ---
535
+
536
+ ## Generated types — stampd-types.d.ts
537
+
538
+ On every build, stampd generates `stampd-types.d.ts` at your project root with **inline literal types** for full IDE autocomplete — no imports needed in your components:
539
+
540
+ ```ts
541
+ // ⚡ Auto-generated by stampd — do not edit manually.
542
+ declare global {
543
+ namespace StyledSystem {
544
+ interface Theme {
545
+ spacing: { xs: 4; sm: 8; md: 16; lg: 24; xl: 32 };
546
+ radius: { sm: 4; md: 8; lg: 16; full: 9999 };
547
+ fontSize: { sm: 12; md: 14; lg: 16; xl: 20; xxl: 24 };
548
+ colors: { primary: "#2563EB"; background: "#F8FAFC"; ... };
549
+ fonts: {
550
+ default: { size: 14; family: "Inter" };
551
+ sizes: { sm: 12; md: 14; lg: 16; xl: 20 };
552
+ family: { inter: "Inter"; mono: "JetBrainsMono" };
553
+ };
554
+ }
555
+ }
556
+ }
557
+ ```
558
+
559
+ The file is regenerated automatically when `stampd.config.ts` changes.
560
+
561
+ ---
562
+
563
+ ## CLI — inspect generated output
564
+
565
+ Inspect exactly what the plugin emits for any file without running the full bundler:
566
+
567
+ ```bash
568
+ node node_modules/stampd/dist/cli/runTransform.js <input> <output> [config]
569
+
570
+ # Example:
571
+ node node_modules/stampd/dist/cli/runTransform.js \
572
+ src/screens/Login.tsx \
573
+ /tmp/Login.out.js \
574
+ stampd.config.ts
575
+ ```
576
+
577
+ Add as a script in `package.json`:
578
+
579
+ ```json
580
+ {
581
+ "scripts": {
582
+ "transform": "node node_modules/stampd/dist/cli/runTransform.js"
583
+ }
584
+ }
585
+ ```
586
+
587
+ ---
588
+
589
+ ## Supported components
590
+
591
+ `Styled` ships with typed factories for all standard React Native components:
592
+
593
+ | Component | Style type |
594
+ |---------------------------------|---------------|
595
+ | `Styled.View` | `ViewStyle` |
596
+ | `Styled.Text` | `TextStyle` |
597
+ | `Styled.TextInput` | `TextStyle` |
598
+ | `Styled.Image` | `ImageStyle` |
599
+ | `Styled.ImageBackground` | `ViewStyle` |
600
+ | `Styled.ScrollView` | `ViewStyle` |
601
+ | `Styled.Pressable` | `ViewStyle` |
602
+ | `Styled.TouchableOpacity` | `ViewStyle` |
603
+ | `Styled.KeyboardAvoidingView` | `ViewStyle` |
604
+ | `Styled.FlatList` | `ViewStyle` |
605
+ | `Styled.SectionList` | `ViewStyle` |
606
+
607
+ ---
608
+
609
+ ## License
610
+
611
+ MIT
@@ -0,0 +1,13 @@
1
+ import { types as t } from "@babel/core";
2
+ export type StyleAnalysis = {
3
+ type: "SAFE";
4
+ } | {
5
+ type: "PARTIAL";
6
+ } | {
7
+ type: "UNSAFE";
8
+ };
9
+ /**
10
+ * Analisa o objeto de style inteiro
11
+ */
12
+ export declare function analyzeStyle(style: t.ObjectExpression): StyleAnalysis;
13
+ //# sourceMappingURL=analyzeStyle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzeStyle.d.ts","sourceRoot":"","sources":["../../src/analyzer/analyzeStyle.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,CAAC,EAAE,MAAM,aAAa,CAAC;AAGzC,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEvB;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,CAAC,CAAC,gBAAgB,GAAG,aAAa,CAqCrE"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyzeStyle = analyzeStyle;
4
+ // plugins/src/analyzer/analyzeStyle.ts
5
+ const core_1 = require("@babel/core");
6
+ const classifyValue_1 = require("./classifyValue");
7
+ /**
8
+ * Analisa o objeto de style inteiro
9
+ */
10
+ function analyzeStyle(style) {
11
+ let hasDynamic = false;
12
+ let hasStaticOrTheme = false;
13
+ for (const prop of style.properties) {
14
+ if (core_1.types.isSpreadElement(prop)) {
15
+ const type = (0, classifyValue_1.classifyValue)(prop);
16
+ if (type === "DYNAMIC")
17
+ hasDynamic = true;
18
+ else
19
+ hasStaticOrTheme = true;
20
+ continue;
21
+ }
22
+ if (!core_1.types.isObjectProperty(prop))
23
+ continue;
24
+ const valueType = (0, classifyValue_1.classifyValue)(prop.value);
25
+ if (valueType === "DYNAMIC") {
26
+ hasDynamic = true;
27
+ }
28
+ else {
29
+ hasStaticOrTheme = true;
30
+ }
31
+ }
32
+ // 🔴 tudo dinâmico → não mexe
33
+ if (hasDynamic && !hasStaticOrTheme) {
34
+ return { type: "UNSAFE" };
35
+ }
36
+ // 🟡 misto → parcial
37
+ if (hasDynamic && hasStaticOrTheme) {
38
+ return { type: "PARTIAL" };
39
+ }
40
+ // 🟢 tudo seguro → pode hardcode
41
+ return { type: "SAFE" };
42
+ }
43
+ //# sourceMappingURL=analyzeStyle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzeStyle.js","sourceRoot":"","sources":["../../src/analyzer/analyzeStyle.ts"],"names":[],"mappings":";;AAYA,oCAqCC;AAjDD,uCAAuC;AACvC,sCAAyC;AACzC,mDAA2D;AAO3D;;GAEG;AACH,SAAgB,YAAY,CAAC,KAAyB;IACpD,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAE7B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACpC,IAAI,YAAC,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,IAAA,6BAAa,EAAC,IAAI,CAAC,CAAC;YAEjC,IAAI,IAAI,KAAK,SAAS;gBAAE,UAAU,GAAG,IAAI,CAAC;;gBACrC,gBAAgB,GAAG,IAAI,CAAC;YAE7B,SAAS;QACX,CAAC;QAED,IAAI,CAAC,YAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC;YAAE,SAAS;QAExC,MAAM,SAAS,GAAG,IAAA,6BAAa,EAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAE5C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;aAAM,CAAC;YACN,gBAAgB,GAAG,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,8BAA8B;IAC9B,IAAI,UAAU,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACpC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC5B,CAAC;IAED,qBAAqB;IACrB,IAAI,UAAU,IAAI,gBAAgB,EAAE,CAAC;QACnC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC7B,CAAC;IAED,iCAAiC;IACjC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { types as t } from "@babel/core";
2
+ export type ValueType = "STATIC" | "THEME" | "DYNAMIC";
3
+ /**
4
+ * Classifica um valor dentro do style
5
+ */
6
+ export declare function classifyValue(node: t.Node): ValueType;
7
+ //# sourceMappingURL=classifyValue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifyValue.d.ts","sourceRoot":"","sources":["../../src/analyzer/classifyValue.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,CAAC,EAAE,MAAM,aAAa,CAAC;AAGzC,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAEvD;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,GAAG,SAAS,CAwBrD"}