rnwind 0.0.4 → 0.0.5

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 (127) hide show
  1. package/lib/cjs/core/normalize-classname.cjs +25 -0
  2. package/lib/cjs/core/normalize-classname.cjs.map +1 -0
  3. package/lib/cjs/core/normalize-classname.d.ts +10 -0
  4. package/lib/cjs/core/style-builder/build-style.cjs +258 -58
  5. package/lib/cjs/core/style-builder/build-style.cjs.map +1 -1
  6. package/lib/cjs/core/style-builder/build-style.d.ts +6 -1
  7. package/lib/cjs/core/style-builder/union-builder.cjs +37 -3
  8. package/lib/cjs/core/style-builder/union-builder.cjs.map +1 -1
  9. package/lib/cjs/core/style-builder/union-builder.d.ts +21 -1
  10. package/lib/cjs/metro/dts.cjs +7 -16
  11. package/lib/cjs/metro/dts.cjs.map +1 -1
  12. package/lib/cjs/metro/dts.d.ts +2 -4
  13. package/lib/cjs/metro/state.cjs +30 -78
  14. package/lib/cjs/metro/state.cjs.map +1 -1
  15. package/lib/cjs/metro/state.d.ts +8 -25
  16. package/lib/cjs/metro/transformer.cjs +193 -34
  17. package/lib/cjs/metro/transformer.cjs.map +1 -1
  18. package/lib/cjs/metro/with-config.cjs +2 -2
  19. package/lib/cjs/metro/with-config.cjs.map +1 -1
  20. package/lib/cjs/metro/with-config.d.ts +11 -26
  21. package/lib/cjs/metro/wrap-imports.cjs +273 -0
  22. package/lib/cjs/metro/wrap-imports.cjs.map +1 -0
  23. package/lib/cjs/metro/wrap-imports.d.ts +26 -0
  24. package/lib/cjs/runtime/components/rnwind-provider.cjs +0 -17
  25. package/lib/cjs/runtime/components/rnwind-provider.cjs.map +1 -1
  26. package/lib/cjs/runtime/components/rnwind-provider.d.ts +0 -14
  27. package/lib/cjs/runtime/hooks/use-css.cjs +16 -10
  28. package/lib/cjs/runtime/hooks/use-css.cjs.map +1 -1
  29. package/lib/cjs/runtime/hooks/use-css.d.ts +15 -9
  30. package/lib/cjs/runtime/index.cjs +11 -13
  31. package/lib/cjs/runtime/index.cjs.map +1 -1
  32. package/lib/cjs/runtime/index.d.ts +4 -9
  33. package/lib/cjs/runtime/lookup-css.cjs +10 -0
  34. package/lib/cjs/runtime/lookup-css.cjs.map +1 -1
  35. package/lib/cjs/runtime/lookup-css.d.ts +7 -0
  36. package/lib/cjs/runtime/resolve.cjs +348 -0
  37. package/lib/cjs/runtime/resolve.cjs.map +1 -0
  38. package/lib/cjs/runtime/resolve.d.ts +61 -0
  39. package/lib/cjs/runtime/wrap.cjs +254 -0
  40. package/lib/cjs/runtime/wrap.cjs.map +1 -0
  41. package/lib/cjs/runtime/wrap.d.ts +37 -0
  42. package/lib/cjs/testing/index.cjs +81 -50
  43. package/lib/cjs/testing/index.cjs.map +1 -1
  44. package/lib/esm/core/normalize-classname.d.ts +10 -0
  45. package/lib/esm/core/normalize-classname.mjs +23 -0
  46. package/lib/esm/core/normalize-classname.mjs.map +1 -0
  47. package/lib/esm/core/style-builder/build-style.d.ts +6 -1
  48. package/lib/esm/core/style-builder/build-style.mjs +258 -58
  49. package/lib/esm/core/style-builder/build-style.mjs.map +1 -1
  50. package/lib/esm/core/style-builder/union-builder.d.ts +21 -1
  51. package/lib/esm/core/style-builder/union-builder.mjs +37 -3
  52. package/lib/esm/core/style-builder/union-builder.mjs.map +1 -1
  53. package/lib/esm/metro/dts.d.ts +2 -4
  54. package/lib/esm/metro/dts.mjs +7 -16
  55. package/lib/esm/metro/dts.mjs.map +1 -1
  56. package/lib/esm/metro/state.d.ts +8 -25
  57. package/lib/esm/metro/state.mjs +30 -76
  58. package/lib/esm/metro/state.mjs.map +1 -1
  59. package/lib/esm/metro/transformer.mjs +194 -35
  60. package/lib/esm/metro/transformer.mjs.map +1 -1
  61. package/lib/esm/metro/with-config.d.ts +11 -26
  62. package/lib/esm/metro/with-config.mjs +2 -2
  63. package/lib/esm/metro/with-config.mjs.map +1 -1
  64. package/lib/esm/metro/wrap-imports.d.ts +26 -0
  65. package/lib/esm/metro/wrap-imports.mjs +250 -0
  66. package/lib/esm/metro/wrap-imports.mjs.map +1 -0
  67. package/lib/esm/runtime/components/rnwind-provider.d.ts +0 -14
  68. package/lib/esm/runtime/components/rnwind-provider.mjs +1 -17
  69. package/lib/esm/runtime/components/rnwind-provider.mjs.map +1 -1
  70. package/lib/esm/runtime/hooks/use-css.d.ts +15 -9
  71. package/lib/esm/runtime/hooks/use-css.mjs +16 -10
  72. package/lib/esm/runtime/hooks/use-css.mjs.map +1 -1
  73. package/lib/esm/runtime/index.d.ts +4 -9
  74. package/lib/esm/runtime/index.mjs +4 -4
  75. package/lib/esm/runtime/index.mjs.map +1 -1
  76. package/lib/esm/runtime/lookup-css.d.ts +7 -0
  77. package/lib/esm/runtime/lookup-css.mjs +10 -1
  78. package/lib/esm/runtime/lookup-css.mjs.map +1 -1
  79. package/lib/esm/runtime/resolve.d.ts +61 -0
  80. package/lib/esm/runtime/resolve.mjs +341 -0
  81. package/lib/esm/runtime/resolve.mjs.map +1 -0
  82. package/lib/esm/runtime/wrap.d.ts +37 -0
  83. package/lib/esm/runtime/wrap.mjs +251 -0
  84. package/lib/esm/runtime/wrap.mjs.map +1 -0
  85. package/lib/esm/testing/index.mjs +84 -53
  86. package/lib/esm/testing/index.mjs.map +1 -1
  87. package/package.json +2 -1
  88. package/src/core/normalize-classname.ts +19 -0
  89. package/src/core/style-builder/build-style.ts +286 -55
  90. package/src/core/style-builder/union-builder.ts +36 -3
  91. package/src/metro/dts.ts +7 -19
  92. package/src/metro/state.ts +29 -74
  93. package/src/metro/transformer.ts +190 -34
  94. package/src/metro/with-config.ts +13 -28
  95. package/src/metro/wrap-imports.ts +260 -0
  96. package/src/runtime/components/rnwind-provider.tsx +0 -17
  97. package/src/runtime/hooks/use-css.ts +17 -11
  98. package/src/runtime/index.ts +3 -26
  99. package/src/runtime/lookup-css.ts +10 -0
  100. package/src/runtime/resolve.ts +381 -0
  101. package/src/runtime/wrap.tsx +267 -0
  102. package/src/testing/index.ts +106 -56
  103. package/lib/cjs/core/parser/text-truncate.cjs +0 -78
  104. package/lib/cjs/core/parser/text-truncate.cjs.map +0 -1
  105. package/lib/cjs/metro/transform-ast.cjs +0 -1472
  106. package/lib/cjs/metro/transform-ast.cjs.map +0 -1
  107. package/lib/cjs/metro/transform-ast.d.ts +0 -88
  108. package/lib/cjs/runtime/haptics.cjs +0 -113
  109. package/lib/cjs/runtime/haptics.cjs.map +0 -1
  110. package/lib/cjs/runtime/haptics.d.ts +0 -48
  111. package/lib/cjs/runtime/interactive-box.cjs +0 -35
  112. package/lib/cjs/runtime/interactive-box.cjs.map +0 -1
  113. package/lib/cjs/runtime/interactive-box.d.ts +0 -40
  114. package/lib/esm/core/parser/text-truncate.mjs +0 -75
  115. package/lib/esm/core/parser/text-truncate.mjs.map +0 -1
  116. package/lib/esm/metro/transform-ast.d.ts +0 -88
  117. package/lib/esm/metro/transform-ast.mjs +0 -1451
  118. package/lib/esm/metro/transform-ast.mjs.map +0 -1
  119. package/lib/esm/runtime/haptics.d.ts +0 -48
  120. package/lib/esm/runtime/haptics.mjs +0 -110
  121. package/lib/esm/runtime/haptics.mjs.map +0 -1
  122. package/lib/esm/runtime/interactive-box.d.ts +0 -40
  123. package/lib/esm/runtime/interactive-box.mjs +0 -33
  124. package/lib/esm/runtime/interactive-box.mjs.map +0 -1
  125. package/src/metro/transform-ast.ts +0 -1729
  126. package/src/runtime/haptics.ts +0 -120
  127. package/src/runtime/interactive-box.tsx +0 -57
@@ -1 +1 @@
1
- {"version":3,"file":"with-config.cjs","sources":["../../../../src/metro/with-config.ts"],"sourcesContent":["import { existsSync, mkdirSync, utimesSync, watch as watchFile } from 'node:fs'\nimport path from 'node:path'\nimport { writeDtsFile } from './dts'\nimport { createRnwindResolver, type ResolveRequestFn } from './resolver'\nimport { configureRnwindState, getRnwindState, onThemeChange } from './state'\n\n/** Default cache directory at the project root. Visible for debugging. */\nconst DEFAULT_CACHE_DIR = '.rnwind'\n\n/**\n * Active CSS watcher — replaced (and the prior one closed) when\n * `withRnwindConfig` is called again (Metro restart, repeated init).\n * Only one watcher per process; no stacking.\n */\nlet activeCssWatcher: { cssPath: string; close: () => void } | null = null\n\n/**\n * Watch the theme CSS for edits. On change, rewrite the per-scheme\n * files with the fresh theme AND bump `mtime` on every source file\n * rnwind has transformed so far — Metro's own watcher sees those\n * mtime changes, invalidates the modules, and re-transforms them\n * against the new CSS on the next request. `getCacheKey()` alone is\n * NOT enough: Metro samples the cache key once per worker lifetime,\n * so edits during an already-running dev server don't propagate\n * without this explicit nudge.\n * @param cssPath Absolute path to the theme CSS to watch.\n * @param projectRoot Metro's project root (for `getRnwindState`).\n */\nfunction watchThemeCss(cssPath: string, projectRoot: string): void {\n if (activeCssWatcher?.cssPath === cssPath) return\n activeCssWatcher?.close()\n if (!existsSync(cssPath)) return\n let pending = false\n const watcher = watchFile(cssPath, { persistent: false }, () => {\n // Debounce: editors often emit 2-3 change events per save (atomic\n // rename dance, tmp files). Coalesce to ONE rebuild per microtask.\n if (pending) return\n pending = true\n queueMicrotask(async () => {\n pending = false\n try {\n await onThemeChange(projectRoot)\n touchRecordedFiles(projectRoot)\n } catch {\n // Invalidation is best-effort — never crash the dev server.\n }\n })\n })\n activeCssWatcher = { cssPath, close: () => watcher.close() }\n}\n\n/**\n * Bump the mtime on every file the builder has transformed. Metro's\n * file watcher keys on `mtime`, so this is what makes it invalidate\n * those modules and re-transform them.\n * @param projectRoot Metro's project root.\n */\nfunction touchRecordedFiles(projectRoot: string): void {\n const state = getRnwindState(projectRoot)\n const files = state.builder.recordedFiles()\n const now = new Date()\n for (const file of files) {\n try {\n if (existsSync(file)) utimesSync(file, now, now)\n } catch {\n // One file's stat failure shouldn't stop the others.\n }\n }\n}\n\n/**\n * Where the rnwind babel transformer lives — resolved relative to this\n * module so the path works from both the `src/` tree (tests) and the\n * built `lib/` output. Tries a few extensions because `require.resolve`\n * doesn't auto-find `.cjs` / `.mjs` from a bare specifier.\n * @returns Absolute path to the rnwind transformer module.\n */\nfunction transformerPath(): string {\n for (const candidate of ['./transformer.cjs', './transformer.mjs', './transformer.js', './transformer.ts', './transformer']) {\n try {\n return require.resolve(candidate)\n } catch {\n // try the next extension\n }\n }\n throw new Error('rnwind: could not resolve the metro transformer path')\n}\n\n/**\n * Resolve the effective cache directory, honoring a user override and\n * falling back to `<projectRoot>/.rnwind`.\n * @param projectRoot Anchor for relative paths.\n * @param override User-supplied option.\n * @returns Absolute cache directory.\n */\nfunction resolveCacheDir(projectRoot: string, override: string | undefined): string {\n if (!override || override.length === 0) return path.resolve(projectRoot, DEFAULT_CACHE_DIR)\n return path.isAbsolute(override) ? override : path.resolve(projectRoot, override)\n}\n\n/**\n * Read the theme CSS and extract `@variant <name>` blocks for the .d.ts\n * generator. Forces construction of `getRnwindState`, then reads\n * `parser.declaredSchemes` (populated synchronously at construction).\n * @param cssEntry Absolute path to theme CSS.\n * @param projectRoot\n * @returns Scheme names (empty when the theme has no variants; `'base'` is filtered).\n */\nfunction discoverSchemes(cssEntry: string, projectRoot: string): readonly string[] {\n if (!existsSync(cssEntry)) return []\n try {\n const { parser } = getRnwindState(projectRoot)\n return parser.declaredSchemes.filter((name) => name !== 'base')\n } catch {\n return []\n }\n}\n\n/** User-facing options for `withRnwindConfig`. */\nexport interface RnwindMetroOptions {\n /** Path to the theme CSS (absolute or relative to `projectRoot`). Required. */\n cssEntryFile: string\n /** Where rnwind writes the `.d.ts` file. Set `false` to disable. Defaults to `<projectRoot>/rnwind-types.d.ts`. */\n dtsFile?: string | false\n /** Optional project-root override — defaults to `metroConfig.projectRoot` then `process.cwd()`. */\n projectRoot?: string\n /** Cache directory. Absolute, or relative to `projectRoot`. Default: `.rnwind` at project root. */\n cacheDir?: string\n /**\n * Extra JSX prop-name prefixes that rnwind should rewrite. Each\n * prefix `P` turns `<Tag PClassName=\"…\">` into `<Tag\n * PStyle={lookupCss(…)}>`. The built-in `'contentContainer'` prefix\n * is always on (covers ScrollView / FlatList / SectionList); user\n * entries merge on top.\n */\n classNamePrefixes?: readonly string[]\n /**\n * Extra module specifiers whose JSX exports rnwind should treat as\n * \"host components\" — i.e. tags whose `className=\"…\"` attribute is\n * rewritten to `style={lookupCss(…)}` at build time (zero runtime\n * cost). Merged with the built-in defaults: `react-native`,\n * `react-native-reanimated`, `react-native-svg`,\n * `react-native-gesture-handler`, `expo-linear-gradient`, `expo-image`.\n *\n * Anything NOT marked as a host has its `className` left untouched —\n * the importing component receives the raw string and decides what\n * to do with it. Use this option to opt your design-system / UI\n * primitive packages into the zero-runtime path.\n */\n hostSources?: readonly string[]\n /**\n * Extra JSX tag names (verbatim — may include `.` for member access\n * like `'Animated.View'`) rnwind should treat as host components,\n * regardless of where they're imported from. Useful for one-off\n * escape-hatches: `import { View as MyBox } from 'react-native'`\n * doesn't change the local name → `'MyBox'` here picks it up.\n */\n hostComponents?: readonly string[]\n}\n\n/** Shape we mutate on Metro's config. Loose so we don't pin Metro's internal types. */\nexport interface MetroConfigLike {\n projectRoot?: string\n watchFolders?: string[]\n transformer?: {\n babelTransformerPath?: string\n [key: string]: unknown\n }\n resolver?: {\n resolveRequest?: ResolveRequestFn | null\n [key: string]: unknown\n }\n [key: string]: unknown\n}\n\n/**\n * Wrap a Metro config with rnwind's pipeline:\n * - Install the rnwind babel transformer.\n * - Chain a `resolveRequest` hook that serves\n * `rnwind/__generated/schemes` from `<cacheDir>/schemes.js`.\n * - Write the `.d.ts` so TypeScript accepts `className=` on RN components.\n * - Publish the theme CSS path + cache dir via env so Metro workers\n * can rebuild their local state.\n * - Ensure the cache dir is a watched folder Metro's haste-map indexes.\n *\n * Theme-edit hot reload happens implicitly: every transformed file\n * imports `rnwind/__generated/schemes`, and that module eager-imports\n * `common.style.js`. When the theme changes, the per-scheme files\n * regenerate with new bytes; Metro's content SHA1 dedup detects the\n * change and invalidates every importer automatically.\n * `getCacheKey()` on the transformer covers the per-file transform\n * cache. No file watcher / source-padding hack needed — the dep graph\n * carries the signal.\n * @param metroConfig Config from `getDefaultConfig(__dirname)` or equivalent.\n * @param options rnwind options.\n * @returns The same config, mutated.\n */\nexport function withRnwindConfig<C extends MetroConfigLike>(metroConfig: C, options: RnwindMetroOptions): C {\n const projectRoot = options.projectRoot ?? metroConfig.projectRoot ?? process.cwd()\n const cacheDir = resolveCacheDir(projectRoot, options.cacheDir)\n const cssEntry = path.isAbsolute(options.cssEntryFile) ? options.cssEntryFile : path.resolve(projectRoot, options.cssEntryFile)\n\n mkdirSync(cacheDir, { recursive: true })\n const watchFolders = (metroConfig.watchFolders ?? []).filter((p) => typeof p === 'string' && p.length > 0)\n configureRnwindState(cssEntry, cacheDir, watchFolders, options.classNamePrefixes, options.hostSources, options.hostComponents)\n\n // Warm the state eagerly (in the Metro master process) so oxide's\n // Scanner walks every project source (and every monorepo\n // watch-folder) ONCE and the manifest + scheme files hold the\n // complete union before Metro's resolver tries to SHA1 them on the\n // first transform. Each worker lazy-repeats this scan on its first\n // transform to converge on identical state.\n try {\n void getRnwindState(projectRoot).builder.ensureFilesExist()\n } catch {\n // Any init error surfaces again at the first transform; don't crash Metro boot.\n }\n\n // Install transformer + resolver. Capture the existing\n // babelTransformerPath BEFORE we override it — our worker chains to\n // it (env-passed) so Flow / expo-router / babel-preset-expo etc. all\n // continue to run.\n const existingTransformerPath = metroConfig.transformer?.babelTransformerPath\n if (typeof existingTransformerPath === 'string' && existingTransformerPath.length > 0) {\n process.env.RNWIND_UPSTREAM_TRANSFORMER = existingTransformerPath\n }\n const upstream = metroConfig.resolver?.resolveRequest ?? null\n metroConfig.transformer = { ...metroConfig.transformer, babelTransformerPath: transformerPath() }\n metroConfig.resolver = { ...metroConfig.resolver, resolveRequest: createRnwindResolver(upstream) }\n\n // Metro's haste-map indexes `watchFolders` at startup. Adding the\n // cache dir guarantees scheme style files + manifest get SHA1'd\n // without a \"Failed to get the SHA-1\" race when the first transform\n // writes them.\n const existingWatch = metroConfig.watchFolders ?? []\n metroConfig.watchFolders = existingWatch.includes(cacheDir) ? existingWatch : [...existingWatch, cacheDir]\n\n if (options.dtsFile !== false) {\n const dtsPath = options.dtsFile ?? path.resolve(projectRoot, 'rnwind-types.d.ts')\n const schemes = discoverSchemes(cssEntry, projectRoot)\n writeDtsFile(dtsPath, schemes, options.classNamePrefixes)\n }\n\n // Watch the theme CSS. On edit, we rewrite scheme files AND touch\n // mtime on every transformed source file so Metro invalidates them\n // and re-transforms — the only reliable way to propagate theme\n // changes to an already-running dev server.\n watchThemeCss(cssEntry, projectRoot)\n\n return metroConfig\n}\n"],"names":["existsSync","watchFile","onThemeChange","state","getRnwindState","utimesSync","mkdirSync","configureRnwindState","createRnwindResolver","writeDtsFile"],"mappings":"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"}
1
+ {"version":3,"file":"with-config.cjs","sources":["../../../../src/metro/with-config.ts"],"sourcesContent":["import { existsSync, mkdirSync, utimesSync, watch as watchFile } from 'node:fs'\nimport path from 'node:path'\nimport { writeDtsFile } from './dts'\nimport { createRnwindResolver, type ResolveRequestFn } from './resolver'\nimport { configureRnwindState, getRnwindState, onThemeChange } from './state'\n\n/** Default cache directory at the project root. Visible for debugging. */\nconst DEFAULT_CACHE_DIR = '.rnwind'\n\n/**\n * Active CSS watcher — replaced (and the prior one closed) when\n * `withRnwindConfig` is called again (Metro restart, repeated init).\n * Only one watcher per process; no stacking.\n */\nlet activeCssWatcher: { cssPath: string; close: () => void } | null = null\n\n/**\n * Watch the theme CSS for edits. On change, rewrite the per-scheme\n * files with the fresh theme AND bump `mtime` on every source file\n * rnwind has transformed so far — Metro's own watcher sees those\n * mtime changes, invalidates the modules, and re-transforms them\n * against the new CSS on the next request. `getCacheKey()` alone is\n * NOT enough: Metro samples the cache key once per worker lifetime,\n * so edits during an already-running dev server don't propagate\n * without this explicit nudge.\n * @param cssPath Absolute path to the theme CSS to watch.\n * @param projectRoot Metro's project root (for `getRnwindState`).\n */\nfunction watchThemeCss(cssPath: string, projectRoot: string): void {\n if (activeCssWatcher?.cssPath === cssPath) return\n activeCssWatcher?.close()\n if (!existsSync(cssPath)) return\n let pending = false\n const watcher = watchFile(cssPath, { persistent: false }, () => {\n // Debounce: editors often emit 2-3 change events per save (atomic\n // rename dance, tmp files). Coalesce to ONE rebuild per microtask.\n if (pending) return\n pending = true\n queueMicrotask(async () => {\n pending = false\n try {\n await onThemeChange(projectRoot)\n touchRecordedFiles(projectRoot)\n } catch {\n // Invalidation is best-effort — never crash the dev server.\n }\n })\n })\n activeCssWatcher = { cssPath, close: () => watcher.close() }\n}\n\n/**\n * Bump the mtime on every file the builder has transformed. Metro's\n * file watcher keys on `mtime`, so this is what makes it invalidate\n * those modules and re-transform them.\n * @param projectRoot Metro's project root.\n */\nfunction touchRecordedFiles(projectRoot: string): void {\n const state = getRnwindState(projectRoot)\n const files = state.builder.recordedFiles()\n const now = new Date()\n for (const file of files) {\n try {\n if (existsSync(file)) utimesSync(file, now, now)\n } catch {\n // One file's stat failure shouldn't stop the others.\n }\n }\n}\n\n/**\n * Where the rnwind babel transformer lives — resolved relative to this\n * module so the path works from both the `src/` tree (tests) and the\n * built `lib/` output. Tries a few extensions because `require.resolve`\n * doesn't auto-find `.cjs` / `.mjs` from a bare specifier.\n * @returns Absolute path to the rnwind transformer module.\n */\nfunction transformerPath(): string {\n for (const candidate of ['./transformer.cjs', './transformer.mjs', './transformer.js', './transformer.ts', './transformer']) {\n try {\n return require.resolve(candidate)\n } catch {\n // try the next extension\n }\n }\n throw new Error('rnwind: could not resolve the metro transformer path')\n}\n\n/**\n * Resolve the effective cache directory, honoring a user override and\n * falling back to `<projectRoot>/.rnwind`.\n * @param projectRoot Anchor for relative paths.\n * @param override User-supplied option.\n * @returns Absolute cache directory.\n */\nfunction resolveCacheDir(projectRoot: string, override: string | undefined): string {\n if (!override || override.length === 0) return path.resolve(projectRoot, DEFAULT_CACHE_DIR)\n return path.isAbsolute(override) ? override : path.resolve(projectRoot, override)\n}\n\n/**\n * Read the theme CSS and extract `@variant <name>` blocks for the .d.ts\n * generator. Forces construction of `getRnwindState`, then reads\n * `parser.declaredSchemes` (populated synchronously at construction).\n * @param cssEntry Absolute path to theme CSS.\n * @param projectRoot\n * @returns Scheme names (empty when the theme has no variants; `'base'` is filtered).\n */\nfunction discoverSchemes(cssEntry: string, projectRoot: string): readonly string[] {\n if (!existsSync(cssEntry)) return []\n try {\n const { parser } = getRnwindState(projectRoot)\n return parser.declaredSchemes.filter((name) => name !== 'base')\n } catch {\n return []\n }\n}\n\n/** User-facing options for `withRnwindConfig`. */\nexport interface RnwindMetroOptions {\n /** Path to the theme CSS (absolute or relative to `projectRoot`). Required. */\n cssEntryFile: string\n /** Where rnwind writes the `.d.ts` file. Set `false` to disable. Defaults to `<projectRoot>/rnwind-types.d.ts`. */\n dtsFile?: string | false\n /** Optional project-root override — defaults to `metroConfig.projectRoot` then `process.cwd()`. */\n projectRoot?: string\n /** Cache directory. Absolute, or relative to `projectRoot`. Default: `.rnwind` at project root. */\n cacheDir?: string\n /**\n * Extra module specifiers whose component exports rnwind should\n * auto-wrap at import sites — `import { View } from 'react-native'`\n * becomes `const View = wrap(_rnw0)` so `<View className=\"…\">` resolves\n * styles at runtime. Merged with the built-in defaults: `react-native`,\n * `react-native-reanimated`, `react-native-svg`,\n * `react-native-gesture-handler`, `react-native-safe-area-context`,\n * `expo-linear-gradient`, `expo-image`, and more.\n *\n * A module NOT in this list keeps its raw imports — the importing\n * component receives `className` as a plain prop and can resolve it\n * via `useCss` / `wrap` itself. Use this to opt your design-system /\n * UI primitive packages into the auto-wrap path.\n */\n wrapModules?: readonly string[]\n}\n\n/** Shape we mutate on Metro's config. Loose so we don't pin Metro's internal types. */\nexport interface MetroConfigLike {\n projectRoot?: string\n watchFolders?: string[]\n transformer?: {\n babelTransformerPath?: string\n [key: string]: unknown\n }\n resolver?: {\n resolveRequest?: ResolveRequestFn | null\n [key: string]: unknown\n }\n [key: string]: unknown\n}\n\n/**\n * Wrap a Metro config with rnwind's pipeline:\n * - Install the rnwind babel transformer.\n * - Chain a `resolveRequest` hook that serves\n * `rnwind/__generated/schemes` from `<cacheDir>/schemes.js`.\n * - Write the `.d.ts` so TypeScript accepts `className=` on RN components.\n * - Publish the theme CSS path + cache dir via env so Metro workers\n * can rebuild their local state.\n * - Ensure the cache dir is a watched folder Metro's haste-map indexes.\n *\n * Theme-edit hot reload happens implicitly: every transformed file\n * imports `rnwind/__generated/schemes`, and that module eager-imports\n * `common.style.js`. When the theme changes, the per-scheme files\n * regenerate with new bytes; Metro's content SHA1 dedup detects the\n * change and invalidates every importer automatically.\n * `getCacheKey()` on the transformer covers the per-file transform\n * cache. No file watcher / source-padding hack needed — the dep graph\n * carries the signal.\n * @param metroConfig Config from `getDefaultConfig(__dirname)` or equivalent.\n * @param options rnwind options.\n * @returns The same config, mutated.\n */\nexport function withRnwindConfig<C extends MetroConfigLike>(metroConfig: C, options: RnwindMetroOptions): C {\n const projectRoot = options.projectRoot ?? metroConfig.projectRoot ?? process.cwd()\n const cacheDir = resolveCacheDir(projectRoot, options.cacheDir)\n const cssEntry = path.isAbsolute(options.cssEntryFile) ? options.cssEntryFile : path.resolve(projectRoot, options.cssEntryFile)\n\n mkdirSync(cacheDir, { recursive: true })\n const watchFolders = (metroConfig.watchFolders ?? []).filter((p) => typeof p === 'string' && p.length > 0)\n configureRnwindState(cssEntry, cacheDir, watchFolders, options.wrapModules)\n\n // Warm the state eagerly (in the Metro master process) so oxide's\n // Scanner walks every project source (and every monorepo\n // watch-folder) ONCE and the manifest + scheme files hold the\n // complete union before Metro's resolver tries to SHA1 them on the\n // first transform. Each worker lazy-repeats this scan on its first\n // transform to converge on identical state.\n try {\n void getRnwindState(projectRoot).builder.ensureFilesExist()\n } catch {\n // Any init error surfaces again at the first transform; don't crash Metro boot.\n }\n\n // Install transformer + resolver. Capture the existing\n // babelTransformerPath BEFORE we override it — our worker chains to\n // it (env-passed) so Flow / expo-router / babel-preset-expo etc. all\n // continue to run.\n const existingTransformerPath = metroConfig.transformer?.babelTransformerPath\n if (typeof existingTransformerPath === 'string' && existingTransformerPath.length > 0) {\n process.env.RNWIND_UPSTREAM_TRANSFORMER = existingTransformerPath\n }\n const upstream = metroConfig.resolver?.resolveRequest ?? null\n metroConfig.transformer = { ...metroConfig.transformer, babelTransformerPath: transformerPath() }\n metroConfig.resolver = { ...metroConfig.resolver, resolveRequest: createRnwindResolver(upstream) }\n\n // Metro's haste-map indexes `watchFolders` at startup. Adding the\n // cache dir guarantees scheme style files + manifest get SHA1'd\n // without a \"Failed to get the SHA-1\" race when the first transform\n // writes them.\n const existingWatch = metroConfig.watchFolders ?? []\n metroConfig.watchFolders = existingWatch.includes(cacheDir) ? existingWatch : [...existingWatch, cacheDir]\n\n if (options.dtsFile !== false) {\n const dtsPath = options.dtsFile ?? path.resolve(projectRoot, 'rnwind-types.d.ts')\n const schemes = discoverSchemes(cssEntry, projectRoot)\n writeDtsFile(dtsPath, schemes)\n }\n\n // Watch the theme CSS. On edit, we rewrite scheme files AND touch\n // mtime on every transformed source file so Metro invalidates them\n // and re-transforms — the only reliable way to propagate theme\n // changes to an already-running dev server.\n watchThemeCss(cssEntry, projectRoot)\n\n return metroConfig\n}\n"],"names":["existsSync","watchFile","onThemeChange","state","getRnwindState","utimesSync","mkdirSync","configureRnwindState","createRnwindResolver","writeDtsFile"],"mappings":";;;;;;;;AAMA;AACA,MAAM,iBAAiB,GAAG,SAAS;AAEnC;;;;AAIG;AACH,IAAI,gBAAgB,GAAkD,IAAI;AAE1E;;;;;;;;;;;AAWG;AACH,SAAS,aAAa,CAAC,OAAe,EAAE,WAAmB,EAAA;AACzD,IAAA,IAAI,gBAAgB,EAAE,OAAO,KAAK,OAAO;QAAE;IAC3C,gBAAgB,EAAE,KAAK,EAAE;AACzB,IAAA,IAAI,CAACA,kBAAU,CAAC,OAAO,CAAC;QAAE;IAC1B,IAAI,OAAO,GAAG,KAAK;AACnB,IAAA,MAAM,OAAO,GAAGC,aAAS,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,MAAK;;;AAG7D,QAAA,IAAI,OAAO;YAAE;QACb,OAAO,GAAG,IAAI;QACd,cAAc,CAAC,YAAW;YACxB,OAAO,GAAG,KAAK;AACf,YAAA,IAAI;AACF,gBAAA,MAAMC,mBAAa,CAAC,WAAW,CAAC;gBAChC,kBAAkB,CAAC,WAAW,CAAC;YACjC;AAAE,YAAA,MAAM;;YAER;AACF,QAAA,CAAC,CAAC;AACJ,IAAA,CAAC,CAAC;AACF,IAAA,gBAAgB,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC,KAAK,EAAE,EAAE;AAC9D;AAEA;;;;;AAKG;AACH,SAAS,kBAAkB,CAAC,WAAmB,EAAA;AAC7C,IAAA,MAAMC,OAAK,GAAGC,oBAAc,CAAC,WAAW,CAAC;IACzC,MAAM,KAAK,GAAGD,OAAK,CAAC,OAAO,CAAC,aAAa,EAAE;AAC3C,IAAA,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE;AACtB,IAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;AACxB,QAAA,IAAI;YACF,IAAIH,kBAAU,CAAC,IAAI,CAAC;AAAE,gBAAAK,kBAAU,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC;QAClD;AAAE,QAAA,MAAM;;QAER;IACF;AACF;AAEA;;;;;;AAMG;AACH,SAAS,eAAe,GAAA;AACtB,IAAA,KAAK,MAAM,SAAS,IAAI,CAAC,mBAAmB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,eAAe,CAAC,EAAE;AAC3H,QAAA,IAAI;AACF,YAAA,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;QACnC;AAAE,QAAA,MAAM;;QAER;IACF;AACA,IAAA,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC;AACzE;AAEA;;;;;;AAMG;AACH,SAAS,eAAe,CAAC,WAAmB,EAAE,QAA4B,EAAA;AACxE,IAAA,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,iBAAiB,CAAC;IAC3F,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC;AACnF;AAEA;;;;;;;AAOG;AACH,SAAS,eAAe,CAAC,QAAgB,EAAE,WAAmB,EAAA;AAC5D,IAAA,IAAI,CAACL,kBAAU,CAAC,QAAQ,CAAC;AAAE,QAAA,OAAO,EAAE;AACpC,IAAA,IAAI;QACF,MAAM,EAAE,MAAM,EAAE,GAAGI,oBAAc,CAAC,WAAW,CAAC;AAC9C,QAAA,OAAO,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,IAAI,KAAK,MAAM,CAAC;IACjE;AAAE,IAAA,MAAM;AACN,QAAA,OAAO,EAAE;IACX;AACF;AA4CA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,gBAAgB,CAA4B,WAAc,EAAE,OAA2B,EAAA;AACrG,IAAA,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,WAAW,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE;IACnF,MAAM,QAAQ,GAAG,eAAe,CAAC,WAAW,EAAE,OAAO,CAAC,QAAQ,CAAC;AAC/D,IAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,YAAY,CAAC;IAE/HE,iBAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AACxC,IAAA,MAAM,YAAY,GAAG,CAAC,WAAW,CAAC,YAAY,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IAC1GC,0BAAoB,CAAC,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,CAAC,WAAW,CAAC;;;;;;;AAQ3E,IAAA,IAAI;QACF,KAAKH,oBAAc,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE;IAC7D;AAAE,IAAA,MAAM;;IAER;;;;;AAMA,IAAA,MAAM,uBAAuB,GAAG,WAAW,CAAC,WAAW,EAAE,oBAAoB;IAC7E,IAAI,OAAO,uBAAuB,KAAK,QAAQ,IAAI,uBAAuB,CAAC,MAAM,GAAG,CAAC,EAAE;AACrF,QAAA,OAAO,CAAC,GAAG,CAAC,2BAA2B,GAAG,uBAAuB;IACnE;IACA,MAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,EAAE,cAAc,IAAI,IAAI;AAC7D,IAAA,WAAW,CAAC,WAAW,GAAG,EAAE,GAAG,WAAW,CAAC,WAAW,EAAE,oBAAoB,EAAE,eAAe,EAAE,EAAE;AACjG,IAAA,WAAW,CAAC,QAAQ,GAAG,EAAE,GAAG,WAAW,CAAC,QAAQ,EAAE,cAAc,EAAEI,6BAAoB,CAAC,QAAQ,CAAC,EAAE;;;;;AAMlG,IAAA,MAAM,aAAa,GAAG,WAAW,CAAC,YAAY,IAAI,EAAE;IACpD,WAAW,CAAC,YAAY,GAAG,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,aAAa,GAAG,CAAC,GAAG,aAAa,EAAE,QAAQ,CAAC;AAE1G,IAAA,IAAI,OAAO,CAAC,OAAO,KAAK,KAAK,EAAE;AAC7B,QAAA,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,mBAAmB,CAAC;QACjF,MAAM,OAAO,GAAG,eAAe,CAAC,QAAQ,EAAE,WAAW,CAAC;AACtD,QAAAC,gBAAY,CAAC,OAAO,EAAE,OAAO,CAAC;IAChC;;;;;AAMA,IAAA,aAAa,CAAC,QAAQ,EAAE,WAAW,CAAC;AAEpC,IAAA,OAAO,WAAW;AACpB;;;;"}
@@ -10,35 +10,20 @@ export interface RnwindMetroOptions {
10
10
  /** Cache directory. Absolute, or relative to `projectRoot`. Default: `.rnwind` at project root. */
11
11
  cacheDir?: string;
12
12
  /**
13
- * Extra JSX prop-name prefixes that rnwind should rewrite. Each
14
- * prefix `P` turns `<Tag PClassName="…">` into `<Tag
15
- * PStyle={lookupCss()}>`. The built-in `'contentContainer'` prefix
16
- * is always on (covers ScrollView / FlatList / SectionList); user
17
- * entries merge on top.
18
- */
19
- classNamePrefixes?: readonly string[];
20
- /**
21
- * Extra module specifiers whose JSX exports rnwind should treat as
22
- * "host components" — i.e. tags whose `className="…"` attribute is
23
- * rewritten to `style={lookupCss(…)}` at build time (zero runtime
24
- * cost). Merged with the built-in defaults: `react-native`,
13
+ * Extra module specifiers whose component exports rnwind should
14
+ * auto-wrap at import sites — `import { View } from 'react-native'`
15
+ * becomes `const View = wrap(_rnw0)` so `<View className="…">` resolves
16
+ * styles at runtime. Merged with the built-in defaults: `react-native`,
25
17
  * `react-native-reanimated`, `react-native-svg`,
26
- * `react-native-gesture-handler`, `expo-linear-gradient`, `expo-image`.
18
+ * `react-native-gesture-handler`, `react-native-safe-area-context`,
19
+ * `expo-linear-gradient`, `expo-image`, and more.
27
20
  *
28
- * Anything NOT marked as a host has its `className` left untouched
29
- * the importing component receives the raw string and decides what
30
- * to do with it. Use this option to opt your design-system / UI
31
- * primitive packages into the zero-runtime path.
32
- */
33
- hostSources?: readonly string[];
34
- /**
35
- * Extra JSX tag names (verbatim — may include `.` for member access
36
- * like `'Animated.View'`) rnwind should treat as host components,
37
- * regardless of where they're imported from. Useful for one-off
38
- * escape-hatches: `import { View as MyBox } from 'react-native'`
39
- * doesn't change the local name → `'MyBox'` here picks it up.
21
+ * A module NOT in this list keeps its raw imports the importing
22
+ * component receives `className` as a plain prop and can resolve it
23
+ * via `useCss` / `wrap` itself. Use this to opt your design-system /
24
+ * UI primitive packages into the auto-wrap path.
40
25
  */
41
- hostComponents?: readonly string[];
26
+ wrapModules?: readonly string[];
42
27
  }
43
28
  /** Shape we mutate on Metro's config. Loose so we don't pin Metro's internal types. */
44
29
  export interface MetroConfigLike {
@@ -0,0 +1,273 @@
1
+ 'use strict';
2
+
3
+ var t = require('@babel/types');
4
+
5
+ function _interopNamespaceDefault(e) {
6
+ var n = Object.create(null);
7
+ if (e) {
8
+ Object.keys(e).forEach(function (k) {
9
+ if (k !== 'default') {
10
+ var d = Object.getOwnPropertyDescriptor(e, k);
11
+ Object.defineProperty(n, k, d.get ? d : {
12
+ enumerable: true,
13
+ get: function () { return e[k]; }
14
+ });
15
+ }
16
+ });
17
+ }
18
+ n.default = e;
19
+ return Object.freeze(n);
20
+ }
21
+
22
+ var t__namespace = /*#__PURE__*/_interopNamespaceDefault(t);
23
+
24
+ /**
25
+ * Build-time import rewrite. For every `import { View } from
26
+ * 'react-native'` (and the other configured modules) it aliases the
27
+ * original export and binds a `wrap()`-ed component in its place:
28
+ *
29
+ * ```
30
+ * import { View, StyleSheet } from 'react-native'
31
+ * ⇩
32
+ * import { View as _rnw0, StyleSheet } from 'react-native'
33
+ * import { wrap as _rnwWrap } from 'rnwind'
34
+ * const View = _rnwWrap(_rnw0)
35
+ * ```
36
+ *
37
+ * Now `<View className="…">` resolves className → style at render via the
38
+ * wrapper — no matter how className arrived (literal, `{...rest}` spread,
39
+ * forwarded through custom layers). Non-component exports (`StyleSheet`)
40
+ * are left untouched.
41
+ */
42
+ /** Local binding the injected `wrap` import is aliased to. */
43
+ const WRAP_LOCAL = '_rnwWrap';
44
+ /** Local binding the injected `wrapNamespace` import is aliased to. */
45
+ const WRAP_NS_LOCAL = '_rnwWrapNs';
46
+ /** Module the wrapper is imported from. */
47
+ const RUNTIME_MODULE = 'rnwind';
48
+ /**
49
+ * Wrap-modules whose DEFAULT export is a component NAMESPACE accessed via
50
+ * member expressions (`Animated.View`), not a single component. Their
51
+ * default import is bound through `wrapNamespace` (a Proxy that wraps each
52
+ * accessed component member) instead of `wrap`. Every other default import
53
+ * is treated as a plain component.
54
+ */
55
+ const NAMESPACE_DEFAULT_MODULES = new Set(['react-native-reanimated']);
56
+ /**
57
+ * react-native mixes styleable components with utilities (`StyleSheet`,
58
+ * `Platform`, …). Only these named exports are wrapped; everything else
59
+ * passes through. Other ecosystem modules export components only and use
60
+ * the `'all'` policy instead.
61
+ */
62
+ const REACT_NATIVE_COMPONENTS = new Set([
63
+ 'View',
64
+ 'Text',
65
+ 'TextInput',
66
+ 'Pressable',
67
+ 'ScrollView',
68
+ 'Image',
69
+ 'ImageBackground',
70
+ 'FlatList',
71
+ 'SectionList',
72
+ 'VirtualizedList',
73
+ 'KeyboardAvoidingView',
74
+ 'SafeAreaView',
75
+ 'Modal',
76
+ 'Switch',
77
+ 'RefreshControl',
78
+ 'ActivityIndicator',
79
+ 'TouchableOpacity',
80
+ 'TouchableHighlight',
81
+ 'TouchableWithoutFeedback',
82
+ 'TouchableNativeFeedback',
83
+ 'Button',
84
+ 'StatusBar',
85
+ ]);
86
+ /**
87
+ * Named exports that LOOK like components (PascalCase) under an `'all'`
88
+ * policy but aren't — React contexts, gesture-handler enums/namespaces,
89
+ * etc. Wrapping these would turn `Gesture.Pan()` / `State.ACTIVE` /
90
+ * `<XContext.Provider>` into a `wrap()`-ed component and break them.
91
+ * Names ending in `Context` are excluded separately.
92
+ */
93
+ const NON_COMPONENT_EXPORTS = new Set([
94
+ 'Gesture',
95
+ 'GestureObjects',
96
+ 'State',
97
+ 'Directions',
98
+ 'Extrapolation',
99
+ 'Extrapolate',
100
+ 'Easing',
101
+ 'ReduceMotion',
102
+ 'KeyframeRegistry',
103
+ ]);
104
+ /**
105
+ * Default module → wrap policy. react-native is allow-listed (mixed
106
+ * exports); the rest are component-only packages → `'all'`. Only modules
107
+ * the project has installed are ever hit (you can't import from a missing
108
+ * package), so listing optional peers is free.
109
+ */
110
+ const DEFAULT_WRAP_MODULES = new Map([
111
+ ['react-native', REACT_NATIVE_COMPONENTS],
112
+ ['react-native-reanimated', 'all'],
113
+ ['react-native-svg', 'all'],
114
+ ['react-native-gesture-handler', 'all'],
115
+ ['react-native-safe-area-context', 'all'],
116
+ ['expo-linear-gradient', 'all'],
117
+ ['expo-image', 'all'],
118
+ ['expo-blur', 'all'],
119
+ ['expo-symbols', 'all'],
120
+ ['@shopify/flash-list', 'all'],
121
+ ['@shopify/react-native-skia', 'all'],
122
+ ['lottie-react-native', 'all'],
123
+ ]);
124
+ /**
125
+ * Whether a named import from a wrap-module should be wrapped.
126
+ *
127
+ * Explicit allow-lists (react-native) match by exact name. The `'all'`
128
+ * policy wraps only component-style names — PascalCase, not a React
129
+ * context (`*Context`), and not a known non-component export. This is
130
+ * what stops `useSafeAreaInsets` (a hook) from being wrapped into a
131
+ * component and crashing when called.
132
+ * @param policy The module's wrap policy.
133
+ * @param importedName The exported name being imported.
134
+ * @returns True when the name is a component to wrap.
135
+ */
136
+ function shouldWrap(policy, importedName) {
137
+ if (policy !== 'all')
138
+ return policy.has(importedName);
139
+ if (!/^[A-Z]/.test(importedName))
140
+ return false;
141
+ if (importedName.endsWith('Context'))
142
+ return false;
143
+ return !NON_COMPONENT_EXPORTS.has(importedName);
144
+ }
145
+ /**
146
+ * Merge user-supplied wrap modules onto the defaults — a bare module name
147
+ * adopts the `'all'` policy.
148
+ * @param extra User module specifiers (or undefined).
149
+ * @returns Effective module → policy map.
150
+ */
151
+ function buildWrapModules(extra) {
152
+ if (!extra || extra.length === 0)
153
+ return DEFAULT_WRAP_MODULES;
154
+ const merged = new Map(DEFAULT_WRAP_MODULES);
155
+ for (const moduleName of extra)
156
+ if (!merged.has(moduleName))
157
+ merged.set(moduleName, 'all');
158
+ return merged;
159
+ }
160
+ /**
161
+ * The `imported` name of an import specifier (`import { a as b }` → `'a'`).
162
+ * @param specifier Named import specifier.
163
+ * @returns The exported name.
164
+ */
165
+ function importedNameOf(specifier) {
166
+ return t__namespace.isIdentifier(specifier.imported) ? specifier.imported.name : specifier.imported.value;
167
+ }
168
+ /**
169
+ * Build `const Local = <wrapper>(alias)` and rebind the specifier's local
170
+ * to `alias` in place.
171
+ * @param specifier The import specifier to rebind.
172
+ * @param alias The `_rnwN` alias to bind the original import to.
173
+ * @param wrapper The runtime helper local (`_rnwWrap` / `_rnwWrapNs`).
174
+ * @returns The wrap declaration.
175
+ */
176
+ function makeWrapDecl(specifier, alias, wrapper) {
177
+ const { name: localName } = specifier.local;
178
+ specifier.local = t__namespace.identifier(alias);
179
+ return t__namespace.variableDeclaration('const', [
180
+ t__namespace.variableDeclarator(t__namespace.identifier(localName), t__namespace.callExpression(t__namespace.identifier(wrapper), [t__namespace.identifier(alias)])),
181
+ ]);
182
+ }
183
+ /**
184
+ * Rewrite one import declaration's wrappable specifiers, aliasing each to
185
+ * `_rnw<N>` in place:
186
+ * - named (`{ View }`) → `const View = wrap(_rnwN)` (per policy),
187
+ * - namespace (`* as Animated`) → `const Animated = wrapNamespace(_rnwN)`,
188
+ * - default → `wrapNamespace` for {@link NAMESPACE_DEFAULT_MODULES}
189
+ * (reanimated's `Animated`), else `wrap` (a plain default component).
190
+ * @param node Import declaration to inspect.
191
+ * @param policy The module's wrap policy.
192
+ * @param counter Next alias index (caller-threaded for uniqueness).
193
+ * @returns New wrap declarations, advanced counter, and whether any
194
+ * binding used `wrapNamespace`.
195
+ */
196
+ function wrapSpecifiers(node, policy, counter) {
197
+ const decls = [];
198
+ const moduleName = node.source.value;
199
+ let next = counter;
200
+ let usesNamespace = false;
201
+ for (const specifier of node.specifiers) {
202
+ if (t__namespace.isImportSpecifier(specifier)) {
203
+ if (!shouldWrap(policy, importedNameOf(specifier)))
204
+ continue;
205
+ decls.push(makeWrapDecl(specifier, `_rnw${next}`, WRAP_LOCAL));
206
+ next += 1;
207
+ continue;
208
+ }
209
+ const isNamespace = t__namespace.isImportNamespaceSpecifier(specifier) || NAMESPACE_DEFAULT_MODULES.has(moduleName);
210
+ const wrapper = isNamespace ? WRAP_NS_LOCAL : WRAP_LOCAL;
211
+ decls.push(makeWrapDecl(specifier, `_rnw${next}`, wrapper));
212
+ next += 1;
213
+ if (isNamespace)
214
+ usesNamespace = true;
215
+ }
216
+ return { decls, counter: next, usesNamespace };
217
+ }
218
+ /**
219
+ * Insert the `wrap` import at the top and the `const X = wrap(_rnwN)`
220
+ * declarations AFTER every import. The consts reference the aliased
221
+ * binding `_rnwN`, and in Metro's real transform (CommonJS interop + the
222
+ * reanimated worklets plugin) a const placed above its source import
223
+ * evaluates before the binding initialises → `ReferenceError: _rnw0
224
+ * doesn't exist`. ESM-only hoisting would mask this; the bundle does not.
225
+ * @param ast Parsed Babel file (mutated).
226
+ * @param wrapDecls The wrap declarations to place.
227
+ * @param usesNamespace Whether any binding used `wrapNamespace`.
228
+ */
229
+ function placeWrapDecls(ast, wrapDecls, usesNamespace) {
230
+ const specifiers = [t__namespace.importSpecifier(t__namespace.identifier(WRAP_LOCAL), t__namespace.identifier('wrap'))];
231
+ if (usesNamespace)
232
+ specifiers.push(t__namespace.importSpecifier(t__namespace.identifier(WRAP_NS_LOCAL), t__namespace.identifier('wrapNamespace')));
233
+ ast.program.body.unshift(t__namespace.importDeclaration(specifiers, t__namespace.stringLiteral(RUNTIME_MODULE)));
234
+ let afterImports = 0;
235
+ for (let index = 0; index < ast.program.body.length; index += 1) {
236
+ if (t__namespace.isImportDeclaration(ast.program.body[index]))
237
+ afterImports = index + 1;
238
+ }
239
+ ast.program.body.splice(afterImports, 0, ...wrapDecls);
240
+ }
241
+ /**
242
+ * Rewrite component imports from the configured wrap-modules into
243
+ * `wrap()`-ed bindings, in place. Injects the `wrap` import once when any
244
+ * binding was rewritten.
245
+ * @param ast Parsed Babel file (mutated).
246
+ * @param modules Effective module → policy map.
247
+ * @returns True when at least one import was wrapped.
248
+ */
249
+ function rewriteWrapImports(ast, modules) {
250
+ const wrapDecls = [];
251
+ let counter = 0;
252
+ let usesNamespace = false;
253
+ for (const node of ast.program.body) {
254
+ if (!t__namespace.isImportDeclaration(node))
255
+ continue;
256
+ const policy = modules.get(node.source.value);
257
+ if (!policy)
258
+ continue;
259
+ const { decls, counter: nextCounter, usesNamespace: ns } = wrapSpecifiers(node, policy, counter);
260
+ counter = nextCounter;
261
+ usesNamespace = usesNamespace || ns;
262
+ wrapDecls.push(...decls);
263
+ }
264
+ if (wrapDecls.length === 0)
265
+ return false;
266
+ placeWrapDecls(ast, wrapDecls, usesNamespace);
267
+ return true;
268
+ }
269
+
270
+ exports.DEFAULT_WRAP_MODULES = DEFAULT_WRAP_MODULES;
271
+ exports.buildWrapModules = buildWrapModules;
272
+ exports.rewriteWrapImports = rewriteWrapImports;
273
+ //# sourceMappingURL=wrap-imports.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrap-imports.cjs","sources":["../../../../src/metro/wrap-imports.ts"],"sourcesContent":["import * as t from '@babel/types'\nimport type { File } from '@babel/types'\n\n/**\n * Build-time import rewrite. For every `import { View } from\n * 'react-native'` (and the other configured modules) it aliases the\n * original export and binds a `wrap()`-ed component in its place:\n *\n * ```\n * import { View, StyleSheet } from 'react-native'\n * ⇩\n * import { View as _rnw0, StyleSheet } from 'react-native'\n * import { wrap as _rnwWrap } from 'rnwind'\n * const View = _rnwWrap(_rnw0)\n * ```\n *\n * Now `<View className=\"…\">` resolves className → style at render via the\n * wrapper — no matter how className arrived (literal, `{...rest}` spread,\n * forwarded through custom layers). Non-component exports (`StyleSheet`)\n * are left untouched.\n */\n\n/** Local binding the injected `wrap` import is aliased to. */\nconst WRAP_LOCAL = '_rnwWrap'\n/** Local binding the injected `wrapNamespace` import is aliased to. */\nconst WRAP_NS_LOCAL = '_rnwWrapNs'\n/** Module the wrapper is imported from. */\nconst RUNTIME_MODULE = 'rnwind'\n\n/**\n * Wrap-modules whose DEFAULT export is a component NAMESPACE accessed via\n * member expressions (`Animated.View`), not a single component. Their\n * default import is bound through `wrapNamespace` (a Proxy that wraps each\n * accessed component member) instead of `wrap`. Every other default import\n * is treated as a plain component.\n */\nconst NAMESPACE_DEFAULT_MODULES: ReadonlySet<string> = new Set(['react-native-reanimated'])\n\n/**\n * react-native mixes styleable components with utilities (`StyleSheet`,\n * `Platform`, …). Only these named exports are wrapped; everything else\n * passes through. Other ecosystem modules export components only and use\n * the `'all'` policy instead.\n */\nconst REACT_NATIVE_COMPONENTS: ReadonlySet<string> = new Set([\n 'View',\n 'Text',\n 'TextInput',\n 'Pressable',\n 'ScrollView',\n 'Image',\n 'ImageBackground',\n 'FlatList',\n 'SectionList',\n 'VirtualizedList',\n 'KeyboardAvoidingView',\n 'SafeAreaView',\n 'Modal',\n 'Switch',\n 'RefreshControl',\n 'ActivityIndicator',\n 'TouchableOpacity',\n 'TouchableHighlight',\n 'TouchableWithoutFeedback',\n 'TouchableNativeFeedback',\n 'Button',\n 'StatusBar',\n])\n\n/**\n * Named exports that LOOK like components (PascalCase) under an `'all'`\n * policy but aren't — React contexts, gesture-handler enums/namespaces,\n * etc. Wrapping these would turn `Gesture.Pan()` / `State.ACTIVE` /\n * `<XContext.Provider>` into a `wrap()`-ed component and break them.\n * Names ending in `Context` are excluded separately.\n */\nconst NON_COMPONENT_EXPORTS: ReadonlySet<string> = new Set([\n 'Gesture',\n 'GestureObjects',\n 'State',\n 'Directions',\n 'Extrapolation',\n 'Extrapolate',\n 'Easing',\n 'ReduceMotion',\n 'KeyframeRegistry',\n])\n\n/** Per-module policy: an explicit allow-list, or `'all'` named exports. */\nexport type WrapPolicy = 'all' | ReadonlySet<string>\n\n/**\n * Default module → wrap policy. react-native is allow-listed (mixed\n * exports); the rest are component-only packages → `'all'`. Only modules\n * the project has installed are ever hit (you can't import from a missing\n * package), so listing optional peers is free.\n */\nexport const DEFAULT_WRAP_MODULES: ReadonlyMap<string, WrapPolicy> = new Map<string, WrapPolicy>([\n ['react-native', REACT_NATIVE_COMPONENTS],\n ['react-native-reanimated', 'all'],\n ['react-native-svg', 'all'],\n ['react-native-gesture-handler', 'all'],\n ['react-native-safe-area-context', 'all'],\n ['expo-linear-gradient', 'all'],\n ['expo-image', 'all'],\n ['expo-blur', 'all'],\n ['expo-symbols', 'all'],\n ['@shopify/flash-list', 'all'],\n ['@shopify/react-native-skia', 'all'],\n ['lottie-react-native', 'all'],\n])\n\n/**\n * Whether a named import from a wrap-module should be wrapped.\n *\n * Explicit allow-lists (react-native) match by exact name. The `'all'`\n * policy wraps only component-style names — PascalCase, not a React\n * context (`*Context`), and not a known non-component export. This is\n * what stops `useSafeAreaInsets` (a hook) from being wrapped into a\n * component and crashing when called.\n * @param policy The module's wrap policy.\n * @param importedName The exported name being imported.\n * @returns True when the name is a component to wrap.\n */\nfunction shouldWrap(policy: WrapPolicy, importedName: string): boolean {\n if (policy !== 'all') return policy.has(importedName)\n if (!/^[A-Z]/.test(importedName)) return false\n if (importedName.endsWith('Context')) return false\n return !NON_COMPONENT_EXPORTS.has(importedName)\n}\n\n/**\n * Merge user-supplied wrap modules onto the defaults — a bare module name\n * adopts the `'all'` policy.\n * @param extra User module specifiers (or undefined).\n * @returns Effective module → policy map.\n */\nexport function buildWrapModules(extra?: readonly string[]): ReadonlyMap<string, WrapPolicy> {\n if (!extra || extra.length === 0) return DEFAULT_WRAP_MODULES\n const merged = new Map<string, WrapPolicy>(DEFAULT_WRAP_MODULES)\n for (const moduleName of extra) if (!merged.has(moduleName)) merged.set(moduleName, 'all')\n return merged\n}\n\n/**\n * The `imported` name of an import specifier (`import { a as b }` → `'a'`).\n * @param specifier Named import specifier.\n * @returns The exported name.\n */\nfunction importedNameOf(specifier: t.ImportSpecifier): string {\n return t.isIdentifier(specifier.imported) ? specifier.imported.name : specifier.imported.value\n}\n\n/**\n * Build `const Local = <wrapper>(alias)` and rebind the specifier's local\n * to `alias` in place.\n * @param specifier The import specifier to rebind.\n * @param alias The `_rnwN` alias to bind the original import to.\n * @param wrapper The runtime helper local (`_rnwWrap` / `_rnwWrapNs`).\n * @returns The wrap declaration.\n */\nfunction makeWrapDecl(\n specifier: t.ImportSpecifier | t.ImportDefaultSpecifier | t.ImportNamespaceSpecifier,\n alias: string,\n wrapper: string,\n): t.VariableDeclaration {\n const { name: localName } = specifier.local\n specifier.local = t.identifier(alias)\n return t.variableDeclaration('const', [\n t.variableDeclarator(t.identifier(localName), t.callExpression(t.identifier(wrapper), [t.identifier(alias)])),\n ])\n}\n\n/**\n * Rewrite one import declaration's wrappable specifiers, aliasing each to\n * `_rnw<N>` in place:\n * - named (`{ View }`) → `const View = wrap(_rnwN)` (per policy),\n * - namespace (`* as Animated`) → `const Animated = wrapNamespace(_rnwN)`,\n * - default → `wrapNamespace` for {@link NAMESPACE_DEFAULT_MODULES}\n * (reanimated's `Animated`), else `wrap` (a plain default component).\n * @param node Import declaration to inspect.\n * @param policy The module's wrap policy.\n * @param counter Next alias index (caller-threaded for uniqueness).\n * @returns New wrap declarations, advanced counter, and whether any\n * binding used `wrapNamespace`.\n */\nfunction wrapSpecifiers(\n node: t.ImportDeclaration,\n policy: WrapPolicy,\n counter: number,\n): { decls: t.VariableDeclaration[]; counter: number; usesNamespace: boolean } {\n const decls: t.VariableDeclaration[] = []\n const moduleName = node.source.value\n let next = counter\n let usesNamespace = false\n for (const specifier of node.specifiers) {\n if (t.isImportSpecifier(specifier)) {\n if (!shouldWrap(policy, importedNameOf(specifier))) continue\n decls.push(makeWrapDecl(specifier, `_rnw${next}`, WRAP_LOCAL))\n next += 1\n continue\n }\n const isNamespace = t.isImportNamespaceSpecifier(specifier) || NAMESPACE_DEFAULT_MODULES.has(moduleName)\n const wrapper = isNamespace ? WRAP_NS_LOCAL : WRAP_LOCAL\n decls.push(makeWrapDecl(specifier, `_rnw${next}`, wrapper))\n next += 1\n if (isNamespace) usesNamespace = true\n }\n return { decls, counter: next, usesNamespace }\n}\n\n/**\n * Insert the `wrap` import at the top and the `const X = wrap(_rnwN)`\n * declarations AFTER every import. The consts reference the aliased\n * binding `_rnwN`, and in Metro's real transform (CommonJS interop + the\n * reanimated worklets plugin) a const placed above its source import\n * evaluates before the binding initialises → `ReferenceError: _rnw0\n * doesn't exist`. ESM-only hoisting would mask this; the bundle does not.\n * @param ast Parsed Babel file (mutated).\n * @param wrapDecls The wrap declarations to place.\n * @param usesNamespace Whether any binding used `wrapNamespace`.\n */\nfunction placeWrapDecls(ast: File, wrapDecls: readonly t.VariableDeclaration[], usesNamespace: boolean): void {\n const specifiers = [t.importSpecifier(t.identifier(WRAP_LOCAL), t.identifier('wrap'))]\n if (usesNamespace) specifiers.push(t.importSpecifier(t.identifier(WRAP_NS_LOCAL), t.identifier('wrapNamespace')))\n ast.program.body.unshift(t.importDeclaration(specifiers, t.stringLiteral(RUNTIME_MODULE)))\n let afterImports = 0\n for (let index = 0; index < ast.program.body.length; index += 1) {\n if (t.isImportDeclaration(ast.program.body[index])) afterImports = index + 1\n }\n ast.program.body.splice(afterImports, 0, ...wrapDecls)\n}\n\n/**\n * Rewrite component imports from the configured wrap-modules into\n * `wrap()`-ed bindings, in place. Injects the `wrap` import once when any\n * binding was rewritten.\n * @param ast Parsed Babel file (mutated).\n * @param modules Effective module → policy map.\n * @returns True when at least one import was wrapped.\n */\nexport function rewriteWrapImports(ast: File, modules: ReadonlyMap<string, WrapPolicy>): boolean {\n const wrapDecls: t.VariableDeclaration[] = []\n let counter = 0\n let usesNamespace = false\n\n for (const node of ast.program.body) {\n if (!t.isImportDeclaration(node)) continue\n const policy = modules.get(node.source.value)\n if (!policy) continue\n const { decls, counter: nextCounter, usesNamespace: ns } = wrapSpecifiers(node, policy, counter)\n counter = nextCounter\n usesNamespace = usesNamespace || ns\n wrapDecls.push(...decls)\n }\n\n if (wrapDecls.length === 0) return false\n placeWrapDecls(ast, wrapDecls, usesNamespace)\n return true\n}\n"],"names":["t"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { File } from '@babel/types';
2
+ /** Per-module policy: an explicit allow-list, or `'all'` named exports. */
3
+ export type WrapPolicy = 'all' | ReadonlySet<string>;
4
+ /**
5
+ * Default module → wrap policy. react-native is allow-listed (mixed
6
+ * exports); the rest are component-only packages → `'all'`. Only modules
7
+ * the project has installed are ever hit (you can't import from a missing
8
+ * package), so listing optional peers is free.
9
+ */
10
+ export declare const DEFAULT_WRAP_MODULES: ReadonlyMap<string, WrapPolicy>;
11
+ /**
12
+ * Merge user-supplied wrap modules onto the defaults — a bare module name
13
+ * adopts the `'all'` policy.
14
+ * @param extra User module specifiers (or undefined).
15
+ * @returns Effective module → policy map.
16
+ */
17
+ export declare function buildWrapModules(extra?: readonly string[]): ReadonlyMap<string, WrapPolicy>;
18
+ /**
19
+ * Rewrite component imports from the configured wrap-modules into
20
+ * `wrap()`-ed bindings, in place. Injects the `wrap` import once when any
21
+ * binding was rewritten.
22
+ * @param ast Parsed Babel file (mutated).
23
+ * @param modules Effective module → policy map.
24
+ * @returns True when at least one import was wrapped.
25
+ */
26
+ export declare function rewriteWrapImports(ast: File, modules: ReadonlyMap<string, WrapPolicy>): boolean;
@@ -46,22 +46,6 @@ const RnwindContext = React.createContext(DEFAULT_STATE);
46
46
  function useRnwind() {
47
47
  return React.useContext(RnwindContext);
48
48
  }
49
- /**
50
- * Internal context hook the babel transformer injects at the top of
51
- * every rewritten component as `const _t = useR_()`. Same body as the
52
- * public {@link useRnwind} — exposed under a `use*`-prefixed name so
53
- * react-refresh's babel plugin (which only tracks call-sites whose
54
- * identifier matches `^use[A-Z]`) folds it into each component's
55
- * fast-refresh signature. Without that prefix the signature stayed
56
- * stable across transformer changes; HMR then preserved fiber state
57
- * while the rendered hook list shifted, surfacing as "change in the
58
- * order of Hooks" runtime errors. Trailing underscore keeps it
59
- * visually distinct from the user-facing `useRnwind`.
60
- * @returns Active rnwind state.
61
- */
62
- function useR_() {
63
- return React.useContext(RnwindContext);
64
- }
65
49
  /**
66
50
  * Provider for rnwind's full runtime state. fontScale + windowWidth
67
51
  * come from `useWindowDimensions()` so they react to OS-level
@@ -93,6 +77,5 @@ function RnwindProvider({ scheme, tables, insets, onHaptics, children }) {
93
77
  }
94
78
 
95
79
  exports.RnwindProvider = RnwindProvider;
96
- exports.useR_ = useR_;
97
80
  exports.useRnwind = useRnwind;
98
81
  //# sourceMappingURL=rnwind-provider.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"rnwind-provider.cjs","sources":["../../../../../src/runtime/components/rnwind-provider.tsx"],"sourcesContent":["import { createContext, useContext, useMemo, type ReactNode } from 'react'\nimport { useWindowDimensions } from 'react-native'\nimport type { ThemeTables } from '../../core/types'\nimport type { Scheme } from '../types'\nimport type { OnHaptics } from '../../core/parser/haptics'\nimport { activeBreakpointFor, BASE_BREAKPOINT, loadScheme } from '../lookup-css'\n\n/**\n * Per-render safe-area insets snapshot. Bridge from any source\n * (`useSafeAreaInsets()`, expo-router insets, a manually computed\n * value) into the {@link RnwindProvider} — rnwind stays\n * library-agnostic.\n */\nexport type Insets = Readonly<{\n top: number\n right: number\n bottom: number\n left: number\n}>\n\n/**\n * Single value carried by the rnwind context. Every piece of runtime\n * state rnwind needs — scheme, theme tables, insets, font scale,\n * window width, active responsive breakpoint, optional haptic\n * dispatcher — lives on this one bag. Consumers read it via\n * {@link useRnwind} and either destructure or forward straight to\n * `lookupCss` / `useCss`.\n *\n * `activeBreakpoint` is the highest-threshold registered breakpoint\n * whose min-width is `<= windowWidth`, or `'base'` when below the\n * smallest one (mobile-first tier) or when no breakpoints are\n * registered yet (tests, bundles without rnwind-transformed sources).\n * Always a string — never null. Reactive: it updates with\n * `useWindowDimensions().width`, so consumers can branch on it\n * without a separate hook.\n */\nexport type RnwindState = Readonly<{\n scheme: Scheme\n tables: ThemeTables\n insets: Insets\n onHaptics: OnHaptics | undefined\n fontScale: number\n windowWidth: number\n activeBreakpoint: string\n}>\n\n/** Props accepted by {@link RnwindProvider}. */\nexport type RnwindProviderProps = Readonly<{\n scheme: Scheme\n tables?: ThemeTables\n insets?: Partial<Insets>\n onHaptics?: OnHaptics\n children?: ReactNode\n}>\n\nconst EMPTY_TABLES: ThemeTables = {}\nconst ZERO_INSETS: Insets = { top: 0, right: 0, bottom: 0, left: 0 }\nconst DEFAULT_STATE: RnwindState = {\n scheme: 'light' as Scheme,\n tables: EMPTY_TABLES,\n insets: ZERO_INSETS,\n onHaptics: undefined,\n fontScale: 1,\n windowWidth: 0,\n activeBreakpoint: BASE_BREAKPOINT,\n}\n\n/**\n * Normalise a `Partial<Insets>` into a complete {@link Insets}, returning\n * the shared {@link ZERO_INSETS} reference when nothing is supplied so\n * downstream memoisation stays stable.\n * @param partial Caller-supplied insets (or undefined).\n * @returns Complete insets record.\n */\nfunction normaliseInsets(partial: Partial<Insets> | undefined): Insets {\n if (!partial) return ZERO_INSETS\n const top = partial.top ?? 0\n const right = partial.right ?? 0\n const bottom = partial.bottom ?? 0\n const left = partial.left ?? 0\n if (top === 0 && right === 0 && bottom === 0 && left === 0) return ZERO_INSETS\n return { top, right, bottom, left }\n}\n\n/** Single internal context the runtime reads from. */\nconst RnwindContext = createContext<RnwindState>(DEFAULT_STATE)\n\n/**\n * Read rnwind's full runtime state — scheme, theme tables, insets,\n * fontScale, windowWidth, onHaptics — in one go. Pass the returned\n * value straight to `lookupCss` / `useCss`, or destructure what you\n * need.\n * @returns Active rnwind state under the nearest {@link RnwindProvider}.\n */\nexport function useRnwind(): RnwindState {\n return useContext(RnwindContext)\n}\n\n/**\n * Internal context hook the babel transformer injects at the top of\n * every rewritten component as `const _t = useR_()`. Same body as the\n * public {@link useRnwind} — exposed under a `use*`-prefixed name so\n * react-refresh's babel plugin (which only tracks call-sites whose\n * identifier matches `^use[A-Z]`) folds it into each component's\n * fast-refresh signature. Without that prefix the signature stayed\n * stable across transformer changes; HMR then preserved fiber state\n * while the rendered hook list shifted, surfacing as \"change in the\n * order of Hooks\" runtime errors. Trailing underscore keeps it\n * visually distinct from the user-facing `useRnwind`.\n * @returns Active rnwind state.\n */\nexport function useR_(): RnwindState {\n return useContext(RnwindContext)\n}\n\n/**\n * Provider for rnwind's full runtime state. fontScale + windowWidth\n * come from `useWindowDimensions()` so they react to OS-level\n * orientation / accessibility-text-size changes automatically.\n * @param props Provider props.\n * @param props.scheme Active scheme name.\n * @param props.tables Optional pre-resolved token tables.\n * @param props.insets Optional safe-area insets.\n * @param props.onHaptics Optional haptic dispatcher.\n * @param props.children React subtree.\n * @returns Provider element.\n */\nexport function RnwindProvider({ scheme, tables, insets, onHaptics, children }: RnwindProviderProps): ReactNode {\n const normalized = normaliseInsets(insets)\n const { fontScale, width } = useWindowDimensions()\n const value = useMemo<RnwindState>(() => {\n loadScheme(scheme)\n return {\n scheme,\n tables: tables ?? EMPTY_TABLES,\n insets: normalized,\n onHaptics,\n fontScale,\n windowWidth: width,\n activeBreakpoint: activeBreakpointFor(width),\n }\n }, [scheme, tables, normalized, onHaptics, fontScale, width])\n return <RnwindContext.Provider value={value}>{children}</RnwindContext.Provider>\n}\n"],"names":["BASE_BREAKPOINT","createContext","useContext","useWindowDimensions","useMemo","loadScheme","activeBreakpointFor","_jsx"],"mappings":";;;;;;;AAuDA,MAAM,YAAY,GAAgB,EAAE;AACpC,MAAM,WAAW,GAAW,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE;AACpE,MAAM,aAAa,GAAgB;AACjC,IAAA,MAAM,EAAE,OAAiB;AACzB,IAAA,MAAM,EAAE,YAAY;AACpB,IAAA,MAAM,EAAE,WAAW;AACnB,IAAA,SAAS,EAAE,SAAS;AACpB,IAAA,SAAS,EAAE,CAAC;AACZ,IAAA,WAAW,EAAE,CAAC;AACd,IAAA,gBAAgB,EAAEA,yBAAe;CAClC;AAED;;;;;;AAMG;AACH,SAAS,eAAe,CAAC,OAAoC,EAAA;AAC3D,IAAA,IAAI,CAAC,OAAO;AAAE,QAAA,OAAO,WAAW;AAChC,IAAA,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC;AAC5B,IAAA,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC;AAChC,IAAA,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC;AAClC,IAAA,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,CAAC;AAC9B,IAAA,IAAI,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC;AAAE,QAAA,OAAO,WAAW;IAC9E,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE;AACrC;AAEA;AACA,MAAM,aAAa,GAAGC,mBAAa,CAAc,aAAa,CAAC;AAE/D;;;;;;AAMG;SACa,SAAS,GAAA;AACvB,IAAA,OAAOC,gBAAU,CAAC,aAAa,CAAC;AAClC;AAEA;;;;;;;;;;;;AAYG;SACa,KAAK,GAAA;AACnB,IAAA,OAAOA,gBAAU,CAAC,aAAa,CAAC;AAClC;AAEA;;;;;;;;;;;AAWG;AACG,SAAU,cAAc,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAuB,EAAA;AACjG,IAAA,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC;IAC1C,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAGC,+BAAmB,EAAE;AAClD,IAAA,MAAM,KAAK,GAAGC,aAAO,CAAc,MAAK;QACtCC,oBAAU,CAAC,MAAM,CAAC;QAClB,OAAO;YACL,MAAM;YACN,MAAM,EAAE,MAAM,IAAI,YAAY;AAC9B,YAAA,MAAM,EAAE,UAAU;YAClB,SAAS;YACT,SAAS;AACT,YAAA,WAAW,EAAE,KAAK;AAClB,YAAA,gBAAgB,EAAEC,6BAAmB,CAAC,KAAK,CAAC;SAC7C;AACH,IAAA,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IAC7D,OAAOC,cAAA,CAAC,aAAa,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,KAAK,EAAA,QAAA,EAAG,QAAQ,EAAA,CAA0B;AAClF;;;;;;"}
1
+ {"version":3,"file":"rnwind-provider.cjs","sources":["../../../../../src/runtime/components/rnwind-provider.tsx"],"sourcesContent":["import { createContext, useContext, useMemo, type ReactNode } from 'react'\nimport { useWindowDimensions } from 'react-native'\nimport type { ThemeTables } from '../../core/types'\nimport type { Scheme } from '../types'\nimport type { OnHaptics } from '../../core/parser/haptics'\nimport { activeBreakpointFor, BASE_BREAKPOINT, loadScheme } from '../lookup-css'\n\n/**\n * Per-render safe-area insets snapshot. Bridge from any source\n * (`useSafeAreaInsets()`, expo-router insets, a manually computed\n * value) into the {@link RnwindProvider} — rnwind stays\n * library-agnostic.\n */\nexport type Insets = Readonly<{\n top: number\n right: number\n bottom: number\n left: number\n}>\n\n/**\n * Single value carried by the rnwind context. Every piece of runtime\n * state rnwind needs — scheme, theme tables, insets, font scale,\n * window width, active responsive breakpoint, optional haptic\n * dispatcher — lives on this one bag. Consumers read it via\n * {@link useRnwind} and either destructure or forward straight to\n * `lookupCss` / `useCss`.\n *\n * `activeBreakpoint` is the highest-threshold registered breakpoint\n * whose min-width is `<= windowWidth`, or `'base'` when below the\n * smallest one (mobile-first tier) or when no breakpoints are\n * registered yet (tests, bundles without rnwind-transformed sources).\n * Always a string — never null. Reactive: it updates with\n * `useWindowDimensions().width`, so consumers can branch on it\n * without a separate hook.\n */\nexport type RnwindState = Readonly<{\n scheme: Scheme\n tables: ThemeTables\n insets: Insets\n onHaptics: OnHaptics | undefined\n fontScale: number\n windowWidth: number\n activeBreakpoint: string\n}>\n\n/** Props accepted by {@link RnwindProvider}. */\nexport type RnwindProviderProps = Readonly<{\n scheme: Scheme\n tables?: ThemeTables\n insets?: Partial<Insets>\n onHaptics?: OnHaptics\n children?: ReactNode\n}>\n\nconst EMPTY_TABLES: ThemeTables = {}\nconst ZERO_INSETS: Insets = { top: 0, right: 0, bottom: 0, left: 0 }\nconst DEFAULT_STATE: RnwindState = {\n scheme: 'light' as Scheme,\n tables: EMPTY_TABLES,\n insets: ZERO_INSETS,\n onHaptics: undefined,\n fontScale: 1,\n windowWidth: 0,\n activeBreakpoint: BASE_BREAKPOINT,\n}\n\n/**\n * Normalise a `Partial<Insets>` into a complete {@link Insets}, returning\n * the shared {@link ZERO_INSETS} reference when nothing is supplied so\n * downstream memoisation stays stable.\n * @param partial Caller-supplied insets (or undefined).\n * @returns Complete insets record.\n */\nfunction normaliseInsets(partial: Partial<Insets> | undefined): Insets {\n if (!partial) return ZERO_INSETS\n const top = partial.top ?? 0\n const right = partial.right ?? 0\n const bottom = partial.bottom ?? 0\n const left = partial.left ?? 0\n if (top === 0 && right === 0 && bottom === 0 && left === 0) return ZERO_INSETS\n return { top, right, bottom, left }\n}\n\n/** Single internal context the runtime reads from. */\nconst RnwindContext = createContext<RnwindState>(DEFAULT_STATE)\n\n/**\n * Read rnwind's full runtime state — scheme, theme tables, insets,\n * fontScale, windowWidth, onHaptics — in one go. Pass the returned\n * value straight to `lookupCss` / `useCss`, or destructure what you\n * need.\n * @returns Active rnwind state under the nearest {@link RnwindProvider}.\n */\nexport function useRnwind(): RnwindState {\n return useContext(RnwindContext)\n}\n\n/**\n * Provider for rnwind's full runtime state. fontScale + windowWidth\n * come from `useWindowDimensions()` so they react to OS-level\n * orientation / accessibility-text-size changes automatically.\n * @param props Provider props.\n * @param props.scheme Active scheme name.\n * @param props.tables Optional pre-resolved token tables.\n * @param props.insets Optional safe-area insets.\n * @param props.onHaptics Optional haptic dispatcher.\n * @param props.children React subtree.\n * @returns Provider element.\n */\nexport function RnwindProvider({ scheme, tables, insets, onHaptics, children }: RnwindProviderProps): ReactNode {\n const normalized = normaliseInsets(insets)\n const { fontScale, width } = useWindowDimensions()\n const value = useMemo<RnwindState>(() => {\n loadScheme(scheme)\n return {\n scheme,\n tables: tables ?? EMPTY_TABLES,\n insets: normalized,\n onHaptics,\n fontScale,\n windowWidth: width,\n activeBreakpoint: activeBreakpointFor(width),\n }\n }, [scheme, tables, normalized, onHaptics, fontScale, width])\n return <RnwindContext.Provider value={value}>{children}</RnwindContext.Provider>\n}\n"],"names":["BASE_BREAKPOINT","createContext","useContext","useWindowDimensions","useMemo","loadScheme","activeBreakpointFor","_jsx"],"mappings":";;;;;;;AAuDA,MAAM,YAAY,GAAgB,EAAE;AACpC,MAAM,WAAW,GAAW,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE;AACpE,MAAM,aAAa,GAAgB;AACjC,IAAA,MAAM,EAAE,OAAiB;AACzB,IAAA,MAAM,EAAE,YAAY;AACpB,IAAA,MAAM,EAAE,WAAW;AACnB,IAAA,SAAS,EAAE,SAAS;AACpB,IAAA,SAAS,EAAE,CAAC;AACZ,IAAA,WAAW,EAAE,CAAC;AACd,IAAA,gBAAgB,EAAEA,yBAAe;CAClC;AAED;;;;;;AAMG;AACH,SAAS,eAAe,CAAC,OAAoC,EAAA;AAC3D,IAAA,IAAI,CAAC,OAAO;AAAE,QAAA,OAAO,WAAW;AAChC,IAAA,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC;AAC5B,IAAA,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC;AAChC,IAAA,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC;AAClC,IAAA,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,CAAC;AAC9B,IAAA,IAAI,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC;AAAE,QAAA,OAAO,WAAW;IAC9E,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE;AACrC;AAEA;AACA,MAAM,aAAa,GAAGC,mBAAa,CAAc,aAAa,CAAC;AAE/D;;;;;;AAMG;SACa,SAAS,GAAA;AACvB,IAAA,OAAOC,gBAAU,CAAC,aAAa,CAAC;AAClC;AAEA;;;;;;;;;;;AAWG;AACG,SAAU,cAAc,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAuB,EAAA;AACjG,IAAA,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC;IAC1C,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAGC,+BAAmB,EAAE;AAClD,IAAA,MAAM,KAAK,GAAGC,aAAO,CAAc,MAAK;QACtCC,oBAAU,CAAC,MAAM,CAAC;QAClB,OAAO;YACL,MAAM;YACN,MAAM,EAAE,MAAM,IAAI,YAAY;AAC9B,YAAA,MAAM,EAAE,UAAU;YAClB,SAAS;YACT,SAAS;AACT,YAAA,WAAW,EAAE,KAAK;AAClB,YAAA,gBAAgB,EAAEC,6BAAmB,CAAC,KAAK,CAAC;SAC7C;AACH,IAAA,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IAC7D,OAAOC,cAAA,CAAC,aAAa,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,KAAK,EAAA,QAAA,EAAG,QAAQ,EAAA,CAA0B;AAClF;;;;;"}
@@ -55,20 +55,6 @@ export type RnwindProviderProps = Readonly<{
55
55
  * @returns Active rnwind state under the nearest {@link RnwindProvider}.
56
56
  */
57
57
  export declare function useRnwind(): RnwindState;
58
- /**
59
- * Internal context hook the babel transformer injects at the top of
60
- * every rewritten component as `const _t = useR_()`. Same body as the
61
- * public {@link useRnwind} — exposed under a `use*`-prefixed name so
62
- * react-refresh's babel plugin (which only tracks call-sites whose
63
- * identifier matches `^use[A-Z]`) folds it into each component's
64
- * fast-refresh signature. Without that prefix the signature stayed
65
- * stable across transformer changes; HMR then preserved fiber state
66
- * while the rendered hook list shifted, surfacing as "change in the
67
- * order of Hooks" runtime errors. Trailing underscore keeps it
68
- * visually distinct from the user-facing `useRnwind`.
69
- * @returns Active rnwind state.
70
- */
71
- export declare function useR_(): RnwindState;
72
58
  /**
73
59
  * Provider for rnwind's full runtime state. fontScale + windowWidth
74
60
  * come from `useWindowDimensions()` so they react to OS-level
@@ -1,20 +1,26 @@
1
1
  'use strict';
2
2
 
3
3
  var rnwindProvider = require('../components/rnwind-provider.cjs');
4
- var lookupCss = require('../lookup-css.cjs');
4
+ var resolve = require('../resolve.cjs');
5
5
 
6
6
  /**
7
- * Convenience hook: `useRnwind()` + `lookupCss()` rolled into one. Use
8
- * inside any component that wants the resolved style array without
9
- * threading the rnwind context manually. JSX-heavy components should
10
- * still call `useRnwind()` once and pass it to `lookupCss(...)` per
11
- * element so React only does a single context read per render.
12
- * @param className Raw className string or transformer-hoisted atom-name array.
13
- * @param userStyle Optional caller-supplied style appended last.
14
- * @returns Frozen style array for React Native's `style` prop.
7
+ * Resolve a className to a React Native `style` value against the active
8
+ * rnwind context (scheme, insets, fontScale, breakpoint). Molecule-fast:
9
+ * a literal className the scanner saw returns a pre-merged object by
10
+ * reference; anything else falls back to per-atom resolution. The escape
11
+ * hatch for custom components that hold a `className` prop:
12
+ *
13
+ * ```tsx
14
+ * function Card({ className, style, ...rest }) {
15
+ * return <RNView style={useCss(className, style)} {...rest} />
16
+ * }
17
+ * ```
18
+ * @param className Raw className string.
19
+ * @param userStyle Optional caller-supplied style appended last (wins).
20
+ * @returns RN `style` value (a single object or an array).
15
21
  */
16
22
  function useCss(className, userStyle) {
17
- return lookupCss.lookupCss(className, rnwindProvider.useRnwind(), userStyle);
23
+ return resolve.resolve(className, rnwindProvider.useRnwind(), userStyle).style;
18
24
  }
19
25
 
20
26
  exports.useCss = useCss;
@@ -1 +1 @@
1
- {"version":3,"file":"use-css.cjs","sources":["../../../../../src/runtime/hooks/use-css.ts"],"sourcesContent":["import { useRnwind } from '../components/rnwind-provider'\nimport { lookupCss } from '../lookup-css'\n\n/**\n * Convenience hook: `useRnwind()` + `lookupCss()` rolled into one. Use\n * inside any component that wants the resolved style array without\n * threading the rnwind context manually. JSX-heavy components should\n * still call `useRnwind()` once and pass it to `lookupCss(...)` per\n * element so React only does a single context read per render.\n * @param className Raw className string or transformer-hoisted atom-name array.\n * @param userStyle Optional caller-supplied style appended last.\n * @returns Frozen style array for React Native's `style` prop.\n */\nexport function useCss(className?: string | readonly string[] | null, userStyle?: unknown): readonly unknown[] {\n return lookupCss(className, useRnwind(), userStyle)\n}\n"],"names":["lookupCss","useRnwind"],"mappings":";;;;;AAGA;;;;;;;;;AASG;AACG,SAAU,MAAM,CAAC,SAA6C,EAAE,SAAmB,EAAA;IACvF,OAAOA,mBAAS,CAAC,SAAS,EAAEC,wBAAS,EAAE,EAAE,SAAS,CAAC;AACrD;;;;"}
1
+ {"version":3,"file":"use-css.cjs","sources":["../../../../../src/runtime/hooks/use-css.ts"],"sourcesContent":["import { useRnwind } from '../components/rnwind-provider'\nimport { resolve } from '../resolve'\n\n/**\n * Resolve a className to a React Native `style` value against the active\n * rnwind context (scheme, insets, fontScale, breakpoint). Molecule-fast:\n * a literal className the scanner saw returns a pre-merged object by\n * reference; anything else falls back to per-atom resolution. The escape\n * hatch for custom components that hold a `className` prop:\n *\n * ```tsx\n * function Card({ className, style, ...rest }) {\n * return <RNView style={useCss(className, style)} {...rest} />\n * }\n * ```\n * @param className Raw className string.\n * @param userStyle Optional caller-supplied style appended last (wins).\n * @returns RN `style` value (a single object or an array).\n */\nexport function useCss(className?: string | null, userStyle?: unknown): unknown {\n return resolve(className, useRnwind(), userStyle).style\n}\n"],"names":["resolve","useRnwind"],"mappings":";;;;;AAGA;;;;;;;;;;;;;;;AAeG;AACG,SAAU,MAAM,CAAC,SAAyB,EAAE,SAAmB,EAAA;IACnE,OAAOA,eAAO,CAAC,SAAS,EAAEC,wBAAS,EAAE,EAAE,SAAS,CAAC,CAAC,KAAK;AACzD;;;;"}
@@ -1,11 +1,17 @@
1
1
  /**
2
- * Convenience hook: `useRnwind()` + `lookupCss()` rolled into one. Use
3
- * inside any component that wants the resolved style array without
4
- * threading the rnwind context manually. JSX-heavy components should
5
- * still call `useRnwind()` once and pass it to `lookupCss(...)` per
6
- * element so React only does a single context read per render.
7
- * @param className Raw className string or transformer-hoisted atom-name array.
8
- * @param userStyle Optional caller-supplied style appended last.
9
- * @returns Frozen style array for React Native's `style` prop.
2
+ * Resolve a className to a React Native `style` value against the active
3
+ * rnwind context (scheme, insets, fontScale, breakpoint). Molecule-fast:
4
+ * a literal className the scanner saw returns a pre-merged object by
5
+ * reference; anything else falls back to per-atom resolution. The escape
6
+ * hatch for custom components that hold a `className` prop:
7
+ *
8
+ * ```tsx
9
+ * function Card({ className, style, ...rest }) {
10
+ * return <RNView style={useCss(className, style)} {...rest} />
11
+ * }
12
+ * ```
13
+ * @param className Raw className string.
14
+ * @param userStyle Optional caller-supplied style appended last (wins).
15
+ * @returns RN `style` value (a single object or an array).
10
16
  */
11
- export declare function useCss(className?: string | readonly string[] | null, userStyle?: unknown): readonly unknown[];
17
+ export declare function useCss(className?: string | null, userStyle?: unknown): unknown;