rnwind 0.0.3 → 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.
- package/lib/cjs/core/normalize-classname.cjs +25 -0
- package/lib/cjs/core/normalize-classname.cjs.map +1 -0
- package/lib/cjs/core/normalize-classname.d.ts +10 -0
- package/lib/cjs/core/style-builder/build-style.cjs +258 -58
- package/lib/cjs/core/style-builder/build-style.cjs.map +1 -1
- package/lib/cjs/core/style-builder/build-style.d.ts +6 -1
- package/lib/cjs/core/style-builder/union-builder.cjs +37 -3
- package/lib/cjs/core/style-builder/union-builder.cjs.map +1 -1
- package/lib/cjs/core/style-builder/union-builder.d.ts +21 -1
- package/lib/cjs/metro/css-imports.cjs +81 -0
- package/lib/cjs/metro/css-imports.cjs.map +1 -0
- package/lib/cjs/metro/css-imports.d.ts +8 -0
- package/lib/cjs/metro/dts.cjs +7 -16
- package/lib/cjs/metro/dts.cjs.map +1 -1
- package/lib/cjs/metro/dts.d.ts +2 -4
- package/lib/cjs/metro/state.cjs +38 -86
- package/lib/cjs/metro/state.cjs.map +1 -1
- package/lib/cjs/metro/state.d.ts +8 -25
- package/lib/cjs/metro/transformer.cjs +193 -34
- package/lib/cjs/metro/transformer.cjs.map +1 -1
- package/lib/cjs/metro/with-config.cjs +2 -2
- package/lib/cjs/metro/with-config.cjs.map +1 -1
- package/lib/cjs/metro/with-config.d.ts +11 -26
- package/lib/cjs/metro/wrap-imports.cjs +273 -0
- package/lib/cjs/metro/wrap-imports.cjs.map +1 -0
- package/lib/cjs/metro/wrap-imports.d.ts +26 -0
- package/lib/cjs/runtime/components/rnwind-provider.cjs +0 -17
- package/lib/cjs/runtime/components/rnwind-provider.cjs.map +1 -1
- package/lib/cjs/runtime/components/rnwind-provider.d.ts +0 -14
- package/lib/cjs/runtime/hooks/use-css.cjs +16 -10
- package/lib/cjs/runtime/hooks/use-css.cjs.map +1 -1
- package/lib/cjs/runtime/hooks/use-css.d.ts +15 -9
- package/lib/cjs/runtime/index.cjs +11 -13
- package/lib/cjs/runtime/index.cjs.map +1 -1
- package/lib/cjs/runtime/index.d.ts +4 -9
- package/lib/cjs/runtime/lookup-css.cjs +10 -0
- package/lib/cjs/runtime/lookup-css.cjs.map +1 -1
- package/lib/cjs/runtime/lookup-css.d.ts +7 -0
- package/lib/cjs/runtime/resolve.cjs +348 -0
- package/lib/cjs/runtime/resolve.cjs.map +1 -0
- package/lib/cjs/runtime/resolve.d.ts +61 -0
- package/lib/cjs/runtime/wrap.cjs +254 -0
- package/lib/cjs/runtime/wrap.cjs.map +1 -0
- package/lib/cjs/runtime/wrap.d.ts +37 -0
- package/lib/cjs/testing/index.cjs +81 -50
- package/lib/cjs/testing/index.cjs.map +1 -1
- package/lib/esm/core/normalize-classname.d.ts +10 -0
- package/lib/esm/core/normalize-classname.mjs +23 -0
- package/lib/esm/core/normalize-classname.mjs.map +1 -0
- package/lib/esm/core/style-builder/build-style.d.ts +6 -1
- package/lib/esm/core/style-builder/build-style.mjs +258 -58
- package/lib/esm/core/style-builder/build-style.mjs.map +1 -1
- package/lib/esm/core/style-builder/union-builder.d.ts +21 -1
- package/lib/esm/core/style-builder/union-builder.mjs +37 -3
- package/lib/esm/core/style-builder/union-builder.mjs.map +1 -1
- package/lib/esm/metro/css-imports.d.ts +8 -0
- package/lib/esm/metro/css-imports.mjs +79 -0
- package/lib/esm/metro/css-imports.mjs.map +1 -0
- package/lib/esm/metro/dts.d.ts +2 -4
- package/lib/esm/metro/dts.mjs +7 -16
- package/lib/esm/metro/dts.mjs.map +1 -1
- package/lib/esm/metro/state.d.ts +8 -25
- package/lib/esm/metro/state.mjs +39 -85
- package/lib/esm/metro/state.mjs.map +1 -1
- package/lib/esm/metro/transformer.mjs +194 -35
- package/lib/esm/metro/transformer.mjs.map +1 -1
- package/lib/esm/metro/with-config.d.ts +11 -26
- package/lib/esm/metro/with-config.mjs +2 -2
- package/lib/esm/metro/with-config.mjs.map +1 -1
- package/lib/esm/metro/wrap-imports.d.ts +26 -0
- package/lib/esm/metro/wrap-imports.mjs +250 -0
- package/lib/esm/metro/wrap-imports.mjs.map +1 -0
- package/lib/esm/runtime/components/rnwind-provider.d.ts +0 -14
- package/lib/esm/runtime/components/rnwind-provider.mjs +1 -17
- package/lib/esm/runtime/components/rnwind-provider.mjs.map +1 -1
- package/lib/esm/runtime/hooks/use-css.d.ts +15 -9
- package/lib/esm/runtime/hooks/use-css.mjs +16 -10
- package/lib/esm/runtime/hooks/use-css.mjs.map +1 -1
- package/lib/esm/runtime/index.d.ts +4 -9
- package/lib/esm/runtime/index.mjs +4 -4
- package/lib/esm/runtime/index.mjs.map +1 -1
- package/lib/esm/runtime/lookup-css.d.ts +7 -0
- package/lib/esm/runtime/lookup-css.mjs +10 -1
- package/lib/esm/runtime/lookup-css.mjs.map +1 -1
- package/lib/esm/runtime/resolve.d.ts +61 -0
- package/lib/esm/runtime/resolve.mjs +341 -0
- package/lib/esm/runtime/resolve.mjs.map +1 -0
- package/lib/esm/runtime/wrap.d.ts +37 -0
- package/lib/esm/runtime/wrap.mjs +251 -0
- package/lib/esm/runtime/wrap.mjs.map +1 -0
- package/lib/esm/testing/index.mjs +84 -53
- package/lib/esm/testing/index.mjs.map +1 -1
- package/package.json +2 -1
- package/src/core/normalize-classname.ts +19 -0
- package/src/core/style-builder/build-style.ts +286 -55
- package/src/core/style-builder/union-builder.ts +36 -3
- package/src/metro/css-imports.ts +75 -0
- package/src/metro/dts.ts +7 -19
- package/src/metro/state.ts +38 -83
- package/src/metro/transformer.ts +190 -34
- package/src/metro/with-config.ts +13 -28
- package/src/metro/wrap-imports.ts +260 -0
- package/src/runtime/components/rnwind-provider.tsx +0 -17
- package/src/runtime/hooks/use-css.ts +17 -11
- package/src/runtime/index.ts +3 -26
- package/src/runtime/lookup-css.ts +10 -0
- package/src/runtime/resolve.ts +381 -0
- package/src/runtime/wrap.tsx +267 -0
- package/src/testing/index.ts +106 -56
- package/lib/cjs/core/parser/text-truncate.cjs +0 -78
- package/lib/cjs/core/parser/text-truncate.cjs.map +0 -1
- package/lib/cjs/metro/transform-ast.cjs +0 -1472
- package/lib/cjs/metro/transform-ast.cjs.map +0 -1
- package/lib/cjs/metro/transform-ast.d.ts +0 -88
- package/lib/cjs/runtime/haptics.cjs +0 -113
- package/lib/cjs/runtime/haptics.cjs.map +0 -1
- package/lib/cjs/runtime/haptics.d.ts +0 -48
- package/lib/cjs/runtime/interactive-box.cjs +0 -35
- package/lib/cjs/runtime/interactive-box.cjs.map +0 -1
- package/lib/cjs/runtime/interactive-box.d.ts +0 -40
- package/lib/esm/core/parser/text-truncate.mjs +0 -75
- package/lib/esm/core/parser/text-truncate.mjs.map +0 -1
- package/lib/esm/metro/transform-ast.d.ts +0 -88
- package/lib/esm/metro/transform-ast.mjs +0 -1451
- package/lib/esm/metro/transform-ast.mjs.map +0 -1
- package/lib/esm/runtime/haptics.d.ts +0 -48
- package/lib/esm/runtime/haptics.mjs +0 -110
- package/lib/esm/runtime/haptics.mjs.map +0 -1
- package/lib/esm/runtime/interactive-box.d.ts +0 -40
- package/lib/esm/runtime/interactive-box.mjs +0 -33
- package/lib/esm/runtime/interactive-box.mjs.map +0 -1
- package/src/metro/transform-ast.ts +0 -1729
- package/src/runtime/haptics.ts +0 -120
- package/src/runtime/interactive-box.tsx +0 -57
package/lib/cjs/metro/state.cjs
CHANGED
|
@@ -5,6 +5,8 @@ var path = require('node:path');
|
|
|
5
5
|
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
|
+
var cssImports = require('./css-imports.cjs');
|
|
9
|
+
var wrapImports = require('./wrap-imports.cjs');
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Default oxide Scanner globs — walk every JS/TS source under the
|
|
@@ -49,21 +51,18 @@ const CSS_ENTRY_ENV = 'RNWIND_CSS_ENTRY_FILE';
|
|
|
49
51
|
const CACHE_DIR_ENV = 'RNWIND_CACHE_DIR';
|
|
50
52
|
/** Env var carrying `watchFolders` from Metro config (NUL-separated). */
|
|
51
53
|
const WATCH_FOLDERS_ENV = 'RNWIND_WATCH_FOLDERS';
|
|
52
|
-
/** Env var carrying extra
|
|
53
|
-
const
|
|
54
|
-
/** Env var carrying extra import sources whose JSX exports get className→style rewrites. Comma-separated. */
|
|
55
|
-
const HOST_SOURCES_ENV = 'RNWIND_HOST_SOURCES';
|
|
56
|
-
/** Env var carrying extra JSX tag names (verbatim, may contain `.`) treated as hosts. Comma-separated. */
|
|
57
|
-
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';
|
|
58
56
|
/** Memoised library fingerprint — read once per worker process. */
|
|
59
57
|
let libraryFingerprint;
|
|
60
58
|
/** Live state shared across one Metro transform worker. */
|
|
61
59
|
let cached = null;
|
|
62
60
|
/**
|
|
63
|
-
* Cheap content-hash readout. SHA-256 prefix of the
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* when the
|
|
61
|
+
* Cheap content-hash readout. SHA-256 prefix of the FULLY-RESOLVED theme
|
|
62
|
+
* CSS — `@import`s flattened — so an edit to a theme file the entry only
|
|
63
|
+
* re-exports (`@import "@acme/ui/theme.css"`) still rotates the hash and
|
|
64
|
+
* invalidates Metro's cache. Returns `'missing'` when the entry can't be
|
|
65
|
+
* read so the cache key stays deterministic.
|
|
67
66
|
* @param cssPath Absolute CSS path.
|
|
68
67
|
* @returns 16-char hex content hash.
|
|
69
68
|
*/
|
|
@@ -71,9 +70,7 @@ function readThemeHashFor(cssPath) {
|
|
|
71
70
|
if (!node_fs.existsSync(cssPath))
|
|
72
71
|
return 'missing';
|
|
73
72
|
try {
|
|
74
|
-
|
|
75
|
-
const mtime = node_fs.statSync(cssPath).mtimeMs.toString();
|
|
76
|
-
return node_crypto.createHash('sha256').update(bytes).update(mtime).digest('hex').slice(0, 16);
|
|
73
|
+
return node_crypto.createHash('sha256').update(cssImports.resolveThemeCss(cssPath)).digest('hex').slice(0, 16);
|
|
77
74
|
}
|
|
78
75
|
catch {
|
|
79
76
|
return 'missing';
|
|
@@ -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
|
|
89
|
-
* style-builder so a change to the transformer —
|
|
90
|
-
* injected
|
|
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, '
|
|
109
|
-
path.resolve(here, '
|
|
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', '
|
|
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
|
|
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 = [],
|
|
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 (!
|
|
155
|
-
delete process.env[
|
|
149
|
+
if (!wrapModules || wrapModules.length === 0) {
|
|
150
|
+
delete process.env[WRAP_MODULES_ENV];
|
|
156
151
|
}
|
|
157
152
|
else {
|
|
158
|
-
process.env[
|
|
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
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
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
|
|
194
|
-
const raw = process.env[
|
|
195
|
-
|
|
196
|
-
|
|
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
|
|
@@ -228,7 +184,7 @@ function getRnwindState(projectRoot) {
|
|
|
228
184
|
const currentHash = readThemeHashFor(cssEntry);
|
|
229
185
|
if (cached?.themeHash === currentHash && cached.projectRoot === projectRoot)
|
|
230
186
|
return cached;
|
|
231
|
-
const themeCss =
|
|
187
|
+
const themeCss = cssImports.resolveThemeCss(cssEntry);
|
|
232
188
|
const parser = new twParser.TailwindParser({
|
|
233
189
|
themeCss,
|
|
234
190
|
sources: defaultSources(projectRoot, cacheDir, readWatchFolders()),
|
|
@@ -248,14 +204,12 @@ function getRnwindState(projectRoot) {
|
|
|
248
204
|
*/
|
|
249
205
|
function getRnwindCacheKey() {
|
|
250
206
|
const cssEntry = process.env[CSS_ENTRY_ENV] ?? '';
|
|
251
|
-
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
//
|
|
255
|
-
|
|
256
|
-
|
|
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;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.cjs","sources":["../../../../src/metro/state.ts"],"sourcesContent":["import { existsSync, readFileSync, statSync } from 'node:fs'\nimport path from 'node:path'\nimport { createHash } from 'node:crypto'\nimport { UnionBuilder } from '../core/style-builder'\nimport { TailwindParser, type SourceEntry } from '../core/parser'\n\n/**\n * Default oxide Scanner globs — walk every JS/TS source under the\n * project root AND every monorepo watch folder, excluding\n * `node_modules` and rnwind's own cache dir so we don't rescan\n * generated scheme files.\n *\n * Monorepo layouts (Yarn workspaces, pnpm workspaces, Nx) surface\n * sibling package roots as `metroConfig.watchFolders`. Every folder\n * Metro watches must also be scanned so atoms declared in shared UI\n * packages make it into the union — without this, only the app's\n * own files would be scanned and every UI-package atom would resolve\n * to `undefined` at runtime.\n * @param projectRoot Absolute project root.\n * @param cacheDir Absolute rnwind cache dir (to exclude).\n * @param watchFolders Extra monorepo roots Metro is watching.\n * @returns Scanner sources suitable for `parser.parseProject()`.\n */\nfunction defaultSources(projectRoot: string, cacheDir: string, watchFolders: readonly string[]): readonly SourceEntry[] {\n const cacheBaseName = path.basename(cacheDir)\n const roots = new Set<string>([projectRoot, ...watchFolders])\n const sources: SourceEntry[] = []\n for (const root of roots) {\n sources.push({ base: root, pattern: '**/*.{ts,tsx,js,jsx}', negated: false }, { base: root, pattern: '**/node_modules/**', negated: true }, { base: root, pattern: `**/${cacheBaseName}/**`, negated: true })\n }\n return sources\n}\n\n/**\n * Read monorepo watch-folder paths out of the worker environment.\n * Empty array when the host isn't a monorepo.\n * @returns Absolute paths Metro also watches (sibling packages).\n */\nfunction readWatchFolders(): readonly string[] {\n const raw = process.env[WATCH_FOLDERS_ENV]\n if (!raw || raw.length === 0) return []\n return raw.split('\\0').filter((entry) => entry.length > 0)\n}\n\n/** Env var Metro workers read to locate the theme CSS on disk. */\nconst CSS_ENTRY_ENV = 'RNWIND_CSS_ENTRY_FILE'\n/** Env var Metro workers read to locate the cache directory (`.rnwind`). */\nconst CACHE_DIR_ENV = 'RNWIND_CACHE_DIR'\n/** Env var carrying `watchFolders` from Metro config (NUL-separated). */\nconst WATCH_FOLDERS_ENV = 'RNWIND_WATCH_FOLDERS'\n/** Env var carrying extra className prefixes the Metro config supplied. */\nconst CLASSNAME_PREFIXES_ENV = 'RNWIND_CLASSNAME_PREFIXES'\n/** Env var carrying extra import sources whose JSX exports get className→style rewrites. Comma-separated. */\nconst HOST_SOURCES_ENV = 'RNWIND_HOST_SOURCES'\n/** Env var carrying extra JSX tag names (verbatim, may contain `.`) treated as hosts. Comma-separated. */\nconst HOST_COMPONENTS_ENV = 'RNWIND_HOST_COMPONENTS'\n\n/** Memoised library fingerprint — read once per worker process. */\nlet libraryFingerprint: string | undefined\n\n/** Live state shared across one Metro transform worker. */\nlet cached: RnwindState | null = null\n\n/**\n * Cheap content-hash readout. SHA-256 prefix of the CSS bytes plus the\n * file's mtime nanoseconds (so identical content with different mtime\n * — atomic rewrites — still picks up the change). Returns `'missing'`\n * when the file can't be read so the cache key is still deterministic.\n * @param cssPath Absolute CSS path.\n * @returns 16-char hex content hash.\n */\nfunction readThemeHashFor(cssPath: string): string {\n if (!existsSync(cssPath)) return 'missing'\n try {\n const bytes = readFileSync(cssPath)\n const mtime = statSync(cssPath).mtimeMs.toString()\n return createHash('sha256').update(bytes).update(mtime).digest('hex').slice(0, 16)\n } catch {\n return 'missing'\n }\n}\n\n/**\n * Hash a small set of rnwind library files whose changes affect the\n * generated transform output. When the library is rebuilt (workspace\n * dev OR npm install of a new version) the file bytes change, the\n * fingerprint rotates, and Metro's transform cache invalidates.\n *\n * Includes the JSX rewriter (`transform-ast`) alongside the parser /\n * style-builder so a change to the transformer — e.g. renaming the\n * injected context hook — invalidates every stale per-file cache entry\n * on the next dev run. Without this, a user upgrading rnwind in-place\n * would keep loading the old transformed bytes; React-refresh would\n * then preserve fiber state across the version bump and the rendered\n * hook list could shift, surfacing as \"change in the order of Hooks\"\n * runtime errors.\n * Memoised — read once per worker process.\n * @returns 16-char hex fingerprint.\n */\nfunction getLibraryFingerprint(): string {\n if (libraryFingerprint !== undefined) return libraryFingerprint\n const here = path.dirname(__filename)\n const candidates = [\n path.resolve(here, '..', 'core', 'style-builder', 'build-style.mjs'),\n path.resolve(here, '..', 'core', 'style-builder', 'build-style.cjs'),\n path.resolve(here, '..', 'core', 'parser', 'tw-parser.mjs'),\n path.resolve(here, '..', 'core', 'parser', 'tw-parser.cjs'),\n path.resolve(here, 'transform-ast.mjs'),\n path.resolve(here, 'transform-ast.cjs'),\n path.resolve(here, 'transformer.mjs'),\n path.resolve(here, 'transformer.cjs'),\n // Source-tree fallback for tests + workspace dev (no built lib yet).\n path.resolve(here, '..', '..', 'src', 'core', 'style-builder', 'build-style.ts'),\n path.resolve(here, '..', '..', 'src', 'core', 'parser', 'tw-parser.ts'),\n path.resolve(here, '..', '..', 'src', 'metro', 'transform-ast.ts'),\n path.resolve(here, '..', '..', 'src', 'metro', 'transformer.ts'),\n ]\n const hash = createHash('sha256')\n let included = 0\n for (const file of candidates) {\n if (!existsSync(file)) continue\n try {\n hash.update(readFileSync(file))\n included += 1\n } catch {\n // Unreadable file — skip; fingerprint still derives from whatever WE could read.\n }\n }\n libraryFingerprint = included > 0 ? hash.digest('hex').slice(0, 16) : '0'.repeat(16)\n return libraryFingerprint\n}\n\n/**\n * Worker-local state. Lazy-initialised on first access so files that\n * bypass the transform don't pay for construction.\n */\nexport interface RnwindState {\n parser: TailwindParser\n builder: UnionBuilder\n themeCss: string\n themeHash: string\n projectRoot: string\n}\n\n/**\n * Publish the theme CSS path + cache dir to the environment so worker\n * subprocesses (spawned by Metro once `babelTransformerPath` is set)\n * can rebuild the same state without re-reading the Metro config.\n * @param cssEntryFile Absolute path to the user's theme CSS.\n * @param cacheDir Absolute path to the cache dir (`.rnwind`).\n * @param watchFolders\n * @param classNamePrefixes Extra JSX prop-name prefixes to rewrite.\n * @param hostSources\n * @param hostComponents\n */\nexport function configureRnwindState(\n cssEntryFile: string,\n cacheDir: string,\n watchFolders: readonly string[] = [],\n classNamePrefixes?: readonly string[],\n hostSources?: readonly string[],\n hostComponents?: readonly string[],\n): void {\n process.env[CSS_ENTRY_ENV] = cssEntryFile\n process.env[CACHE_DIR_ENV] = cacheDir\n if (watchFolders.length === 0) {\n delete process.env[WATCH_FOLDERS_ENV]\n } else {\n process.env[WATCH_FOLDERS_ENV] = watchFolders.join('\\0')\n }\n if (!classNamePrefixes || classNamePrefixes.length === 0) {\n delete process.env[CLASSNAME_PREFIXES_ENV]\n } else {\n process.env[CLASSNAME_PREFIXES_ENV] = classNamePrefixes.join(',')\n }\n if (!hostSources || hostSources.length === 0) {\n delete process.env[HOST_SOURCES_ENV]\n } else {\n process.env[HOST_SOURCES_ENV] = hostSources.join(',')\n }\n if (!hostComponents || hostComponents.length === 0) {\n delete process.env[HOST_COMPONENTS_ENV]\n } else {\n process.env[HOST_COMPONENTS_ENV] = hostComponents.join(',')\n }\n cached = null\n}\n\n/**\n * Read the caller-configured extra className prefixes out of the\n * worker environment. Returns an empty array when unset — the\n * transformer applies the built-in `contentContainer` default on top\n * either way.\n * @returns User-supplied extra prefixes.\n */\nexport function getClassNamePrefixes(): readonly string[] {\n const raw = process.env[CLASSNAME_PREFIXES_ENV]\n if (!raw || raw.length === 0) return []\n return raw.split(',').filter((entry) => entry.length > 0)\n}\n\n/**\n * Read the caller-configured extra host module sources out of the\n * worker environment. Empty array when unset — the transformer applies\n * its built-in default list on top either way.\n * @returns User-supplied extra host sources.\n */\nexport function getHostSources(): readonly string[] {\n const raw = process.env[HOST_SOURCES_ENV]\n if (!raw || raw.length === 0) return []\n return raw.split(',').filter((entry) => entry.length > 0)\n}\n\n/**\n * Read the caller-configured extra host JSX tag names out of the worker\n * environment. Verbatim names — may include `.` for member expressions\n * like `'Animated.View'`.\n * @returns User-supplied extra host component names.\n */\nexport function getHostComponents(): readonly string[] {\n const raw = process.env[HOST_COMPONENTS_ENV]\n if (!raw || raw.length === 0) return []\n return raw.split(',').filter((entry) => entry.length > 0)\n}\n\n/**\n * Fetch (or build) the worker-local rnwind state. Re-reads the theme\n * CSS hash on every call: if the user edited `global.css` while Metro\n * is running, the cached state is dropped and a fresh parser + ledger\n * is built. Combined with the `getCacheKey()` export on the\n * transformer (which folds the same hash into Metro's per-file cache\n * key) every CSS edit produces a full, correct re-bundle.\n * @param projectRoot\n * @returns The live rnwind state.\n */\nexport function getRnwindState(projectRoot: string): RnwindState {\n const cssEntry = process.env[CSS_ENTRY_ENV]\n const cacheDir = process.env[CACHE_DIR_ENV]\n if (!cssEntry) throw new Error('rnwind: RNWIND_CSS_ENTRY_FILE is not set — did `withRnwindConfig` run?')\n if (!cacheDir) throw new Error('rnwind: RNWIND_CACHE_DIR is not set — did `withRnwindConfig` run?')\n const currentHash = readThemeHashFor(cssEntry)\n if (cached?.themeHash === currentHash && cached.projectRoot === projectRoot) return cached\n const themeCss = readFileSync(cssEntry, 'utf8')\n const parser = new TailwindParser({\n themeCss,\n sources: defaultSources(projectRoot, cacheDir, readWatchFolders()),\n })\n const builder = new UnionBuilder(cacheDir, parser)\n cached = { parser, builder, themeCss, themeHash: currentHash, projectRoot }\n return cached\n}\n\n/**\n * Compute the rnwind cache-key suffix Metro mixes into every per-file\n * transform cache entry via the transformer's `getCacheKey()` export.\n * Includes the CSS path + its current content hash + the rnwind\n * library fingerprint, so any edit to `global.css` OR a library\n * upgrade flips the cache key and forces Metro to re-run the\n * transformer.\n * @returns Deterministic string suitable for appending to Metro's cache key.\n */\nexport function getRnwindCacheKey(): string {\n const cssEntry = process.env[CSS_ENTRY_ENV] ?? ''\n const prefixes = process.env[CLASSNAME_PREFIXES_ENV] ?? ''\n // Host source / component config changes which JSX tags get rewritten,\n // so it MUST flip the cache key — otherwise Metro replays stale\n // transforms (a newly-opted-in host keeps its raw className, a removed\n // one keeps the rewrite).\n const hostSources = process.env[HOST_SOURCES_ENV] ?? ''\n const hostComponents = process.env[HOST_COMPONENTS_ENV] ?? ''\n return `rnwind:${cssEntry}:${readThemeHashFor(cssEntry)}|lib:${getLibraryFingerprint()}|pfx:${prefixes}|hs:${hostSources}|hc:${hostComponents}`\n}\n\n/** Drop the cached state — call after editing the theme CSS. */\nexport function resetRnwindState(): void {\n cached = null\n}\n\n/**\n * Drop cached state, rebuild parser/builder with the fresh CSS, rescan\n * the project, and rewrite every scheme file on disk. This is what\n * `withRnwindConfig`'s CSS file-watcher invokes so `global.css` edits\n * propagate to the app via Metro's HMR — without this, the CSS-as-JS\n * module would re-emit `export {}` whose bytes never change, so Metro\n * would never invalidate downstream modules.\n * @param projectRoot Absolute project root (from `metroConfig.projectRoot`).\n */\nexport async function onThemeChange(projectRoot: string): Promise<void> {\n resetRnwindState()\n const state = getRnwindState(projectRoot)\n await state.builder.writeSchemes()\n}\n\n/**\n * Resolve the on-disk path of the scheme manifest module for the\n * resolver. The manifest eager-imports `common.style.js` and\n * lazy-requires each variant scheme; SchemeProvider calls its\n * `ensureSchemeLoaded` export to trigger per-scheme requires.\n * @returns Absolute path to `<cacheDir>/schemes.js`.\n */\nexport function manifestPathFor(): string {\n const cacheDir = process.env[CACHE_DIR_ENV]\n if (!cacheDir) throw new Error('rnwind: RNWIND_CACHE_DIR is not set')\n return path.join(cacheDir, 'schemes.js')\n}\n"],"names":["existsSync","readFileSync","statSync","createHash","TailwindParser","UnionBuilder"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"state.cjs","sources":["../../../../src/metro/state.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport path from 'node:path'\nimport { createHash } from 'node:crypto'\nimport { UnionBuilder } from '../core/style-builder'\nimport { TailwindParser, type SourceEntry } from '../core/parser'\nimport { resolveThemeCss } from './css-imports'\nimport { buildWrapModules } from './wrap-imports'\n\n/**\n * Default oxide Scanner globs — walk every JS/TS source under the\n * project root AND every monorepo watch folder, excluding\n * `node_modules` and rnwind's own cache dir so we don't rescan\n * generated scheme files.\n *\n * Monorepo layouts (Yarn workspaces, pnpm workspaces, Nx) surface\n * sibling package roots as `metroConfig.watchFolders`. Every folder\n * Metro watches must also be scanned so atoms declared in shared UI\n * packages make it into the union — without this, only the app's\n * own files would be scanned and every UI-package atom would resolve\n * to `undefined` at runtime.\n * @param projectRoot Absolute project root.\n * @param cacheDir Absolute rnwind cache dir (to exclude).\n * @param watchFolders Extra monorepo roots Metro is watching.\n * @returns Scanner sources suitable for `parser.parseProject()`.\n */\nfunction defaultSources(projectRoot: string, cacheDir: string, watchFolders: readonly string[]): readonly SourceEntry[] {\n const cacheBaseName = path.basename(cacheDir)\n const roots = new Set<string>([projectRoot, ...watchFolders])\n const sources: SourceEntry[] = []\n for (const root of roots) {\n sources.push({ base: root, pattern: '**/*.{ts,tsx,js,jsx}', negated: false }, { base: root, pattern: '**/node_modules/**', negated: true }, { base: root, pattern: `**/${cacheBaseName}/**`, negated: true })\n }\n return sources\n}\n\n/**\n * Read monorepo watch-folder paths out of the worker environment.\n * Empty array when the host isn't a monorepo.\n * @returns Absolute paths Metro also watches (sibling packages).\n */\nfunction readWatchFolders(): readonly string[] {\n const raw = process.env[WATCH_FOLDERS_ENV]\n if (!raw || raw.length === 0) return []\n return raw.split('\\0').filter((entry) => entry.length > 0)\n}\n\n/** Env var Metro workers read to locate the theme CSS on disk. */\nconst CSS_ENTRY_ENV = 'RNWIND_CSS_ENTRY_FILE'\n/** Env var Metro workers read to locate the cache directory (`.rnwind`). */\nconst CACHE_DIR_ENV = 'RNWIND_CACHE_DIR'\n/** Env var carrying `watchFolders` from Metro config (NUL-separated). */\nconst WATCH_FOLDERS_ENV = 'RNWIND_WATCH_FOLDERS'\n/** Env var carrying extra modules whose component exports get `wrap()`-ed. Comma-separated. */\nconst WRAP_MODULES_ENV = 'RNWIND_WRAP_MODULES'\n\n/** Memoised library fingerprint — read once per worker process. */\nlet libraryFingerprint: string | undefined\n\n/** Live state shared across one Metro transform worker. */\nlet cached: RnwindState | null = null\n\n/**\n * Cheap content-hash readout. SHA-256 prefix of the FULLY-RESOLVED theme\n * CSS — `@import`s flattened — so an edit to a theme file the entry only\n * re-exports (`@import \"@acme/ui/theme.css\"`) still rotates the hash and\n * invalidates Metro's cache. Returns `'missing'` when the entry can't be\n * read so the cache key stays deterministic.\n * @param cssPath Absolute CSS path.\n * @returns 16-char hex content hash.\n */\nfunction readThemeHashFor(cssPath: string): string {\n if (!existsSync(cssPath)) return 'missing'\n try {\n return createHash('sha256').update(resolveThemeCss(cssPath)).digest('hex').slice(0, 16)\n } catch {\n return 'missing'\n }\n}\n\n/**\n * Hash a small set of rnwind library files whose changes affect the\n * generated transform output. When the library is rebuilt (workspace\n * dev OR npm install of a new version) the file bytes change, the\n * fingerprint rotates, and Metro's transform cache invalidates.\n *\n * Includes the import-rewriter (`wrap-imports`) and runtime resolver\n * alongside the parser / style-builder so a change to the transformer —\n * e.g. renaming the injected wrap helper — invalidates every stale\n * per-file cache entry on the next dev run. Without this, a user upgrading rnwind in-place\n * would keep loading the old transformed bytes; React-refresh would\n * then preserve fiber state across the version bump and the rendered\n * hook list could shift, surfacing as \"change in the order of Hooks\"\n * runtime errors.\n * Memoised — read once per worker process.\n * @returns 16-char hex fingerprint.\n */\nfunction getLibraryFingerprint(): string {\n if (libraryFingerprint !== undefined) return libraryFingerprint\n const here = path.dirname(__filename)\n const candidates = [\n path.resolve(here, '..', 'core', 'style-builder', 'build-style.mjs'),\n path.resolve(here, '..', 'core', 'style-builder', 'build-style.cjs'),\n path.resolve(here, '..', 'core', 'parser', 'tw-parser.mjs'),\n path.resolve(here, '..', 'core', 'parser', 'tw-parser.cjs'),\n path.resolve(here, 'wrap-imports.mjs'),\n path.resolve(here, 'wrap-imports.cjs'),\n path.resolve(here, 'transformer.mjs'),\n path.resolve(here, 'transformer.cjs'),\n // Source-tree fallback for tests + workspace dev (no built lib yet).\n path.resolve(here, '..', '..', 'src', 'core', 'style-builder', 'build-style.ts'),\n path.resolve(here, '..', '..', 'src', 'core', 'parser', 'tw-parser.ts'),\n path.resolve(here, '..', '..', 'src', 'metro', 'wrap-imports.ts'),\n path.resolve(here, '..', '..', 'src', 'metro', 'transformer.ts'),\n ]\n const hash = createHash('sha256')\n let included = 0\n for (const file of candidates) {\n if (!existsSync(file)) continue\n try {\n hash.update(readFileSync(file))\n included += 1\n } catch {\n // Unreadable file — skip; fingerprint still derives from whatever WE could read.\n }\n }\n libraryFingerprint = included > 0 ? hash.digest('hex').slice(0, 16) : '0'.repeat(16)\n return libraryFingerprint\n}\n\n/**\n * Worker-local state. Lazy-initialised on first access so files that\n * bypass the transform don't pay for construction.\n */\nexport interface RnwindState {\n parser: TailwindParser\n builder: UnionBuilder\n themeCss: string\n themeHash: string\n projectRoot: string\n}\n\n/**\n * Publish the theme CSS path + cache dir to the environment so worker\n * subprocesses (spawned by Metro once `babelTransformerPath` is set)\n * can rebuild the same state without re-reading the Metro config.\n * @param cssEntryFile Absolute path to the user's theme CSS.\n * @param cacheDir Absolute path to the cache dir (`.rnwind`).\n * @param watchFolders Monorepo watch folders to scan for atoms.\n * @param wrapModules Extra modules whose component exports get `wrap()`-ed.\n */\nexport function configureRnwindState(\n cssEntryFile: string,\n cacheDir: string,\n watchFolders: readonly string[] = [],\n wrapModules?: readonly string[],\n): void {\n process.env[CSS_ENTRY_ENV] = cssEntryFile\n process.env[CACHE_DIR_ENV] = cacheDir\n if (watchFolders.length === 0) {\n delete process.env[WATCH_FOLDERS_ENV]\n } else {\n process.env[WATCH_FOLDERS_ENV] = watchFolders.join('\\0')\n }\n if (!wrapModules || wrapModules.length === 0) {\n delete process.env[WRAP_MODULES_ENV]\n } else {\n process.env[WRAP_MODULES_ENV] = wrapModules.join(',')\n }\n cached = null\n}\n\n/**\n * Effective module → wrap-policy map: the built-in defaults merged with\n * any extra modules the Metro config supplied.\n * @returns Module → policy map the import-rewrite consults.\n */\nexport function getWrapModules(): ReturnType<typeof buildWrapModules> {\n const raw = process.env[WRAP_MODULES_ENV]\n const extra = raw && raw.length > 0 ? raw.split(',').filter((entry) => entry.length > 0) : undefined\n return buildWrapModules(extra)\n}\n\n/**\n * Fetch (or build) the worker-local rnwind state. Re-reads the theme\n * CSS hash on every call: if the user edited `global.css` while Metro\n * is running, the cached state is dropped and a fresh parser + ledger\n * is built. Combined with the `getCacheKey()` export on the\n * transformer (which folds the same hash into Metro's per-file cache\n * key) every CSS edit produces a full, correct re-bundle.\n * @param projectRoot\n * @returns The live rnwind state.\n */\nexport function getRnwindState(projectRoot: string): RnwindState {\n const cssEntry = process.env[CSS_ENTRY_ENV]\n const cacheDir = process.env[CACHE_DIR_ENV]\n if (!cssEntry) throw new Error('rnwind: RNWIND_CSS_ENTRY_FILE is not set — did `withRnwindConfig` run?')\n if (!cacheDir) throw new Error('rnwind: RNWIND_CACHE_DIR is not set — did `withRnwindConfig` run?')\n const currentHash = readThemeHashFor(cssEntry)\n if (cached?.themeHash === currentHash && cached.projectRoot === projectRoot) return cached\n const themeCss = resolveThemeCss(cssEntry)\n const parser = new TailwindParser({\n themeCss,\n sources: defaultSources(projectRoot, cacheDir, readWatchFolders()),\n })\n const builder = new UnionBuilder(cacheDir, parser)\n cached = { parser, builder, themeCss, themeHash: currentHash, projectRoot }\n return cached\n}\n\n/**\n * Compute the rnwind cache-key suffix Metro mixes into every per-file\n * transform cache entry via the transformer's `getCacheKey()` export.\n * Includes the CSS path + its current content hash + the rnwind\n * library fingerprint, so any edit to `global.css` OR a library\n * upgrade flips the cache key and forces Metro to re-run the\n * transformer.\n * @returns Deterministic string suitable for appending to Metro's cache key.\n */\nexport function getRnwindCacheKey(): string {\n const cssEntry = process.env[CSS_ENTRY_ENV] ?? ''\n // Wrap-module config changes which import sites get `wrap()`-ed, so it\n // MUST flip the cache key — otherwise Metro replays stale transforms\n // (a newly-opted-in module keeps its raw import, a removed one keeps\n // the wrap).\n const wrapModules = process.env[WRAP_MODULES_ENV] ?? ''\n return `rnwind:${cssEntry}:${readThemeHashFor(cssEntry)}|lib:${getLibraryFingerprint()}|wm:${wrapModules}`\n}\n\n/** Drop the cached state — call after editing the theme CSS. */\nexport function resetRnwindState(): void {\n cached = null\n}\n\n/**\n * Drop cached state, rebuild parser/builder with the fresh CSS, rescan\n * the project, and rewrite every scheme file on disk. This is what\n * `withRnwindConfig`'s CSS file-watcher invokes so `global.css` edits\n * propagate to the app via Metro's HMR — without this, the CSS-as-JS\n * module would re-emit `export {}` whose bytes never change, so Metro\n * would never invalidate downstream modules.\n * @param projectRoot Absolute project root (from `metroConfig.projectRoot`).\n */\nexport async function onThemeChange(projectRoot: string): Promise<void> {\n resetRnwindState()\n const state = getRnwindState(projectRoot)\n await state.builder.writeSchemes()\n}\n\n/**\n * Resolve the on-disk path of the scheme manifest module for the\n * resolver. The manifest eager-imports `common.style.js` and\n * lazy-requires each variant scheme; SchemeProvider calls its\n * `ensureSchemeLoaded` export to trigger per-scheme requires.\n * @returns Absolute path to `<cacheDir>/schemes.js`.\n */\nexport function manifestPathFor(): string {\n const cacheDir = process.env[CACHE_DIR_ENV]\n if (!cacheDir) throw new Error('rnwind: RNWIND_CACHE_DIR is not set')\n return path.join(cacheDir, 'schemes.js')\n}\n"],"names":["existsSync","createHash","resolveThemeCss","readFileSync","buildWrapModules","TailwindParser","UnionBuilder"],"mappings":"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"}
|
package/lib/cjs/metro/state.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { UnionBuilder } from '../core/style-builder';
|
|
2
2
|
import { TailwindParser } from '../core/parser';
|
|
3
|
+
import { buildWrapModules } from './wrap-imports';
|
|
3
4
|
/**
|
|
4
5
|
* Worker-local state. Lazy-initialised on first access so files that
|
|
5
6
|
* bypass the transform don't pay for construction.
|
|
@@ -17,34 +18,16 @@ export interface RnwindState {
|
|
|
17
18
|
* can rebuild the same state without re-reading the Metro config.
|
|
18
19
|
* @param cssEntryFile Absolute path to the user's theme CSS.
|
|
19
20
|
* @param cacheDir Absolute path to the cache dir (`.rnwind`).
|
|
20
|
-
* @param watchFolders
|
|
21
|
-
* @param
|
|
22
|
-
* @param hostSources
|
|
23
|
-
* @param hostComponents
|
|
21
|
+
* @param watchFolders Monorepo watch folders to scan for atoms.
|
|
22
|
+
* @param wrapModules Extra modules whose component exports get `wrap()`-ed.
|
|
24
23
|
*/
|
|
25
|
-
export declare function configureRnwindState(cssEntryFile: string, cacheDir: string, watchFolders?: readonly string[],
|
|
24
|
+
export declare function configureRnwindState(cssEntryFile: string, cacheDir: string, watchFolders?: readonly string[], wrapModules?: readonly string[]): void;
|
|
26
25
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* either way.
|
|
31
|
-
* @returns User-supplied extra prefixes.
|
|
26
|
+
* Effective module → wrap-policy map: the built-in defaults merged with
|
|
27
|
+
* any extra modules the Metro config supplied.
|
|
28
|
+
* @returns Module → policy map the import-rewrite consults.
|
|
32
29
|
*/
|
|
33
|
-
export declare function
|
|
34
|
-
/**
|
|
35
|
-
* Read the caller-configured extra host module sources out of the
|
|
36
|
-
* worker environment. Empty array when unset — the transformer applies
|
|
37
|
-
* its built-in default list on top either way.
|
|
38
|
-
* @returns User-supplied extra host sources.
|
|
39
|
-
*/
|
|
40
|
-
export declare function getHostSources(): readonly string[];
|
|
41
|
-
/**
|
|
42
|
-
* Read the caller-configured extra host JSX tag names out of the worker
|
|
43
|
-
* environment. Verbatim names — may include `.` for member expressions
|
|
44
|
-
* like `'Animated.View'`.
|
|
45
|
-
* @returns User-supplied extra host component names.
|
|
46
|
-
*/
|
|
47
|
-
export declare function getHostComponents(): readonly string[];
|
|
30
|
+
export declare function getWrapModules(): ReturnType<typeof buildWrapModules>;
|
|
48
31
|
/**
|
|
49
32
|
* Fetch (or build) the worker-local rnwind state. Re-reads the theme
|
|
50
33
|
* CSS hash on every call: if the user edited `global.css` while Metro
|
|
@@ -5,8 +5,8 @@ var parser = require('@babel/parser');
|
|
|
5
5
|
var generate = require('@babel/generator');
|
|
6
6
|
var node_crypto = require('node:crypto');
|
|
7
7
|
var node_fs = require('node:fs');
|
|
8
|
-
var transformAst = require('./transform-ast.cjs');
|
|
9
8
|
var state = require('./state.cjs');
|
|
9
|
+
var wrapImports = require('./wrap-imports.cjs');
|
|
10
10
|
var resolver = require('./resolver.cjs');
|
|
11
11
|
var warnUnknownClasses$1 = require('./warn-unknown-classes.cjs');
|
|
12
12
|
|
|
@@ -75,10 +75,19 @@ function parseUserSource(source) {
|
|
|
75
75
|
* @param candidates Every candidate oxide surfaced from the source.
|
|
76
76
|
* @param atoms Successfully resolved atoms (keys are class names).
|
|
77
77
|
* @param filename Source path, prefixed onto the warning.
|
|
78
|
+
* @param features Feature-atom maps (gradient / haptic) — their names are
|
|
79
|
+
* known classes even though they carry no RN style, so they're excluded
|
|
80
|
+
* from the unknown-class warning.
|
|
78
81
|
*/
|
|
79
|
-
function warnUnknownClasses(source, candidates, atoms, filename) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
function warnUnknownClasses(source, candidates, atoms, filename, features = []) {
|
|
83
|
+
// Feature atoms (gradient / haptic) resolve to no RN style, so they're
|
|
84
|
+
// absent from `atoms` — but they're NOT unknown. Fold their names into
|
|
85
|
+
// the known set so `active:haptic-rigid` etc. don't warn at build time.
|
|
86
|
+
const known = new Set(atoms.keys());
|
|
87
|
+
for (const map of features)
|
|
88
|
+
for (const name of map.keys())
|
|
89
|
+
known.add(name);
|
|
90
|
+
const unknown = warnUnknownClasses$1.filterUnknownClassCandidates(source, candidates, known);
|
|
82
91
|
if (unknown.length === 0)
|
|
83
92
|
return;
|
|
84
93
|
// eslint-disable-next-line no-console
|
|
@@ -121,52 +130,181 @@ function isThemeCssEntry(filename) {
|
|
|
121
130
|
return typeof cssEntry === 'string' && cssEntry.length > 0 && cssEntry === filename;
|
|
122
131
|
}
|
|
123
132
|
/**
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
133
|
+
* Wrap host imports + compile any className literals, then regenerate
|
|
134
|
+
* source. Two paths:
|
|
135
|
+
* - **className present**: oxide-scan the file, record its atoms into
|
|
136
|
+
* the union, and inject the generated-style + theme-signature
|
|
137
|
+
* side-effect imports so the runtime registries populate.
|
|
138
|
+
* - **import-only** (a `{...rest}` forwarder or a leaf with no literal
|
|
139
|
+
* `className=`): just wrap the host imports so a forwarded className
|
|
140
|
+
* still resolves at render — no oxide scan, no injected imports.
|
|
141
|
+
*
|
|
142
|
+
* On parse failure, fall back to the original source — a transient parse
|
|
143
|
+
* error shouldn't crash Metro for a file the upstream might handle fine.
|
|
128
144
|
* @param args Metro args; `src` is the original source text.
|
|
129
|
-
* @returns Rewritten source text
|
|
145
|
+
* @returns Rewritten source text.
|
|
130
146
|
*/
|
|
131
147
|
async function rewriteSource(args) {
|
|
132
148
|
const ast = parseUserSource(args.src);
|
|
133
149
|
if (!ast)
|
|
134
150
|
return args.src;
|
|
151
|
+
// Wrap host component imports so `<View className=…>` resolves at render
|
|
152
|
+
// through the runtime `wrap` (works for literal, spread, and forwarded
|
|
153
|
+
// classNames alike). No JSX is rewritten here.
|
|
154
|
+
const wrapped = wrapImports.rewriteWrapImports(ast, state.getWrapModules());
|
|
155
|
+
if (!/classname=/i.test(args.src)) {
|
|
156
|
+
// Import-only file: nothing to compile. Drop any stale atom
|
|
157
|
+
// contribution (className may have just been removed) and emit the
|
|
158
|
+
// wrapped imports — or the untouched source when nothing wrapped.
|
|
159
|
+
dropFileSafely(args.filename, projectRootOf(args));
|
|
160
|
+
return wrapped ? generateModule(ast).code : args.src;
|
|
161
|
+
}
|
|
135
162
|
const state$1 = state.getRnwindState(projectRootOf(args));
|
|
136
163
|
const extension = extensionOf(args.filename);
|
|
137
164
|
const parsed = await state$1.parser.parseAtoms({ content: args.src, extension });
|
|
138
|
-
warnUnknownClasses(args.src, parsed.candidates, parsed.atoms, args.filename);
|
|
139
|
-
const classNamePrefixes = state.getClassNamePrefixes();
|
|
140
|
-
const hostSources = state.getHostSources();
|
|
141
|
-
const hostComponents = state.getHostComponents();
|
|
165
|
+
warnUnknownClasses(args.src, parsed.candidates, parsed.atoms, args.filename, [parsed.gradientAtoms, parsed.hapticAtoms]);
|
|
142
166
|
if (parsed.atoms.size === 0) {
|
|
143
167
|
state$1.builder.dropFile(args.filename);
|
|
144
168
|
await state$1.builder.writeSchemes();
|
|
145
|
-
transformAst.transformAst(ast, {
|
|
146
|
-
styleSpecifiers: [],
|
|
147
|
-
gradientAtoms: parsed.gradientAtoms,
|
|
148
|
-
hapticAtoms: parsed.hapticAtoms,
|
|
149
|
-
classNamePrefixes,
|
|
150
|
-
hostSources,
|
|
151
|
-
hostComponents,
|
|
152
|
-
});
|
|
153
169
|
injectThemeSignatureImport(ast);
|
|
154
170
|
return generateModule(ast).code;
|
|
155
171
|
}
|
|
156
|
-
const
|
|
172
|
+
const literals = collectClassNameLiterals(ast);
|
|
173
|
+
const { changed } = await state$1.builder.recordFile(args.filename, parsed.atoms, parsed.keyframes, literals);
|
|
157
174
|
if (changed)
|
|
158
175
|
await state$1.builder.writeSchemes();
|
|
159
|
-
|
|
160
|
-
styleSpecifiers: resolver.STYLE_SPECIFIERS,
|
|
161
|
-
gradientAtoms: parsed.gradientAtoms,
|
|
162
|
-
hapticAtoms: parsed.hapticAtoms,
|
|
163
|
-
classNamePrefixes,
|
|
164
|
-
hostSources,
|
|
165
|
-
hostComponents,
|
|
166
|
-
});
|
|
176
|
+
injectSideEffectImports(ast, resolver.STYLE_SPECIFIERS);
|
|
167
177
|
injectThemeSignatureImport(ast);
|
|
168
178
|
return generateModule(ast).code;
|
|
169
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Drop a file's union contribution, swallowing the "state not configured"
|
|
182
|
+
* error unit tests hit when they call the transformer without
|
|
183
|
+
* `configureRnwindState`.
|
|
184
|
+
* @param filename Absolute source path.
|
|
185
|
+
* @param projectRoot Project root for state lookup.
|
|
186
|
+
*/
|
|
187
|
+
function dropFileSafely(filename, projectRoot) {
|
|
188
|
+
try {
|
|
189
|
+
state.getRnwindState(projectRoot).builder.dropFile(filename);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// State not configured (standalone/unit test). Nothing to drop.
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Whether a JSX attribute names a className-style prop (`className` or
|
|
197
|
+
* any `<prefix>ClassName`).
|
|
198
|
+
* @param node JSX attribute node.
|
|
199
|
+
* @returns True when the attribute is a className prop.
|
|
200
|
+
*/
|
|
201
|
+
function isClassNameAttribute(node) {
|
|
202
|
+
if (!t__namespace.isJSXIdentifier(node.name))
|
|
203
|
+
return false;
|
|
204
|
+
const { name } = node.name;
|
|
205
|
+
return name === 'className' || name.endsWith('ClassName');
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Pull static string literals out of a className expression. Handles a
|
|
209
|
+
* bare string, a no-substitution template, and the branches of a
|
|
210
|
+
* ternary / `&&` (so `cond ? 'a' : 'b'` and `flag && 'x'` both register
|
|
211
|
+
* their literals). Dynamic interpolations are skipped — they resolve via
|
|
212
|
+
* the runtime atom path.
|
|
213
|
+
* @param expr Expression inside a `className={...}` container.
|
|
214
|
+
* @param out Accumulator for discovered literals.
|
|
215
|
+
*/
|
|
216
|
+
function collectLiteralsFromExpression(expr, out) {
|
|
217
|
+
if (!expr)
|
|
218
|
+
return;
|
|
219
|
+
if (t__namespace.isStringLiteral(expr)) {
|
|
220
|
+
out.push(expr.value);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (t__namespace.isTemplateLiteral(expr) && expr.expressions.length === 0 && expr.quasis.length === 1) {
|
|
224
|
+
const cooked = expr.quasis[0]?.value.cooked;
|
|
225
|
+
if (typeof cooked === 'string')
|
|
226
|
+
out.push(cooked);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (t__namespace.isConditionalExpression(expr)) {
|
|
230
|
+
collectLiteralsFromExpression(expr.consequent, out);
|
|
231
|
+
collectLiteralsFromExpression(expr.alternate, out);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (t__namespace.isLogicalExpression(expr)) {
|
|
235
|
+
collectLiteralsFromExpression(expr.right, out);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/** AST node keys the literal walk skips — position / comment metadata. */
|
|
239
|
+
const SKIP_WALK_KEYS = new Set(['type', 'loc', 'start', 'end', 'range', 'leadingComments', 'trailingComments', 'innerComments']);
|
|
240
|
+
/**
|
|
241
|
+
* Collect the static literals from one className JSX attribute into the
|
|
242
|
+
* dedup accumulator.
|
|
243
|
+
* @param attribute The (already className-matched) JSX attribute.
|
|
244
|
+
* @param seen Dedup set of literals already collected.
|
|
245
|
+
* @param out Ordered accumulator.
|
|
246
|
+
*/
|
|
247
|
+
function collectAttributeLiterals(attribute, seen, out) {
|
|
248
|
+
const { value } = attribute;
|
|
249
|
+
const found = [];
|
|
250
|
+
if (t__namespace.isStringLiteral(value))
|
|
251
|
+
found.push(value.value);
|
|
252
|
+
else if (t__namespace.isJSXExpressionContainer(value))
|
|
253
|
+
collectLiteralsFromExpression(value.expression, found);
|
|
254
|
+
for (const literal of found) {
|
|
255
|
+
if (seen.has(literal))
|
|
256
|
+
continue;
|
|
257
|
+
seen.add(literal);
|
|
258
|
+
out.push(literal);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Walk the AST for every `className=` / `<prefix>ClassName=` literal so
|
|
263
|
+
* the builder can pre-merge each into a per-scheme molecule. A generic
|
|
264
|
+
* node walk (no scope build) keeps it cheap; only JSX attribute nodes do
|
|
265
|
+
* any work.
|
|
266
|
+
* @param ast Parsed Babel file.
|
|
267
|
+
* @returns Distinct literal className strings, in first-seen order.
|
|
268
|
+
*/
|
|
269
|
+
function collectClassNameLiterals(ast) {
|
|
270
|
+
const out = [];
|
|
271
|
+
const seen = new Set();
|
|
272
|
+
const visit = (node) => {
|
|
273
|
+
if (!node || typeof node !== 'object')
|
|
274
|
+
return;
|
|
275
|
+
if (Array.isArray(node)) {
|
|
276
|
+
for (const child of node)
|
|
277
|
+
visit(child);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const typed = node;
|
|
281
|
+
if (typeof typed.type !== 'string')
|
|
282
|
+
return;
|
|
283
|
+
if (typed.type === 'JSXAttribute' && isClassNameAttribute(node)) {
|
|
284
|
+
collectAttributeLiterals(node, seen, out);
|
|
285
|
+
}
|
|
286
|
+
for (const key in typed) {
|
|
287
|
+
if (SKIP_WALK_KEYS.has(key))
|
|
288
|
+
continue;
|
|
289
|
+
visit(typed[key]);
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
visit(ast.program);
|
|
293
|
+
return out;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Prepend side-effect imports (`import '<spec>'`) so the generated
|
|
297
|
+
* per-scheme style + manifest modules load — registering this file's
|
|
298
|
+
* atoms / molecules / features into the runtime registries the wrapper's
|
|
299
|
+
* `resolve` reads.
|
|
300
|
+
* @param ast Babel File AST to mutate in place.
|
|
301
|
+
* @param specifiers Module specifiers to side-effect-import.
|
|
302
|
+
*/
|
|
303
|
+
function injectSideEffectImports(ast, specifiers) {
|
|
304
|
+
for (const specifier of specifiers) {
|
|
305
|
+
ast.program.body.unshift(t__namespace.importDeclaration([], t__namespace.stringLiteral(specifier)));
|
|
306
|
+
}
|
|
307
|
+
}
|
|
170
308
|
/**
|
|
171
309
|
* Prepend `import 'rnwind/__generated/theme-signature'` to every
|
|
172
310
|
* rnwind-transformed file. The resolver maps that specifier to the
|
|
@@ -262,10 +400,16 @@ function loadUpstream() {
|
|
|
262
400
|
function isRewriteCandidate(args) {
|
|
263
401
|
if (!/\.(?:tsx|ts|jsx|js)$/i.test(args.filename))
|
|
264
402
|
return false;
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
// `
|
|
268
|
-
|
|
403
|
+
// Process the file when it either:
|
|
404
|
+
// - carries a `className=` / `<prefix>ClassName=` literal (case-
|
|
405
|
+
// insensitive — `contentContainerClassName=` has a capital C), or
|
|
406
|
+
// - spreads props (`{...rest}`) onto a host from a wrap-module, where a
|
|
407
|
+
// forwarded className must still get its import wrapped (no literal
|
|
408
|
+
// appears in this file). A style-less `<View/>` with neither is left
|
|
409
|
+
// alone so it never pays for an unused wrapper.
|
|
410
|
+
const hasClassName = /classname=/i.test(args.src);
|
|
411
|
+
const isForwarder = /\{\s*\.\.\./.test(args.src) && mentionsWrapModule(args.src);
|
|
412
|
+
if (!hasClassName && !isForwarder)
|
|
269
413
|
return false;
|
|
270
414
|
if (!args.filename.includes('/node_modules/'))
|
|
271
415
|
return true;
|
|
@@ -278,6 +422,21 @@ function isRewriteCandidate(args) {
|
|
|
278
422
|
return false;
|
|
279
423
|
}
|
|
280
424
|
}
|
|
425
|
+
/**
|
|
426
|
+
* Cheap pre-parse check: does the source import from any configured
|
|
427
|
+
* wrap-module? A quoted specifier match is enough — `rewriteWrapImports`
|
|
428
|
+
* re-verifies precisely on the AST, so a false positive only costs a
|
|
429
|
+
* no-op parse.
|
|
430
|
+
* @param source Source text.
|
|
431
|
+
* @returns True when a wrap-module specifier appears in the source.
|
|
432
|
+
*/
|
|
433
|
+
function mentionsWrapModule(source) {
|
|
434
|
+
for (const moduleName of state.getWrapModules().keys()) {
|
|
435
|
+
if (source.includes(`'${moduleName}'`) || source.includes(`"${moduleName}"`))
|
|
436
|
+
return true;
|
|
437
|
+
}
|
|
438
|
+
return false;
|
|
439
|
+
}
|
|
281
440
|
/**
|
|
282
441
|
* Fallback parse when no upstream is configured AND Metro didn't hand
|
|
283
442
|
* us an AST. Used by unit tests and standalone setups.
|