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
@@ -47,8 +47,13 @@ export type AtomSerializedCache = Map<string, AtomSerializedEntry>;
47
47
  * manifest emits `registerBreakpoints({...})` so the runtime can gate
48
48
  * `md:*` / `lg:*` atoms on `windowWidth`. Optional — empty when the
49
49
  * theme declares no breakpoints (legacy/test callers).
50
+ * @param gradients Gradient feature map (atom → role/colour) for the manifest + molecule eligibility.
51
+ * @param haptics Haptic feature map (atom → request) for the manifest + molecule eligibility.
52
+ * @param literals Distinct literal className strings — pre-merged into
53
+ * per-scheme molecules so the runtime resolver's O(1) molecule-first
54
+ * path is populated. Empty for legacy/test callers (atom path only).
50
55
  * @returns Per-scheme sources, manifest source, variant list.
51
56
  */
52
- export declare function buildSchemeSources(atomNames: readonly string[], resolved: ReadonlyMap<string, SchemedStyle>, keyframes: ReadonlyMap<string, KeyframeBlock>, cache?: AtomSerializedCache, breakpoints?: ReadonlyMap<string, number>): BuildSchemeSourcesOutput;
57
+ export declare function buildSchemeSources(atomNames: readonly string[], resolved: ReadonlyMap<string, SchemedStyle>, keyframes: ReadonlyMap<string, KeyframeBlock>, cache?: AtomSerializedCache, breakpoints?: ReadonlyMap<string, number>, gradients?: ReadonlyMap<string, unknown>, haptics?: ReadonlyMap<string, unknown>, literals?: readonly string[]): BuildSchemeSourcesOutput;
53
58
  /** Registry key the runtime uses for the always-loaded fallback. */
54
59
  export declare const COMMON_SCHEME_NAME: string;
@@ -92,6 +92,17 @@ class UnionBuilder {
92
92
  parser;
93
93
  unionAtoms = new Map();
94
94
  unionKeyframes = new Map();
95
+ /** atom name → gradient role/colour, surfaced into the manifest's `registerGradients`. */
96
+ unionGradients = new Map();
97
+ /** atom name → haptic request, surfaced into the manifest's `registerHaptics`. */
98
+ unionHaptics = new Map();
99
+ /**
100
+ * Distinct literal className strings seen across all files, pre-merged
101
+ * into per-scheme molecules at write time. Accumulate-only (like
102
+ * `unionAtoms`): orphaned literals just yield unused molecules and get
103
+ * reaped on the next cold start, so no refcount is needed.
104
+ */
105
+ unionLiterals = new Set();
95
106
  /**
96
107
  * Responsive breakpoints captured from the parser. Refreshed on every
97
108
  * `recordFile` / `ensureProjectScanned` so user-defined
@@ -169,6 +180,10 @@ class UnionBuilder {
169
180
  this.unionAtoms.set(name, style);
170
181
  for (const [name, kf] of parsed.keyframes)
171
182
  this.unionKeyframes.set(name, kf);
183
+ for (const [name, gradient] of parsed.gradientAtoms)
184
+ this.unionGradients.set(name, gradient);
185
+ for (const [name, haptic] of parsed.hapticAtoms)
186
+ this.unionHaptics.set(name, haptic);
172
187
  this.breakpoints = parsed.breakpoints;
173
188
  this.projectScanned = true;
174
189
  })();
@@ -187,12 +202,14 @@ class UnionBuilder {
187
202
  * @param file Absolute source file path.
188
203
  * @param atoms Per-atom resolved schemed styles from this transform.
189
204
  * @param keyframes Keyframe blocks referenced by this file's atoms.
205
+ * @param literals
190
206
  * @returns `{ changed: true }` when the union shifted (new atom name,
191
207
  * removed atom name, or new keyframe) — the transformer uses this
192
208
  * to skip the serializer + `writeSchemes` when nothing changed.
193
209
  */
194
- async recordFile(file, atoms, keyframes) {
210
+ async recordFile(file, atoms, keyframes, literals = []) {
195
211
  await this.ensureProjectScanned();
212
+ const literalAdded = this.recordLiterals(literals);
196
213
  const newAtomNames = new Set(atoms.keys());
197
214
  const previous = this.fileAtomSets.get(file);
198
215
  if (previous && setsEqual(previous, newAtomNames)) {
@@ -208,11 +225,28 @@ class UnionBuilder {
208
225
  keyframeAdded = true;
209
226
  this.unionKeyframes.set(name, kf);
210
227
  }
211
- return { changed: keyframeAdded };
228
+ return { changed: keyframeAdded || literalAdded };
212
229
  }
213
230
  this.applyDiff(file, newAtomNames, atoms, keyframes);
214
231
  return { changed: true };
215
232
  }
233
+ /**
234
+ * Merge a file's literal classNames into the union. A literal the
235
+ * union hasn't seen flips `changed` so `writeSchemes` re-emits the
236
+ * scheme files with the new molecule.
237
+ * @param literals Distinct literal className strings.
238
+ * @returns Whether any literal was new to the union.
239
+ */
240
+ recordLiterals(literals) {
241
+ let added = false;
242
+ for (const literal of literals) {
243
+ if (this.unionLiterals.has(literal))
244
+ continue;
245
+ this.unionLiterals.add(literal);
246
+ added = true;
247
+ }
248
+ return added;
249
+ }
216
250
  /**
217
251
  * Forget one source file's contribution. Idempotent — repeated calls
218
252
  * for a file that's already dropped are no-ops. Does NOT remove the
@@ -244,7 +278,7 @@ class UnionBuilder {
244
278
  async writeSchemes() {
245
279
  await this.ensureProjectScanned();
246
280
  const sortedAtomNames = [...this.unionAtoms.keys()].toSorted((a, b) => a.localeCompare(b));
247
- const result = buildStyle.buildSchemeSources(sortedAtomNames, this.unionAtoms, this.unionKeyframes, this.serializedCache, this.breakpoints);
281
+ const result = buildStyle.buildSchemeSources(sortedAtomNames, this.unionAtoms, this.unionKeyframes, this.serializedCache, this.breakpoints, this.unionGradients, this.unionHaptics, [...this.unionLiterals]);
248
282
  this.serializedMissesCount += result.serializedMisses;
249
283
  const { schemeSources, manifestSource } = result;
250
284
  const changed = [];
@@ -1 +1 @@
1
- {"version":3,"file":"union-builder.cjs","sources":["../../../../../src/core/style-builder/union-builder.ts"],"sourcesContent":["import { createHash, randomBytes } from 'node:crypto'\nimport { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'\nimport path from 'node:path'\nimport type { KeyframeBlock, SchemedStyle, TailwindParser } from '../parser'\nimport { buildSchemeSources, type AtomSerializedCache } from './build-style'\n\n/** Manifest module basename — the file SchemeProvider imports via the resolver. */\nconst MANIFEST_BASENAME = 'schemes.js'\n\n/**\n * Atomic file write — stage to a `.tmp.<pid>.<nonce>` sibling, then\n * `rename()` into place. Skips the write entirely when the existing\n * content matches.\n * @param target Final destination path.\n * @param content Bytes to write.\n * @returns Whether the file was actually rewritten.\n */\nfunction writeIfChanged(target: string, content: string): boolean {\n if (existsSync(target)) {\n try {\n if (readFileSync(target, 'utf8') === content) return false\n } catch {\n // Unreadable — fall through to rewrite.\n }\n }\n mkdirSync(path.dirname(target), { recursive: true })\n const temporary = `${target}.${process.pid}.${randomBytes(4).toString('hex')}.tmp`\n try {\n writeFileSync(temporary, content, 'utf8')\n renameSync(temporary, target)\n return true\n } catch (error) {\n rmSync(temporary, { force: true })\n throw error\n }\n}\n\n/**\n * SHA-256 prefix of a string — cheap signature used to detect whether a\n * per-scheme file's source has changed since the last write.\n * @param text Input text.\n * @returns 16-char hex digest.\n */\nfunction signatureOf(text: string): string {\n return createHash('sha256').update(text).digest('hex').slice(0, 16)\n}\n\n/**\n * Compare two `Set<string>`s for equality — same size + every element\n * of `a` present in `b`.\n * @param a First set.\n * @param b Second set.\n * @returns Whether the two sets contain identical values.\n */\nfunction setsEqual(a: ReadonlySet<string>, b: ReadonlySet<string>): boolean {\n if (a.size !== b.size) return false\n for (const v of a) if (!b.has(v)) return false\n return true\n}\n\n/**\n * Compute the absolute path of a per-scheme style file under the cache dir.\n * @param cacheDir Absolute cache directory.\n * @param scheme Registry key (`'common'` or a variant name).\n * @returns Absolute path, e.g. `<cacheDir>/dark.style.js`.\n */\nfunction schemeFilePath(cacheDir: string, scheme: string): string {\n return path.join(cacheDir, `${scheme}.style.js`)\n}\n\n/**\n * In-memory atom union + per-scheme style-file emitter.\n *\n * Correctness under multi-worker Metro relies on `ensureProjectScanned`:\n * the FIRST `recordFile` / `writeSchemes` call in every worker drives\n * the oxide Scanner across ALL project sources and hydrates the union\n * with the complete set of candidates. Subsequent per-file\n * `recordFile` calls only layer in atoms the scan already knew about,\n * so writes are idempotent — different workers can't clobber each\n * other's scheme files with partial views.\n *\n * Per-file deltas (atom set unchanged → early return) skip\n * serialization entirely. On a theme-CSS change, `getRnwindState`\n * builds a fresh parser + builder; the next call re-runs\n * `ensureProjectScanned` against the new parser, producing scheme\n * files with the new theme values.\n */\nclass UnionBuilder {\n private readonly cacheDir: string\n private readonly parser: TailwindParser\n private readonly unionAtoms = new Map<string, SchemedStyle>()\n private readonly unionKeyframes = new Map<string, KeyframeBlock>()\n /**\n * Responsive breakpoints captured from the parser. Refreshed on every\n * `recordFile` / `ensureProjectScanned` so user-defined\n * `--breakpoint-*` overrides land in the manifest the next time it's\n * written. Identical for every parser call within one parser instance\n * (theme is fixed for the parser's lifetime), so storing the latest\n * snapshot is sufficient.\n */\n private breakpoints: ReadonlyMap<string, number> = new Map()\n /** file → set of atom names this file currently contributes. */\n private readonly fileAtomSets = new Map<string, Set<string>>()\n /** atom name → how many files currently contribute it (refcount). */\n private readonly atomRefCount = new Map<string, number>()\n /** scheme → last-written source SHA. Skips re-writing unchanged schemes. */\n private readonly schemeSignatures = new Map<string, string>()\n /**\n * Per-atom serialized-value cache — identity-keyed on each atom's\n * SchemedStyle reference. Carried across `writeSchemes` calls so the\n * typical \"user added one className\" FR case re-stringifies ONE atom\n * instead of all 175+. Cleared on `ensureProjectScanned` (full\n * rescan replaces every reference) and individually invalidated for\n * any atom `applyDiff` mutates.\n */\n private readonly serializedCache: AtomSerializedCache = new Map()\n /** Running count of stringify passes (cache misses). Test telemetry. */\n private serializedMissesCount = 0\n /** Set after `ensureProjectScanned` completes. */\n private projectScanned = false\n /** Promise guard so concurrent first-calls await ONE scan. */\n private pendingScan: Promise<void> | null = null\n\n constructor(cacheDir: string, parser: TailwindParser) {\n this.cacheDir = cacheDir\n this.parser = parser\n mkdirSync(this.cacheDir, { recursive: true })\n }\n\n /** Absolute path of the manifest module (`rnwind/__generated/schemes`). */\n public get manifestPath(): string {\n return path.join(this.cacheDir, MANIFEST_BASENAME)\n }\n\n /**\n * Snapshot of every source file the builder has recorded atoms for\n * this worker session. Used by `withRnwindConfig`'s CSS watcher to\n * touch `mtime` on each and nudge Metro into re-transforming them\n * with the new theme values.\n * @returns Absolute source paths.\n */\n public recordedFiles(): readonly string[] {\n return [...this.fileAtomSets.keys()]\n }\n\n /** Cumulative cache-miss count — exposed for tests to assert cache behaviour. */\n public get serializedMisses(): number {\n return this.serializedMissesCount\n }\n\n /**\n * Absolute path of one scheme's style file.\n * @param scheme Registry key.\n * @returns Absolute path.\n */\n public schemePath(scheme: string): string {\n return schemeFilePath(this.cacheDir, scheme)\n }\n\n /**\n * One-shot oxide scan + compile across every source the parser was\n * configured with. Idempotent — safe to call from any entry point.\n * Concurrent callers share the same in-flight promise.\n */\n public async ensureProjectScanned(): Promise<void> {\n if (this.projectScanned) return\n if (this.pendingScan) return this.pendingScan\n this.pendingScan = (async () => {\n const parsed = await this.parser.parseProject()\n for (const [name, style] of parsed.atoms) this.unionAtoms.set(name, style)\n for (const [name, kf] of parsed.keyframes) this.unionKeyframes.set(name, kf)\n this.breakpoints = parsed.breakpoints\n this.projectScanned = true\n })()\n try {\n await this.pendingScan\n } finally {\n this.pendingScan = null\n }\n }\n\n /**\n * Record one source file's resolved atoms + keyframes. Short-circuits\n * when the file's atom name set hasn't changed — the common case on\n * every Fast Refresh save of a file whose className literals are\n * unchanged.\n * @param file Absolute source file path.\n * @param atoms Per-atom resolved schemed styles from this transform.\n * @param keyframes Keyframe blocks referenced by this file's atoms.\n * @returns `{ changed: true }` when the union shifted (new atom name,\n * removed atom name, or new keyframe) — the transformer uses this\n * to skip the serializer + `writeSchemes` when nothing changed.\n */\n public async recordFile(\n file: string,\n atoms: ReadonlyMap<string, SchemedStyle>,\n keyframes: ReadonlyMap<string, KeyframeBlock>,\n ): Promise<{ changed: boolean }> {\n await this.ensureProjectScanned()\n const newAtomNames = new Set(atoms.keys())\n const previous = this.fileAtomSets.get(file)\n if (previous && setsEqual(previous, newAtomNames)) {\n // Atom set unchanged — skip the unionAtoms update entirely. The\n // project scan already populated them, and re-setting a fresh\n // object ref here would invalidate the per-atom serialization\n // cache on every FR save for no gain (values are identical).\n // Theme edits go through `getRnwindState` → new builder → fresh\n // scan, so stale cache is impossible.\n let keyframeAdded = false\n for (const [name, kf] of keyframes) {\n if (!this.unionKeyframes.has(name)) keyframeAdded = true\n this.unionKeyframes.set(name, kf)\n }\n return { changed: keyframeAdded }\n }\n this.applyDiff(file, newAtomNames, atoms, keyframes)\n return { changed: true }\n }\n\n /**\n * Forget one source file's contribution. Idempotent — repeated calls\n * for a file that's already dropped are no-ops. Does NOT remove the\n * atom from the union when another file (or the project scan) still\n * references it.\n * @param file Absolute source file path.\n */\n public dropFile(file: string): void {\n const previous = this.fileAtomSets.get(file)\n if (!previous) return\n for (const name of previous) {\n const count = (this.atomRefCount.get(name) ?? 0) - 1\n if (count <= 0) this.atomRefCount.delete(name)\n else this.atomRefCount.set(name, count)\n }\n this.fileAtomSets.delete(file)\n }\n\n /**\n * Serialize the union into per-scheme files + manifest, writing only\n * files whose source bytes changed. Called after every `recordFile`\n * from the transformer — and once at Metro startup via\n * `ensureFilesExist` to seed disk from the project scan alone.\n * @returns List of scheme keys whose files were rewritten (empty\n * when the union is byte-identical to the last flush).\n */\n public async writeSchemes(): Promise<{ changedSchemes: readonly string[] }> {\n await this.ensureProjectScanned()\n const sortedAtomNames = [...this.unionAtoms.keys()].toSorted((a, b) => a.localeCompare(b))\n const result = buildSchemeSources(sortedAtomNames, this.unionAtoms, this.unionKeyframes, this.serializedCache, this.breakpoints)\n this.serializedMissesCount += result.serializedMisses\n const { schemeSources, manifestSource } = result\n\n const changed: string[] = []\n for (const [scheme, source] of Object.entries(schemeSources)) {\n const signature = signatureOf(source)\n const target = schemeFilePath(this.cacheDir, scheme)\n if (this.schemeSignatures.get(scheme) === signature && existsSync(target)) continue\n if (writeIfChanged(target, source)) changed.push(scheme)\n this.schemeSignatures.set(scheme, signature)\n }\n\n const manifestSignature = signatureOf(manifestSource)\n const manifestTarget = path.join(this.cacheDir, MANIFEST_BASENAME)\n if (this.schemeSignatures.get('__manifest') !== manifestSignature || !existsSync(manifestTarget)) {\n if (writeIfChanged(manifestTarget, manifestSource)) changed.push('__manifest')\n this.schemeSignatures.set('__manifest', manifestSignature)\n }\n\n return { changedSchemes: changed }\n }\n\n /**\n * Ensure the manifest + common scheme files exist on disk so Metro's\n * resolver can SHA1 them at boot before the first transform runs.\n */\n public async ensureFilesExist(): Promise<void> {\n if (existsSync(this.manifestPath) && existsSync(schemeFilePath(this.cacheDir, 'common'))) {\n // Still trigger the scan so the in-memory union is complete; file\n // bytes may already be authoritative from a prior Metro run.\n await this.ensureProjectScanned()\n return\n }\n await this.writeSchemes()\n }\n\n /**\n * Apply one file's atom-name diff to the in-memory refcount + union.\n * @param file Source file path.\n * @param newAtoms New atom-name set for the file.\n * @param resolvedAtoms Fresh parser output — carries the resolved\n * styles for every entry in `newAtoms`.\n * @param newKeyframes Keyframes this file's atoms reference.\n */\n private applyDiff(\n file: string,\n newAtoms: ReadonlySet<string>,\n resolvedAtoms: ReadonlyMap<string, SchemedStyle>,\n newKeyframes: ReadonlyMap<string, KeyframeBlock>,\n ): void {\n const previous = this.fileAtomSets.get(file) ?? new Set<string>()\n for (const name of previous) {\n if (newAtoms.has(name)) continue\n const count = (this.atomRefCount.get(name) ?? 0) - 1\n if (count <= 0) this.atomRefCount.delete(name)\n else this.atomRefCount.set(name, count)\n // Do NOT remove `name` from `unionAtoms` — the project scan still\n // references it (orphaned atoms get reaped on the next Metro\n // cold start when the scanner re-walks disk).\n }\n for (const name of newAtoms) {\n if (!previous.has(name)) this.atomRefCount.set(name, (this.atomRefCount.get(name) ?? 0) + 1)\n // Only install the resolved style when the atom is new to the\n // union. Replacing an existing entry with a fresh parser-\n // produced object would swap the identity guard the per-atom\n // serialization cache uses and force a re-stringify for every\n // atom on every FR save. CSS edits rebuild the whole builder\n // (via `getRnwindState`) so stale values aren't possible.\n if (!this.unionAtoms.has(name)) {\n const style = resolvedAtoms.get(name)\n if (style) this.unionAtoms.set(name, style)\n }\n }\n this.fileAtomSets.set(file, new Set(newAtoms))\n for (const [name, kf] of newKeyframes) this.unionKeyframes.set(name, kf)\n }\n}\n\nexport { UnionBuilder }\n"],"names":["existsSync","readFileSync","mkdirSync","randomBytes","writeFileSync","renameSync","rmSync","createHash","buildSchemeSources"],"mappings":"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"}
1
+ {"version":3,"file":"union-builder.cjs","sources":["../../../../../src/core/style-builder/union-builder.ts"],"sourcesContent":["import { createHash, randomBytes } from 'node:crypto'\nimport { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'\nimport path from 'node:path'\nimport type { GradientAtomInfo, HapticRequest, KeyframeBlock, SchemedStyle, TailwindParser } from '../parser'\nimport { buildSchemeSources, type AtomSerializedCache } from './build-style'\n\n/** Manifest module basename — the file SchemeProvider imports via the resolver. */\nconst MANIFEST_BASENAME = 'schemes.js'\n\n/**\n * Atomic file write — stage to a `.tmp.<pid>.<nonce>` sibling, then\n * `rename()` into place. Skips the write entirely when the existing\n * content matches.\n * @param target Final destination path.\n * @param content Bytes to write.\n * @returns Whether the file was actually rewritten.\n */\nfunction writeIfChanged(target: string, content: string): boolean {\n if (existsSync(target)) {\n try {\n if (readFileSync(target, 'utf8') === content) return false\n } catch {\n // Unreadable — fall through to rewrite.\n }\n }\n mkdirSync(path.dirname(target), { recursive: true })\n const temporary = `${target}.${process.pid}.${randomBytes(4).toString('hex')}.tmp`\n try {\n writeFileSync(temporary, content, 'utf8')\n renameSync(temporary, target)\n return true\n } catch (error) {\n rmSync(temporary, { force: true })\n throw error\n }\n}\n\n/**\n * SHA-256 prefix of a string — cheap signature used to detect whether a\n * per-scheme file's source has changed since the last write.\n * @param text Input text.\n * @returns 16-char hex digest.\n */\nfunction signatureOf(text: string): string {\n return createHash('sha256').update(text).digest('hex').slice(0, 16)\n}\n\n/**\n * Compare two `Set<string>`s for equality — same size + every element\n * of `a` present in `b`.\n * @param a First set.\n * @param b Second set.\n * @returns Whether the two sets contain identical values.\n */\nfunction setsEqual(a: ReadonlySet<string>, b: ReadonlySet<string>): boolean {\n if (a.size !== b.size) return false\n for (const v of a) if (!b.has(v)) return false\n return true\n}\n\n/**\n * Compute the absolute path of a per-scheme style file under the cache dir.\n * @param cacheDir Absolute cache directory.\n * @param scheme Registry key (`'common'` or a variant name).\n * @returns Absolute path, e.g. `<cacheDir>/dark.style.js`.\n */\nfunction schemeFilePath(cacheDir: string, scheme: string): string {\n return path.join(cacheDir, `${scheme}.style.js`)\n}\n\n/**\n * In-memory atom union + per-scheme style-file emitter.\n *\n * Correctness under multi-worker Metro relies on `ensureProjectScanned`:\n * the FIRST `recordFile` / `writeSchemes` call in every worker drives\n * the oxide Scanner across ALL project sources and hydrates the union\n * with the complete set of candidates. Subsequent per-file\n * `recordFile` calls only layer in atoms the scan already knew about,\n * so writes are idempotent — different workers can't clobber each\n * other's scheme files with partial views.\n *\n * Per-file deltas (atom set unchanged → early return) skip\n * serialization entirely. On a theme-CSS change, `getRnwindState`\n * builds a fresh parser + builder; the next call re-runs\n * `ensureProjectScanned` against the new parser, producing scheme\n * files with the new theme values.\n */\nclass UnionBuilder {\n private readonly cacheDir: string\n private readonly parser: TailwindParser\n private readonly unionAtoms = new Map<string, SchemedStyle>()\n private readonly unionKeyframes = new Map<string, KeyframeBlock>()\n /** atom name → gradient role/colour, surfaced into the manifest's `registerGradients`. */\n private readonly unionGradients = new Map<string, GradientAtomInfo>()\n /** atom name → haptic request, surfaced into the manifest's `registerHaptics`. */\n private readonly unionHaptics = new Map<string, HapticRequest>()\n /**\n * Distinct literal className strings seen across all files, pre-merged\n * into per-scheme molecules at write time. Accumulate-only (like\n * `unionAtoms`): orphaned literals just yield unused molecules and get\n * reaped on the next cold start, so no refcount is needed.\n */\n private readonly unionLiterals = new Set<string>()\n /**\n * Responsive breakpoints captured from the parser. Refreshed on every\n * `recordFile` / `ensureProjectScanned` so user-defined\n * `--breakpoint-*` overrides land in the manifest the next time it's\n * written. Identical for every parser call within one parser instance\n * (theme is fixed for the parser's lifetime), so storing the latest\n * snapshot is sufficient.\n */\n private breakpoints: ReadonlyMap<string, number> = new Map()\n /** file → set of atom names this file currently contributes. */\n private readonly fileAtomSets = new Map<string, Set<string>>()\n /** atom name → how many files currently contribute it (refcount). */\n private readonly atomRefCount = new Map<string, number>()\n /** scheme → last-written source SHA. Skips re-writing unchanged schemes. */\n private readonly schemeSignatures = new Map<string, string>()\n /**\n * Per-atom serialized-value cache — identity-keyed on each atom's\n * SchemedStyle reference. Carried across `writeSchemes` calls so the\n * typical \"user added one className\" FR case re-stringifies ONE atom\n * instead of all 175+. Cleared on `ensureProjectScanned` (full\n * rescan replaces every reference) and individually invalidated for\n * any atom `applyDiff` mutates.\n */\n private readonly serializedCache: AtomSerializedCache = new Map()\n /** Running count of stringify passes (cache misses). Test telemetry. */\n private serializedMissesCount = 0\n /** Set after `ensureProjectScanned` completes. */\n private projectScanned = false\n /** Promise guard so concurrent first-calls await ONE scan. */\n private pendingScan: Promise<void> | null = null\n\n constructor(cacheDir: string, parser: TailwindParser) {\n this.cacheDir = cacheDir\n this.parser = parser\n mkdirSync(this.cacheDir, { recursive: true })\n }\n\n /** Absolute path of the manifest module (`rnwind/__generated/schemes`). */\n public get manifestPath(): string {\n return path.join(this.cacheDir, MANIFEST_BASENAME)\n }\n\n /**\n * Snapshot of every source file the builder has recorded atoms for\n * this worker session. Used by `withRnwindConfig`'s CSS watcher to\n * touch `mtime` on each and nudge Metro into re-transforming them\n * with the new theme values.\n * @returns Absolute source paths.\n */\n public recordedFiles(): readonly string[] {\n return [...this.fileAtomSets.keys()]\n }\n\n /** Cumulative cache-miss count — exposed for tests to assert cache behaviour. */\n public get serializedMisses(): number {\n return this.serializedMissesCount\n }\n\n /**\n * Absolute path of one scheme's style file.\n * @param scheme Registry key.\n * @returns Absolute path.\n */\n public schemePath(scheme: string): string {\n return schemeFilePath(this.cacheDir, scheme)\n }\n\n /**\n * One-shot oxide scan + compile across every source the parser was\n * configured with. Idempotent — safe to call from any entry point.\n * Concurrent callers share the same in-flight promise.\n */\n public async ensureProjectScanned(): Promise<void> {\n if (this.projectScanned) return\n if (this.pendingScan) return this.pendingScan\n this.pendingScan = (async () => {\n const parsed = await this.parser.parseProject()\n for (const [name, style] of parsed.atoms) this.unionAtoms.set(name, style)\n for (const [name, kf] of parsed.keyframes) this.unionKeyframes.set(name, kf)\n for (const [name, gradient] of parsed.gradientAtoms) this.unionGradients.set(name, gradient)\n for (const [name, haptic] of parsed.hapticAtoms) this.unionHaptics.set(name, haptic)\n this.breakpoints = parsed.breakpoints\n this.projectScanned = true\n })()\n try {\n await this.pendingScan\n } finally {\n this.pendingScan = null\n }\n }\n\n /**\n * Record one source file's resolved atoms + keyframes. Short-circuits\n * when the file's atom name set hasn't changed — the common case on\n * every Fast Refresh save of a file whose className literals are\n * unchanged.\n * @param file Absolute source file path.\n * @param atoms Per-atom resolved schemed styles from this transform.\n * @param keyframes Keyframe blocks referenced by this file's atoms.\n * @param literals\n * @returns `{ changed: true }` when the union shifted (new atom name,\n * removed atom name, or new keyframe) — the transformer uses this\n * to skip the serializer + `writeSchemes` when nothing changed.\n */\n public async recordFile(\n file: string,\n atoms: ReadonlyMap<string, SchemedStyle>,\n keyframes: ReadonlyMap<string, KeyframeBlock>,\n literals: readonly string[] = [],\n ): Promise<{ changed: boolean }> {\n await this.ensureProjectScanned()\n const literalAdded = this.recordLiterals(literals)\n const newAtomNames = new Set(atoms.keys())\n const previous = this.fileAtomSets.get(file)\n if (previous && setsEqual(previous, newAtomNames)) {\n // Atom set unchanged — skip the unionAtoms update entirely. The\n // project scan already populated them, and re-setting a fresh\n // object ref here would invalidate the per-atom serialization\n // cache on every FR save for no gain (values are identical).\n // Theme edits go through `getRnwindState` → new builder → fresh\n // scan, so stale cache is impossible.\n let keyframeAdded = false\n for (const [name, kf] of keyframes) {\n if (!this.unionKeyframes.has(name)) keyframeAdded = true\n this.unionKeyframes.set(name, kf)\n }\n return { changed: keyframeAdded || literalAdded }\n }\n this.applyDiff(file, newAtomNames, atoms, keyframes)\n return { changed: true }\n }\n\n /**\n * Merge a file's literal classNames into the union. A literal the\n * union hasn't seen flips `changed` so `writeSchemes` re-emits the\n * scheme files with the new molecule.\n * @param literals Distinct literal className strings.\n * @returns Whether any literal was new to the union.\n */\n private recordLiterals(literals: readonly string[]): boolean {\n let added = false\n for (const literal of literals) {\n if (this.unionLiterals.has(literal)) continue\n this.unionLiterals.add(literal)\n added = true\n }\n return added\n }\n\n /**\n * Forget one source file's contribution. Idempotent — repeated calls\n * for a file that's already dropped are no-ops. Does NOT remove the\n * atom from the union when another file (or the project scan) still\n * references it.\n * @param file Absolute source file path.\n */\n public dropFile(file: string): void {\n const previous = this.fileAtomSets.get(file)\n if (!previous) return\n for (const name of previous) {\n const count = (this.atomRefCount.get(name) ?? 0) - 1\n if (count <= 0) this.atomRefCount.delete(name)\n else this.atomRefCount.set(name, count)\n }\n this.fileAtomSets.delete(file)\n }\n\n /**\n * Serialize the union into per-scheme files + manifest, writing only\n * files whose source bytes changed. Called after every `recordFile`\n * from the transformer — and once at Metro startup via\n * `ensureFilesExist` to seed disk from the project scan alone.\n * @returns List of scheme keys whose files were rewritten (empty\n * when the union is byte-identical to the last flush).\n */\n public async writeSchemes(): Promise<{ changedSchemes: readonly string[] }> {\n await this.ensureProjectScanned()\n const sortedAtomNames = [...this.unionAtoms.keys()].toSorted((a, b) => a.localeCompare(b))\n const result = buildSchemeSources(sortedAtomNames, this.unionAtoms, this.unionKeyframes, this.serializedCache, this.breakpoints, this.unionGradients, this.unionHaptics, [...this.unionLiterals])\n this.serializedMissesCount += result.serializedMisses\n const { schemeSources, manifestSource } = result\n\n const changed: string[] = []\n for (const [scheme, source] of Object.entries(schemeSources)) {\n const signature = signatureOf(source)\n const target = schemeFilePath(this.cacheDir, scheme)\n if (this.schemeSignatures.get(scheme) === signature && existsSync(target)) continue\n if (writeIfChanged(target, source)) changed.push(scheme)\n this.schemeSignatures.set(scheme, signature)\n }\n\n const manifestSignature = signatureOf(manifestSource)\n const manifestTarget = path.join(this.cacheDir, MANIFEST_BASENAME)\n if (this.schemeSignatures.get('__manifest') !== manifestSignature || !existsSync(manifestTarget)) {\n if (writeIfChanged(manifestTarget, manifestSource)) changed.push('__manifest')\n this.schemeSignatures.set('__manifest', manifestSignature)\n }\n\n return { changedSchemes: changed }\n }\n\n /**\n * Ensure the manifest + common scheme files exist on disk so Metro's\n * resolver can SHA1 them at boot before the first transform runs.\n */\n public async ensureFilesExist(): Promise<void> {\n if (existsSync(this.manifestPath) && existsSync(schemeFilePath(this.cacheDir, 'common'))) {\n // Still trigger the scan so the in-memory union is complete; file\n // bytes may already be authoritative from a prior Metro run.\n await this.ensureProjectScanned()\n return\n }\n await this.writeSchemes()\n }\n\n /**\n * Apply one file's atom-name diff to the in-memory refcount + union.\n * @param file Source file path.\n * @param newAtoms New atom-name set for the file.\n * @param resolvedAtoms Fresh parser output — carries the resolved\n * styles for every entry in `newAtoms`.\n * @param newKeyframes Keyframes this file's atoms reference.\n */\n private applyDiff(\n file: string,\n newAtoms: ReadonlySet<string>,\n resolvedAtoms: ReadonlyMap<string, SchemedStyle>,\n newKeyframes: ReadonlyMap<string, KeyframeBlock>,\n ): void {\n const previous = this.fileAtomSets.get(file) ?? new Set<string>()\n for (const name of previous) {\n if (newAtoms.has(name)) continue\n const count = (this.atomRefCount.get(name) ?? 0) - 1\n if (count <= 0) this.atomRefCount.delete(name)\n else this.atomRefCount.set(name, count)\n // Do NOT remove `name` from `unionAtoms` — the project scan still\n // references it (orphaned atoms get reaped on the next Metro\n // cold start when the scanner re-walks disk).\n }\n for (const name of newAtoms) {\n if (!previous.has(name)) this.atomRefCount.set(name, (this.atomRefCount.get(name) ?? 0) + 1)\n // Only install the resolved style when the atom is new to the\n // union. Replacing an existing entry with a fresh parser-\n // produced object would swap the identity guard the per-atom\n // serialization cache uses and force a re-stringify for every\n // atom on every FR save. CSS edits rebuild the whole builder\n // (via `getRnwindState`) so stale values aren't possible.\n if (!this.unionAtoms.has(name)) {\n const style = resolvedAtoms.get(name)\n if (style) this.unionAtoms.set(name, style)\n }\n }\n this.fileAtomSets.set(file, new Set(newAtoms))\n for (const [name, kf] of newKeyframes) this.unionKeyframes.set(name, kf)\n }\n}\n\nexport { UnionBuilder }\n"],"names":["existsSync","readFileSync","mkdirSync","randomBytes","writeFileSync","renameSync","rmSync","createHash","buildSchemeSources"],"mappings":"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"}
@@ -21,6 +21,17 @@ declare class UnionBuilder {
21
21
  private readonly parser;
22
22
  private readonly unionAtoms;
23
23
  private readonly unionKeyframes;
24
+ /** atom name → gradient role/colour, surfaced into the manifest's `registerGradients`. */
25
+ private readonly unionGradients;
26
+ /** atom name → haptic request, surfaced into the manifest's `registerHaptics`. */
27
+ private readonly unionHaptics;
28
+ /**
29
+ * Distinct literal className strings seen across all files, pre-merged
30
+ * into per-scheme molecules at write time. Accumulate-only (like
31
+ * `unionAtoms`): orphaned literals just yield unused molecules and get
32
+ * reaped on the next cold start, so no refcount is needed.
33
+ */
34
+ private readonly unionLiterals;
24
35
  /**
25
36
  * Responsive breakpoints captured from the parser. Refreshed on every
26
37
  * `recordFile` / `ensureProjectScanned` so user-defined
@@ -84,13 +95,22 @@ declare class UnionBuilder {
84
95
  * @param file Absolute source file path.
85
96
  * @param atoms Per-atom resolved schemed styles from this transform.
86
97
  * @param keyframes Keyframe blocks referenced by this file's atoms.
98
+ * @param literals
87
99
  * @returns `{ changed: true }` when the union shifted (new atom name,
88
100
  * removed atom name, or new keyframe) — the transformer uses this
89
101
  * to skip the serializer + `writeSchemes` when nothing changed.
90
102
  */
91
- recordFile(file: string, atoms: ReadonlyMap<string, SchemedStyle>, keyframes: ReadonlyMap<string, KeyframeBlock>): Promise<{
103
+ recordFile(file: string, atoms: ReadonlyMap<string, SchemedStyle>, keyframes: ReadonlyMap<string, KeyframeBlock>, literals?: readonly string[]): Promise<{
92
104
  changed: boolean;
93
105
  }>;
106
+ /**
107
+ * Merge a file's literal classNames into the union. A literal the
108
+ * union hasn't seen flips `changed` so `writeSchemes` re-emits the
109
+ * scheme files with the new molecule.
110
+ * @param literals Distinct literal className strings.
111
+ * @returns Whether any literal was new to the union.
112
+ */
113
+ private recordLiterals;
94
114
  /**
95
115
  * Forget one source file's contribution. Idempotent — repeated calls
96
116
  * for a file that's already dropped are no-ops. Does NOT remove the
@@ -55,23 +55,16 @@ const CONTENT_CONTAINER_INTERFACES = new Set([
55
55
  ]);
56
56
  /**
57
57
  * Build the body of one interface augmentation: `className?: string`
58
- * plus `<prefix>ClassName?: string` for every prefix that applies to
59
- * THIS interface. Emits a single-line body so the file stays easy to
60
- * scan and diff.
58
+ * plus `contentContainerClassName?: string` for the scroll interfaces
59
+ * that natively expose `contentContainerStyle`. Emits a single-line
60
+ * body so the file stays easy to scan and diff.
61
61
  * @param interfaceName Bare interface name (generic parameters stripped).
62
- * @param userPrefixes Extra prefixes from the Metro config — applied to
63
- * every interface the same way `className` is.
64
62
  * @returns Space-separated property declarations.
65
63
  */
66
- function buildInterfaceBody(interfaceName, userPrefixes) {
64
+ function buildInterfaceBody(interfaceName) {
67
65
  const props = ['className?: string'];
68
66
  if (CONTENT_CONTAINER_INTERFACES.has(interfaceName))
69
67
  props.push(`${BUILTIN_PREFIX}ClassName?: string`);
70
- for (const prefix of userPrefixes) {
71
- if (prefix === BUILTIN_PREFIX)
72
- continue;
73
- props.push(`${prefix}ClassName?: string`);
74
- }
75
68
  return props.join('; ');
76
69
  }
77
70
  /**
@@ -83,13 +76,11 @@ function buildInterfaceBody(interfaceName, userPrefixes) {
83
76
  * `Scheme` union so `useScheme()` returns the actual names.
84
77
  *
85
78
  * Called once at Metro-config time — overwrite-on-rewrite so the file
86
- * stays in sync with the user's current theme CSS + prefix config.
79
+ * stays in sync with the user's current theme CSS.
87
80
  * @param targetPath Absolute path to write (typically `rnwind-types.d.ts` at project root).
88
81
  * @param schemes Scheme names from the user's `@variant` blocks (empty when none declared).
89
- * @param classNamePrefixes Extra prefixes from the Metro config — merged
90
- * on top of the built-in `'contentContainer'`. Defaults to empty.
91
82
  */
92
- function writeDtsFile(targetPath, schemes, classNamePrefixes = []) {
83
+ function writeDtsFile(targetPath, schemes) {
93
84
  const lines = [
94
85
  '// Auto-generated by rnwind — do not edit by hand.',
95
86
  '// Overwritten on Metro start / theme CSS change.',
@@ -98,7 +89,7 @@ function writeDtsFile(targetPath, schemes, classNamePrefixes = []) {
98
89
  ];
99
90
  for (const entry of INTERFACES) {
100
91
  const name = typeof entry === 'string' ? entry : entry.name;
101
- const body = buildInterfaceBody(name, classNamePrefixes);
92
+ const body = buildInterfaceBody(name);
102
93
  if (typeof entry === 'string') {
103
94
  lines.push(` interface ${entry} { ${body} }`);
104
95
  continue;
@@ -1 +1 @@
1
- {"version":3,"file":"dts.cjs","sources":["../../../../src/metro/dts.ts"],"sourcesContent":["import { mkdirSync, writeFileSync } from 'node:fs'\nimport path from 'node:path'\n\n/**\n * React Native component-props interfaces rnwind augments. Adding an\n * optional `className?: string` to each lets TypeScript accept\n * `<View className=\"…\" />` without the user touching types by hand.\n * The generic ones are written with a placeholder so the output type\n * stays parameterised.\n */\nconst INTERFACES: ReadonlyArray<string | { name: string; generic: string }> = [\n 'ViewProps',\n 'TextProps',\n 'ImageProps',\n 'ImageBackgroundProps',\n 'ScrollViewProps',\n 'PressableProps',\n 'ModalProps',\n 'TextInputProps',\n 'TouchableOpacityProps',\n 'TouchableHighlightProps',\n 'TouchableNativeFeedbackProps',\n 'TouchableWithoutFeedbackProps',\n 'SafeAreaViewProps',\n 'KeyboardAvoidingViewProps',\n 'RefreshControlProps',\n 'StatusBarProps',\n 'SwitchProps',\n 'ActivityIndicatorProps',\n 'SectionListProps',\n { name: 'FlatListProps', generic: 'ItemT' },\n { name: 'VirtualizedListProps', generic: 'ItemT' },\n]\n\n/**\n * Built-in prefix wired into every install. Restricted to the interfaces\n * that natively expose `contentContainerStyle` so TypeScript doesn't\n * start accepting `<View contentContainerClassName=\"…\" />` — RN would\n * silently ignore it at runtime and a typed lint is more useful than a\n * permissive one. User prefixes don't come with this restriction; rnwind\n * can't know which component props they target.\n */\nconst BUILTIN_PREFIX = 'contentContainer'\n\n/**\n * Interfaces that expose `contentContainerStyle` in React Native's own\n * types — the set the built-in prefix's `.d.ts` augmentation targets.\n * Everything else stays `className`-only by default.\n */\nconst CONTENT_CONTAINER_INTERFACES: ReadonlySet<string> = new Set([\n 'ScrollViewProps',\n 'FlatListProps',\n 'SectionListProps',\n 'VirtualizedListProps',\n])\n\n/**\n * Build the body of one interface augmentation: `className?: string`\n * plus `<prefix>ClassName?: string` for every prefix that applies to\n * THIS interface. Emits a single-line body so the file stays easy to\n * scan and diff.\n * @param interfaceName Bare interface name (generic parameters stripped).\n * @param userPrefixes Extra prefixes from the Metro config — applied to\n * every interface the same way `className` is.\n * @returns Space-separated property declarations.\n */\nfunction buildInterfaceBody(interfaceName: string, userPrefixes: readonly string[]): string {\n const props = ['className?: string']\n if (CONTENT_CONTAINER_INTERFACES.has(interfaceName)) props.push(`${BUILTIN_PREFIX}ClassName?: string`)\n for (const prefix of userPrefixes) {\n if (prefix === BUILTIN_PREFIX) continue\n props.push(`${prefix}ClassName?: string`)\n }\n return props.join('; ')\n}\n\n/**\n * Write the rnwind TypeScript declaration file. The generated `.d.ts`\n * declares a `rnwind` module augmentation for `react-native`'s\n * component props — every interface gets an optional `className?:\n * string` plus `<prefix>ClassName?: string` for each active prefix.\n * Runtime-rendered schemes (`@variant` blocks) are reflected in the\n * `Scheme` union so `useScheme()` returns the actual names.\n *\n * Called once at Metro-config time — overwrite-on-rewrite so the file\n * stays in sync with the user's current theme CSS + prefix config.\n * @param targetPath Absolute path to write (typically `rnwind-types.d.ts` at project root).\n * @param schemes Scheme names from the user's `@variant` blocks (empty when none declared).\n * @param classNamePrefixes Extra prefixes from the Metro config — merged\n * on top of the built-in `'contentContainer'`. Defaults to empty.\n */\nexport function writeDtsFile(\n targetPath: string,\n schemes: readonly string[],\n classNamePrefixes: readonly string[] = [],\n): void {\n const lines: string[] = [\n '// Auto-generated by rnwind — do not edit by hand.',\n '// Overwritten on Metro start / theme CSS change.',\n '',\n `declare module 'react-native' {`,\n ]\n for (const entry of INTERFACES) {\n const name = typeof entry === 'string' ? entry : entry.name\n const body = buildInterfaceBody(name, classNamePrefixes)\n if (typeof entry === 'string') {\n lines.push(` interface ${entry} { ${body} }`)\n continue\n }\n lines.push(` interface ${entry.name}<${entry.generic}> { ${body} }`)\n }\n lines.push('}', '')\n if (schemes.length > 0) {\n lines.push(`declare module 'rnwind' {`, ` export interface RnwindConfig {`)\n const schemeLiterals = schemes.map((s) => `'${s}'`).join(', ')\n lines.push(` themes: readonly [${schemeLiterals}]`, ` }`, '}', '')\n }\n // The `export {}` is mandatory — without at least one top-level\n // import/export, TypeScript treats this file as a SCRIPT and the\n // `declare module 'react-native'` block above becomes a complete\n // *replacement* declaration (hiding the real RN exports like\n // `Pressable`, `useColorScheme`, etc.). With `export {}` the file\n // becomes a module and the `declare module` blocks are interpreted as\n // *augmentations* — which is what we want.\n lines.push('export {}', '')\n mkdirSync(path.dirname(targetPath), { recursive: true })\n writeFileSync(targetPath, lines.join('\\n'), 'utf8')\n}\n"],"names":["mkdirSync","writeFileSync"],"mappings":"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"}
1
+ {"version":3,"file":"dts.cjs","sources":["../../../../src/metro/dts.ts"],"sourcesContent":["import { mkdirSync, writeFileSync } from 'node:fs'\nimport path from 'node:path'\n\n/**\n * React Native component-props interfaces rnwind augments. Adding an\n * optional `className?: string` to each lets TypeScript accept\n * `<View className=\"…\" />` without the user touching types by hand.\n * The generic ones are written with a placeholder so the output type\n * stays parameterised.\n */\nconst INTERFACES: ReadonlyArray<string | { name: string; generic: string }> = [\n 'ViewProps',\n 'TextProps',\n 'ImageProps',\n 'ImageBackgroundProps',\n 'ScrollViewProps',\n 'PressableProps',\n 'ModalProps',\n 'TextInputProps',\n 'TouchableOpacityProps',\n 'TouchableHighlightProps',\n 'TouchableNativeFeedbackProps',\n 'TouchableWithoutFeedbackProps',\n 'SafeAreaViewProps',\n 'KeyboardAvoidingViewProps',\n 'RefreshControlProps',\n 'StatusBarProps',\n 'SwitchProps',\n 'ActivityIndicatorProps',\n 'SectionListProps',\n { name: 'FlatListProps', generic: 'ItemT' },\n { name: 'VirtualizedListProps', generic: 'ItemT' },\n]\n\n/**\n * Built-in prefix wired into every install. Restricted to the interfaces\n * that natively expose `contentContainerStyle` so TypeScript doesn't\n * start accepting `<View contentContainerClassName=\"…\" />` — RN would\n * silently ignore it at runtime and a typed lint is more useful than a\n * permissive one. User prefixes don't come with this restriction; rnwind\n * can't know which component props they target.\n */\nconst BUILTIN_PREFIX = 'contentContainer'\n\n/**\n * Interfaces that expose `contentContainerStyle` in React Native's own\n * types — the set the built-in prefix's `.d.ts` augmentation targets.\n * Everything else stays `className`-only by default.\n */\nconst CONTENT_CONTAINER_INTERFACES: ReadonlySet<string> = new Set([\n 'ScrollViewProps',\n 'FlatListProps',\n 'SectionListProps',\n 'VirtualizedListProps',\n])\n\n/**\n * Build the body of one interface augmentation: `className?: string`\n * plus `contentContainerClassName?: string` for the scroll interfaces\n * that natively expose `contentContainerStyle`. Emits a single-line\n * body so the file stays easy to scan and diff.\n * @param interfaceName Bare interface name (generic parameters stripped).\n * @returns Space-separated property declarations.\n */\nfunction buildInterfaceBody(interfaceName: string): string {\n const props = ['className?: string']\n if (CONTENT_CONTAINER_INTERFACES.has(interfaceName)) props.push(`${BUILTIN_PREFIX}ClassName?: string`)\n return props.join('; ')\n}\n\n/**\n * Write the rnwind TypeScript declaration file. The generated `.d.ts`\n * declares a `rnwind` module augmentation for `react-native`'s\n * component props — every interface gets an optional `className?:\n * string` plus `<prefix>ClassName?: string` for each active prefix.\n * Runtime-rendered schemes (`@variant` blocks) are reflected in the\n * `Scheme` union so `useScheme()` returns the actual names.\n *\n * Called once at Metro-config time — overwrite-on-rewrite so the file\n * stays in sync with the user's current theme CSS.\n * @param targetPath Absolute path to write (typically `rnwind-types.d.ts` at project root).\n * @param schemes Scheme names from the user's `@variant` blocks (empty when none declared).\n */\nexport function writeDtsFile(targetPath: string, schemes: readonly string[]): void {\n const lines: string[] = [\n '// Auto-generated by rnwind — do not edit by hand.',\n '// Overwritten on Metro start / theme CSS change.',\n '',\n `declare module 'react-native' {`,\n ]\n for (const entry of INTERFACES) {\n const name = typeof entry === 'string' ? entry : entry.name\n const body = buildInterfaceBody(name)\n if (typeof entry === 'string') {\n lines.push(` interface ${entry} { ${body} }`)\n continue\n }\n lines.push(` interface ${entry.name}<${entry.generic}> { ${body} }`)\n }\n lines.push('}', '')\n if (schemes.length > 0) {\n lines.push(`declare module 'rnwind' {`, ` export interface RnwindConfig {`)\n const schemeLiterals = schemes.map((s) => `'${s}'`).join(', ')\n lines.push(` themes: readonly [${schemeLiterals}]`, ` }`, '}', '')\n }\n // The `export {}` is mandatory — without at least one top-level\n // import/export, TypeScript treats this file as a SCRIPT and the\n // `declare module 'react-native'` block above becomes a complete\n // *replacement* declaration (hiding the real RN exports like\n // `Pressable`, `useColorScheme`, etc.). With `export {}` the file\n // becomes a module and the `declare module` blocks are interpreted as\n // *augmentations* — which is what we want.\n lines.push('export {}', '')\n mkdirSync(path.dirname(targetPath), { recursive: true })\n writeFileSync(targetPath, lines.join('\\n'), 'utf8')\n}\n"],"names":["mkdirSync","writeFileSync"],"mappings":"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"}
@@ -7,10 +7,8 @@
7
7
  * `Scheme` union so `useScheme()` returns the actual names.
8
8
  *
9
9
  * Called once at Metro-config time — overwrite-on-rewrite so the file
10
- * stays in sync with the user's current theme CSS + prefix config.
10
+ * stays in sync with the user's current theme CSS.
11
11
  * @param targetPath Absolute path to write (typically `rnwind-types.d.ts` at project root).
12
12
  * @param schemes Scheme names from the user's `@variant` blocks (empty when none declared).
13
- * @param classNamePrefixes Extra prefixes from the Metro config — merged
14
- * on top of the built-in `'contentContainer'`. Defaults to empty.
15
13
  */
16
- export declare function writeDtsFile(targetPath: string, schemes: readonly string[], classNamePrefixes?: readonly string[]): void;
14
+ export declare function writeDtsFile(targetPath: string, schemes: readonly string[]): void;
@@ -6,6 +6,7 @@ var node_crypto = require('node:crypto');
6
6
  var unionBuilder = require('../core/style-builder/union-builder.cjs');
7
7
  var twParser = require('../core/parser/tw-parser.cjs');
8
8
  var cssImports = require('./css-imports.cjs');
9
+ var wrapImports = require('./wrap-imports.cjs');
9
10
 
10
11
  /**
11
12
  * Default oxide Scanner globs — walk every JS/TS source under the
@@ -50,12 +51,8 @@ const CSS_ENTRY_ENV = 'RNWIND_CSS_ENTRY_FILE';
50
51
  const CACHE_DIR_ENV = 'RNWIND_CACHE_DIR';
51
52
  /** Env var carrying `watchFolders` from Metro config (NUL-separated). */
52
53
  const WATCH_FOLDERS_ENV = 'RNWIND_WATCH_FOLDERS';
53
- /** Env var carrying extra className prefixes the Metro config supplied. */
54
- const CLASSNAME_PREFIXES_ENV = 'RNWIND_CLASSNAME_PREFIXES';
55
- /** Env var carrying extra import sources whose JSX exports get className→style rewrites. Comma-separated. */
56
- const HOST_SOURCES_ENV = 'RNWIND_HOST_SOURCES';
57
- /** Env var carrying extra JSX tag names (verbatim, may contain `.`) treated as hosts. Comma-separated. */
58
- const HOST_COMPONENTS_ENV = 'RNWIND_HOST_COMPONENTS';
54
+ /** Env var carrying extra modules whose component exports get `wrap()`-ed. Comma-separated. */
55
+ const WRAP_MODULES_ENV = 'RNWIND_WRAP_MODULES';
59
56
  /** Memoised library fingerprint — read once per worker process. */
60
57
  let libraryFingerprint;
61
58
  /** Live state shared across one Metro transform worker. */
@@ -85,10 +82,10 @@ function readThemeHashFor(cssPath) {
85
82
  * dev OR npm install of a new version) the file bytes change, the
86
83
  * fingerprint rotates, and Metro's transform cache invalidates.
87
84
  *
88
- * Includes the JSX rewriter (`transform-ast`) alongside the parser /
89
- * style-builder so a change to the transformer — e.g. renaming the
90
- * injected context hook — invalidates every stale per-file cache entry
91
- * on the next dev run. Without this, a user upgrading rnwind in-place
85
+ * Includes the import-rewriter (`wrap-imports`) and runtime resolver
86
+ * alongside the parser / style-builder so a change to the transformer —
87
+ * e.g. renaming the injected wrap helper — invalidates every stale
88
+ * per-file cache entry on the next dev run. Without this, a user upgrading rnwind in-place
92
89
  * would keep loading the old transformed bytes; React-refresh would
93
90
  * then preserve fiber state across the version bump and the rendered
94
91
  * hook list could shift, surfacing as "change in the order of Hooks"
@@ -105,14 +102,14 @@ function getLibraryFingerprint() {
105
102
  path.resolve(here, '..', 'core', 'style-builder', 'build-style.cjs'),
106
103
  path.resolve(here, '..', 'core', 'parser', 'tw-parser.mjs'),
107
104
  path.resolve(here, '..', 'core', 'parser', 'tw-parser.cjs'),
108
- path.resolve(here, 'transform-ast.mjs'),
109
- path.resolve(here, 'transform-ast.cjs'),
105
+ path.resolve(here, 'wrap-imports.mjs'),
106
+ path.resolve(here, 'wrap-imports.cjs'),
110
107
  path.resolve(here, 'transformer.mjs'),
111
108
  path.resolve(here, 'transformer.cjs'),
112
109
  // Source-tree fallback for tests + workspace dev (no built lib yet).
113
110
  path.resolve(here, '..', '..', 'src', 'core', 'style-builder', 'build-style.ts'),
114
111
  path.resolve(here, '..', '..', 'src', 'core', 'parser', 'tw-parser.ts'),
115
- path.resolve(here, '..', '..', 'src', 'metro', 'transform-ast.ts'),
112
+ path.resolve(here, '..', '..', 'src', 'metro', 'wrap-imports.ts'),
116
113
  path.resolve(here, '..', '..', 'src', 'metro', 'transformer.ts'),
117
114
  ];
118
115
  const hash = node_crypto.createHash('sha256');
@@ -137,12 +134,10 @@ function getLibraryFingerprint() {
137
134
  * can rebuild the same state without re-reading the Metro config.
138
135
  * @param cssEntryFile Absolute path to the user's theme CSS.
139
136
  * @param cacheDir Absolute path to the cache dir (`.rnwind`).
140
- * @param watchFolders
141
- * @param classNamePrefixes Extra JSX prop-name prefixes to rewrite.
142
- * @param hostSources
143
- * @param hostComponents
137
+ * @param watchFolders Monorepo watch folders to scan for atoms.
138
+ * @param wrapModules Extra modules whose component exports get `wrap()`-ed.
144
139
  */
145
- function configureRnwindState(cssEntryFile, cacheDir, watchFolders = [], classNamePrefixes, hostSources, hostComponents) {
140
+ function configureRnwindState(cssEntryFile, cacheDir, watchFolders = [], wrapModules) {
146
141
  process.env[CSS_ENTRY_ENV] = cssEntryFile;
147
142
  process.env[CACHE_DIR_ENV] = cacheDir;
148
143
  if (watchFolders.length === 0) {
@@ -151,62 +146,23 @@ function configureRnwindState(cssEntryFile, cacheDir, watchFolders = [], classNa
151
146
  else {
152
147
  process.env[WATCH_FOLDERS_ENV] = watchFolders.join('\0');
153
148
  }
154
- if (!classNamePrefixes || classNamePrefixes.length === 0) {
155
- delete process.env[CLASSNAME_PREFIXES_ENV];
149
+ if (!wrapModules || wrapModules.length === 0) {
150
+ delete process.env[WRAP_MODULES_ENV];
156
151
  }
157
152
  else {
158
- process.env[CLASSNAME_PREFIXES_ENV] = classNamePrefixes.join(',');
159
- }
160
- if (!hostSources || hostSources.length === 0) {
161
- delete process.env[HOST_SOURCES_ENV];
162
- }
163
- else {
164
- process.env[HOST_SOURCES_ENV] = hostSources.join(',');
165
- }
166
- if (!hostComponents || hostComponents.length === 0) {
167
- delete process.env[HOST_COMPONENTS_ENV];
168
- }
169
- else {
170
- process.env[HOST_COMPONENTS_ENV] = hostComponents.join(',');
153
+ process.env[WRAP_MODULES_ENV] = wrapModules.join(',');
171
154
  }
172
155
  cached = null;
173
156
  }
174
157
  /**
175
- * Read the caller-configured extra className prefixes out of the
176
- * worker environment. Returns an empty array when unset — the
177
- * transformer applies the built-in `contentContainer` default on top
178
- * either way.
179
- * @returns User-supplied extra prefixes.
180
- */
181
- function getClassNamePrefixes() {
182
- const raw = process.env[CLASSNAME_PREFIXES_ENV];
183
- if (!raw || raw.length === 0)
184
- return [];
185
- return raw.split(',').filter((entry) => entry.length > 0);
186
- }
187
- /**
188
- * Read the caller-configured extra host module sources out of the
189
- * worker environment. Empty array when unset — the transformer applies
190
- * its built-in default list on top either way.
191
- * @returns User-supplied extra host sources.
158
+ * Effective module → wrap-policy map: the built-in defaults merged with
159
+ * any extra modules the Metro config supplied.
160
+ * @returns Module policy map the import-rewrite consults.
192
161
  */
193
- function getHostSources() {
194
- const raw = process.env[HOST_SOURCES_ENV];
195
- if (!raw || raw.length === 0)
196
- return [];
197
- return raw.split(',').filter((entry) => entry.length > 0);
198
- }
199
- /**
200
- * Read the caller-configured extra host JSX tag names out of the worker
201
- * environment. Verbatim names — may include `.` for member expressions
202
- * like `'Animated.View'`.
203
- * @returns User-supplied extra host component names.
204
- */
205
- function getHostComponents() {
206
- const raw = process.env[HOST_COMPONENTS_ENV];
207
- if (!raw || raw.length === 0)
208
- return [];
209
- return raw.split(',').filter((entry) => entry.length > 0);
162
+ function getWrapModules() {
163
+ const raw = process.env[WRAP_MODULES_ENV];
164
+ const extra = raw && raw.length > 0 ? raw.split(',').filter((entry) => entry.length > 0) : undefined;
165
+ return wrapImports.buildWrapModules(extra);
210
166
  }
211
167
  /**
212
168
  * Fetch (or build) the worker-local rnwind state. Re-reads the theme
@@ -248,14 +204,12 @@ function getRnwindState(projectRoot) {
248
204
  */
249
205
  function getRnwindCacheKey() {
250
206
  const cssEntry = process.env[CSS_ENTRY_ENV] ?? '';
251
- const prefixes = process.env[CLASSNAME_PREFIXES_ENV] ?? '';
252
- // Host source / component config changes which JSX tags get rewritten,
253
- // so it MUST flip the cache key otherwise Metro replays stale
254
- // transforms (a newly-opted-in host keeps its raw className, a removed
255
- // one keeps the rewrite).
256
- const hostSources = process.env[HOST_SOURCES_ENV] ?? '';
257
- const hostComponents = process.env[HOST_COMPONENTS_ENV] ?? '';
258
- return `rnwind:${cssEntry}:${readThemeHashFor(cssEntry)}|lib:${getLibraryFingerprint()}|pfx:${prefixes}|hs:${hostSources}|hc:${hostComponents}`;
207
+ // Wrap-module config changes which import sites get `wrap()`-ed, so it
208
+ // MUST flip the cache key otherwise Metro replays stale transforms
209
+ // (a newly-opted-in module keeps its raw import, a removed one keeps
210
+ // the wrap).
211
+ const wrapModules = process.env[WRAP_MODULES_ENV] ?? '';
212
+ return `rnwind:${cssEntry}:${readThemeHashFor(cssEntry)}|lib:${getLibraryFingerprint()}|wm:${wrapModules}`;
259
213
  }
260
214
  /** Drop the cached state — call after editing the theme CSS. */
261
215
  function resetRnwindState() {
@@ -290,11 +244,9 @@ function manifestPathFor() {
290
244
  }
291
245
 
292
246
  exports.configureRnwindState = configureRnwindState;
293
- exports.getClassNamePrefixes = getClassNamePrefixes;
294
- exports.getHostComponents = getHostComponents;
295
- exports.getHostSources = getHostSources;
296
247
  exports.getRnwindCacheKey = getRnwindCacheKey;
297
248
  exports.getRnwindState = getRnwindState;
249
+ exports.getWrapModules = getWrapModules;
298
250
  exports.manifestPathFor = manifestPathFor;
299
251
  exports.onThemeChange = onThemeChange;
300
252
  exports.resetRnwindState = resetRnwindState;