rnwind 0.0.1 → 0.0.3
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/parser/animation.cjs +427 -0
- package/lib/cjs/core/parser/animation.cjs.map +1 -0
- package/lib/cjs/core/parser/animation.d.ts +126 -0
- package/lib/cjs/core/parser/border-dispatcher.cjs +180 -0
- package/lib/cjs/core/parser/border-dispatcher.cjs.map +1 -0
- package/lib/cjs/core/parser/border-dispatcher.d.ts +15 -0
- package/lib/cjs/core/parser/case-convert.cjs +15 -0
- package/lib/cjs/core/parser/case-convert.cjs.map +1 -0
- package/lib/cjs/core/parser/case-convert.d.ts +6 -0
- package/lib/cjs/core/parser/color-properties-dispatcher.cjs +84 -0
- package/lib/cjs/core/parser/color-properties-dispatcher.cjs.map +1 -0
- package/lib/cjs/core/parser/color-properties-dispatcher.d.ts +19 -0
- package/lib/cjs/core/parser/color.cjs +193 -0
- package/lib/cjs/core/parser/color.cjs.map +1 -0
- package/lib/cjs/core/parser/color.d.ts +12 -0
- package/lib/cjs/core/parser/constants.cjs +21 -0
- package/lib/cjs/core/parser/constants.cjs.map +1 -0
- package/lib/cjs/core/parser/constants.d.ts +8 -0
- package/lib/cjs/core/parser/declaration.cjs +347 -0
- package/lib/cjs/core/parser/declaration.cjs.map +1 -0
- package/lib/cjs/core/parser/declaration.d.ts +15 -0
- package/lib/cjs/core/parser/gradient.cjs +132 -0
- package/lib/cjs/core/parser/gradient.cjs.map +1 -0
- package/lib/cjs/core/parser/gradient.d.ts +59 -0
- package/lib/cjs/core/parser/haptics.cjs +73 -0
- package/lib/cjs/core/parser/haptics.cjs.map +1 -0
- package/lib/cjs/core/parser/haptics.d.ts +47 -0
- package/lib/cjs/core/parser/index.d.ts +8 -0
- package/lib/cjs/core/parser/keyframes.cjs +95 -0
- package/lib/cjs/core/parser/keyframes.cjs.map +1 -0
- package/lib/cjs/core/parser/keyframes.d.ts +26 -0
- package/lib/cjs/core/parser/layout-dispatcher.cjs +120 -0
- package/lib/cjs/core/parser/layout-dispatcher.cjs.map +1 -0
- package/lib/cjs/core/parser/layout-dispatcher.d.ts +14 -0
- package/lib/cjs/core/parser/length.cjs +110 -0
- package/lib/cjs/core/parser/length.cjs.map +1 -0
- package/lib/cjs/core/parser/length.d.ts +51 -0
- package/lib/cjs/core/parser/motion-dispatcher.cjs +77 -0
- package/lib/cjs/core/parser/motion-dispatcher.cjs.map +1 -0
- package/lib/cjs/core/parser/motion-dispatcher.d.ts +11 -0
- package/lib/cjs/core/parser/property.cjs +22 -0
- package/lib/cjs/core/parser/property.cjs.map +1 -0
- package/lib/cjs/core/parser/property.d.ts +8 -0
- package/lib/cjs/core/parser/safe-area.cjs +404 -0
- package/lib/cjs/core/parser/safe-area.cjs.map +1 -0
- package/lib/cjs/core/parser/safe-area.d.ts +39 -0
- package/lib/cjs/core/parser/selector.cjs +22 -0
- package/lib/cjs/core/parser/selector.cjs.map +1 -0
- package/lib/cjs/core/parser/selector.d.ts +11 -0
- package/lib/cjs/core/parser/shorthand.cjs +188 -0
- package/lib/cjs/core/parser/shorthand.cjs.map +1 -0
- package/lib/cjs/core/parser/shorthand.d.ts +67 -0
- package/lib/cjs/core/parser/text-truncate.cjs +78 -0
- package/lib/cjs/core/parser/text-truncate.cjs.map +1 -0
- package/lib/cjs/core/parser/text-truncate.d.ts +44 -0
- package/lib/cjs/core/parser/theme-vars.cjs +467 -0
- package/lib/cjs/core/parser/theme-vars.cjs.map +1 -0
- package/lib/cjs/core/parser/theme-vars.d.ts +82 -0
- package/lib/cjs/core/parser/tokens.cjs +486 -0
- package/lib/cjs/core/parser/tokens.cjs.map +1 -0
- package/lib/cjs/core/parser/tokens.d.ts +45 -0
- package/lib/cjs/core/parser/transform.cjs +198 -0
- package/lib/cjs/core/parser/transform.cjs.map +1 -0
- package/lib/cjs/core/parser/transform.d.ts +36 -0
- package/lib/cjs/core/parser/tw-parser.cjs +1680 -0
- package/lib/cjs/core/parser/tw-parser.cjs.map +1 -0
- package/lib/cjs/core/parser/tw-parser.d.ts +210 -0
- package/lib/cjs/core/parser/types.d.ts +37 -0
- package/lib/cjs/core/parser/typography-dispatcher.cjs +108 -0
- package/lib/cjs/core/parser/typography-dispatcher.cjs.map +1 -0
- package/lib/cjs/core/parser/typography-dispatcher.d.ts +11 -0
- package/lib/cjs/core/parser/typography.cjs +97 -0
- package/lib/cjs/core/parser/typography.cjs.map +1 -0
- package/lib/cjs/core/parser/typography.d.ts +43 -0
- package/lib/cjs/core/style-builder/build-style.cjs +444 -0
- package/lib/cjs/core/style-builder/build-style.cjs.map +1 -0
- package/lib/cjs/core/style-builder/build-style.d.ts +54 -0
- package/lib/cjs/core/style-builder/index.d.ts +3 -0
- package/lib/cjs/core/style-builder/union-builder.cjs +326 -0
- package/lib/cjs/core/style-builder/union-builder.cjs.map +1 -0
- package/lib/cjs/core/style-builder/union-builder.d.ts +128 -0
- package/lib/cjs/core/types.d.ts +14 -0
- package/lib/cjs/metro/dts.cjs +127 -0
- package/lib/cjs/metro/dts.cjs.map +1 -0
- package/lib/cjs/metro/dts.d.ts +16 -0
- package/lib/cjs/metro/index.cjs +19 -0
- package/lib/cjs/metro/index.cjs.map +1 -0
- package/lib/cjs/metro/index.d.ts +9 -0
- package/lib/cjs/metro/resolver.cjs +47 -0
- package/lib/cjs/metro/resolver.cjs.map +1 -0
- package/lib/cjs/metro/resolver.d.ts +22 -0
- package/lib/cjs/metro/state.cjs +301 -0
- package/lib/cjs/metro/state.cjs.map +1 -0
- package/lib/cjs/metro/state.d.ts +88 -0
- package/lib/cjs/metro/transform-ast.cjs +1472 -0
- package/lib/cjs/metro/transform-ast.cjs.map +1 -0
- package/lib/cjs/metro/transform-ast.d.ts +88 -0
- package/lib/cjs/metro/transformer.cjs +372 -0
- package/lib/cjs/metro/transformer.cjs.map +1 -0
- package/lib/cjs/metro/transformer.d.ts +47 -0
- package/lib/cjs/metro/warn-unknown-classes.cjs +86 -0
- package/lib/cjs/metro/warn-unknown-classes.cjs.map +1 -0
- package/lib/cjs/metro/warn-unknown-classes.d.ts +21 -0
- package/lib/cjs/metro/with-config.cjs +196 -0
- package/lib/cjs/metro/with-config.cjs.map +1 -0
- package/lib/cjs/metro/with-config.d.ts +79 -0
- package/lib/cjs/runtime/chain-handlers.cjs +37 -0
- package/lib/cjs/runtime/chain-handlers.cjs.map +1 -0
- package/lib/cjs/runtime/chain-handlers.d.ts +33 -0
- package/lib/cjs/runtime/components/rnwind-provider.cjs +98 -0
- package/lib/cjs/runtime/components/rnwind-provider.cjs.map +1 -0
- package/lib/cjs/runtime/components/rnwind-provider.d.ts +84 -0
- package/lib/cjs/runtime/gradient-types.d.ts +58 -0
- package/lib/cjs/runtime/haptics.cjs +113 -0
- package/lib/cjs/runtime/haptics.cjs.map +1 -0
- package/lib/cjs/runtime/haptics.d.ts +48 -0
- package/lib/cjs/runtime/hooks/use-css.cjs +21 -0
- package/lib/cjs/runtime/hooks/use-css.cjs.map +1 -0
- package/lib/cjs/runtime/hooks/use-css.d.ts +11 -0
- package/lib/cjs/runtime/hooks/use-interact.cjs +46 -0
- package/lib/cjs/runtime/hooks/use-interact.cjs.map +1 -0
- package/lib/cjs/runtime/hooks/use-interact.d.ts +42 -0
- package/lib/cjs/runtime/hooks/use-scheme.cjs +68 -0
- package/lib/cjs/runtime/hooks/use-scheme.cjs.map +1 -0
- package/lib/cjs/runtime/hooks/use-scheme.d.ts +34 -0
- package/lib/cjs/runtime/index.cjs +45 -0
- package/lib/cjs/runtime/index.cjs.map +1 -0
- package/lib/cjs/runtime/index.d.ts +27 -0
- package/lib/cjs/runtime/interactive-box.cjs +35 -0
- package/lib/cjs/runtime/interactive-box.cjs.map +1 -0
- package/lib/cjs/runtime/interactive-box.d.ts +40 -0
- package/lib/cjs/runtime/lookup-css.cjs +542 -0
- package/lib/cjs/runtime/lookup-css.cjs.map +1 -0
- package/lib/cjs/runtime/lookup-css.d.ts +164 -0
- package/lib/cjs/runtime/types.d.ts +29 -0
- package/lib/cjs/testing/index.cjs +367 -0
- package/lib/cjs/testing/index.cjs.map +1 -0
- package/lib/cjs/testing/index.d.ts +145 -0
- package/lib/esm/core/parser/animation.d.ts +126 -0
- package/lib/esm/core/parser/animation.mjs +408 -0
- package/lib/esm/core/parser/animation.mjs.map +1 -0
- package/lib/esm/core/parser/border-dispatcher.d.ts +15 -0
- package/lib/esm/core/parser/border-dispatcher.mjs +178 -0
- package/lib/esm/core/parser/border-dispatcher.mjs.map +1 -0
- package/lib/esm/core/parser/case-convert.d.ts +6 -0
- package/lib/esm/core/parser/case-convert.mjs +13 -0
- package/lib/esm/core/parser/case-convert.mjs.map +1 -0
- package/lib/esm/core/parser/color-properties-dispatcher.d.ts +19 -0
- package/lib/esm/core/parser/color-properties-dispatcher.mjs +82 -0
- package/lib/esm/core/parser/color-properties-dispatcher.mjs.map +1 -0
- package/lib/esm/core/parser/color.d.ts +12 -0
- package/lib/esm/core/parser/color.mjs +191 -0
- package/lib/esm/core/parser/color.mjs.map +1 -0
- package/lib/esm/core/parser/constants.d.ts +8 -0
- package/lib/esm/core/parser/constants.mjs +13 -0
- package/lib/esm/core/parser/constants.mjs.map +1 -0
- package/lib/esm/core/parser/declaration.d.ts +15 -0
- package/lib/esm/core/parser/declaration.mjs +345 -0
- package/lib/esm/core/parser/declaration.mjs.map +1 -0
- package/lib/esm/core/parser/gradient.d.ts +59 -0
- package/lib/esm/core/parser/gradient.mjs +130 -0
- package/lib/esm/core/parser/gradient.mjs.map +1 -0
- package/lib/esm/core/parser/haptics.d.ts +47 -0
- package/lib/esm/core/parser/haptics.mjs +71 -0
- package/lib/esm/core/parser/haptics.mjs.map +1 -0
- package/lib/esm/core/parser/index.d.ts +8 -0
- package/lib/esm/core/parser/keyframes.d.ts +26 -0
- package/lib/esm/core/parser/keyframes.mjs +91 -0
- package/lib/esm/core/parser/keyframes.mjs.map +1 -0
- package/lib/esm/core/parser/layout-dispatcher.d.ts +14 -0
- package/lib/esm/core/parser/layout-dispatcher.mjs +118 -0
- package/lib/esm/core/parser/layout-dispatcher.mjs.map +1 -0
- package/lib/esm/core/parser/length.d.ts +51 -0
- package/lib/esm/core/parser/length.mjs +104 -0
- package/lib/esm/core/parser/length.mjs.map +1 -0
- package/lib/esm/core/parser/motion-dispatcher.d.ts +11 -0
- package/lib/esm/core/parser/motion-dispatcher.mjs +75 -0
- package/lib/esm/core/parser/motion-dispatcher.mjs.map +1 -0
- package/lib/esm/core/parser/property.d.ts +8 -0
- package/lib/esm/core/parser/property.mjs +20 -0
- package/lib/esm/core/parser/property.mjs.map +1 -0
- package/lib/esm/core/parser/safe-area.d.ts +39 -0
- package/lib/esm/core/parser/safe-area.mjs +402 -0
- package/lib/esm/core/parser/safe-area.mjs.map +1 -0
- package/lib/esm/core/parser/selector.d.ts +11 -0
- package/lib/esm/core/parser/selector.mjs +20 -0
- package/lib/esm/core/parser/selector.mjs.map +1 -0
- package/lib/esm/core/parser/shorthand.d.ts +67 -0
- package/lib/esm/core/parser/shorthand.mjs +180 -0
- package/lib/esm/core/parser/shorthand.mjs.map +1 -0
- package/lib/esm/core/parser/text-truncate.d.ts +44 -0
- package/lib/esm/core/parser/text-truncate.mjs +75 -0
- package/lib/esm/core/parser/text-truncate.mjs.map +1 -0
- package/lib/esm/core/parser/theme-vars.d.ts +82 -0
- package/lib/esm/core/parser/theme-vars.mjs +461 -0
- package/lib/esm/core/parser/theme-vars.mjs.map +1 -0
- package/lib/esm/core/parser/tokens.d.ts +45 -0
- package/lib/esm/core/parser/tokens.mjs +480 -0
- package/lib/esm/core/parser/tokens.mjs.map +1 -0
- package/lib/esm/core/parser/transform.d.ts +36 -0
- package/lib/esm/core/parser/transform.mjs +193 -0
- package/lib/esm/core/parser/transform.mjs.map +1 -0
- package/lib/esm/core/parser/tw-parser.d.ts +210 -0
- package/lib/esm/core/parser/tw-parser.mjs +1678 -0
- package/lib/esm/core/parser/tw-parser.mjs.map +1 -0
- package/lib/esm/core/parser/types.d.ts +37 -0
- package/lib/esm/core/parser/typography-dispatcher.d.ts +11 -0
- package/lib/esm/core/parser/typography-dispatcher.mjs +106 -0
- package/lib/esm/core/parser/typography-dispatcher.mjs.map +1 -0
- package/lib/esm/core/parser/typography.d.ts +43 -0
- package/lib/esm/core/parser/typography.mjs +91 -0
- package/lib/esm/core/parser/typography.mjs.map +1 -0
- package/lib/esm/core/style-builder/build-style.d.ts +54 -0
- package/lib/esm/core/style-builder/build-style.mjs +442 -0
- package/lib/esm/core/style-builder/build-style.mjs.map +1 -0
- package/lib/esm/core/style-builder/index.d.ts +3 -0
- package/lib/esm/core/style-builder/union-builder.d.ts +128 -0
- package/lib/esm/core/style-builder/union-builder.mjs +324 -0
- package/lib/esm/core/style-builder/union-builder.mjs.map +1 -0
- package/lib/esm/core/types.d.ts +14 -0
- package/lib/esm/metro/dts.d.ts +16 -0
- package/lib/esm/metro/dts.mjs +125 -0
- package/lib/esm/metro/dts.mjs.map +1 -0
- package/lib/esm/metro/index.d.ts +9 -0
- package/lib/esm/metro/index.mjs +6 -0
- package/lib/esm/metro/index.mjs.map +1 -0
- package/lib/esm/metro/resolver.d.ts +22 -0
- package/lib/esm/metro/resolver.mjs +43 -0
- package/lib/esm/metro/resolver.mjs.map +1 -0
- package/lib/esm/metro/state.d.ts +88 -0
- package/lib/esm/metro/state.mjs +291 -0
- package/lib/esm/metro/state.mjs.map +1 -0
- package/lib/esm/metro/transform-ast.d.ts +88 -0
- package/lib/esm/metro/transform-ast.mjs +1451 -0
- package/lib/esm/metro/transform-ast.mjs.map +1 -0
- package/lib/esm/metro/transformer.d.ts +47 -0
- package/lib/esm/metro/transformer.mjs +349 -0
- package/lib/esm/metro/transformer.mjs.map +1 -0
- package/lib/esm/metro/warn-unknown-classes.d.ts +21 -0
- package/lib/esm/metro/warn-unknown-classes.mjs +84 -0
- package/lib/esm/metro/warn-unknown-classes.mjs.map +1 -0
- package/lib/esm/metro/with-config.d.ts +79 -0
- package/lib/esm/metro/with-config.mjs +194 -0
- package/lib/esm/metro/with-config.mjs.map +1 -0
- package/lib/esm/runtime/chain-handlers.d.ts +33 -0
- package/lib/esm/runtime/chain-handlers.mjs +34 -0
- package/lib/esm/runtime/chain-handlers.mjs.map +1 -0
- package/lib/esm/runtime/components/rnwind-provider.d.ts +84 -0
- package/lib/esm/runtime/components/rnwind-provider.mjs +94 -0
- package/lib/esm/runtime/components/rnwind-provider.mjs.map +1 -0
- package/lib/esm/runtime/gradient-types.d.ts +58 -0
- package/lib/esm/runtime/haptics.d.ts +48 -0
- package/lib/esm/runtime/haptics.mjs +110 -0
- package/lib/esm/runtime/haptics.mjs.map +1 -0
- package/lib/esm/runtime/hooks/use-css.d.ts +11 -0
- package/lib/esm/runtime/hooks/use-css.mjs +19 -0
- package/lib/esm/runtime/hooks/use-css.mjs.map +1 -0
- package/lib/esm/runtime/hooks/use-interact.d.ts +42 -0
- package/lib/esm/runtime/hooks/use-interact.mjs +44 -0
- package/lib/esm/runtime/hooks/use-interact.mjs.map +1 -0
- package/lib/esm/runtime/hooks/use-scheme.d.ts +34 -0
- package/lib/esm/runtime/hooks/use-scheme.mjs +63 -0
- package/lib/esm/runtime/hooks/use-scheme.mjs.map +1 -0
- package/lib/esm/runtime/index.d.ts +27 -0
- package/lib/esm/runtime/index.mjs +18 -0
- package/lib/esm/runtime/index.mjs.map +1 -0
- package/lib/esm/runtime/interactive-box.d.ts +40 -0
- package/lib/esm/runtime/interactive-box.mjs +33 -0
- package/lib/esm/runtime/interactive-box.mjs.map +1 -0
- package/lib/esm/runtime/lookup-css.d.ts +164 -0
- package/lib/esm/runtime/lookup-css.mjs +531 -0
- package/lib/esm/runtime/lookup-css.mjs.map +1 -0
- package/lib/esm/runtime/types.d.ts +29 -0
- package/lib/esm/testing/index.d.ts +145 -0
- package/lib/esm/testing/index.mjs +344 -0
- package/lib/esm/testing/index.mjs.map +1 -0
- package/package.json +80 -13
- package/preset.css +1171 -0
- package/src/core/parser/animation.ts +404 -0
- package/src/core/parser/border-dispatcher.ts +176 -0
- package/src/core/parser/case-convert.ts +10 -0
- package/src/core/parser/color-properties-dispatcher.ts +78 -0
- package/src/core/parser/color.ts +191 -0
- package/src/core/parser/constants.ts +11 -0
- package/src/core/parser/declaration.ts +340 -0
- package/src/core/parser/gradient.ts +148 -0
- package/src/core/parser/haptics.ts +88 -0
- package/src/core/parser/index.ts +8 -0
- package/src/core/parser/keyframes.ts +84 -0
- package/src/core/parser/layout-dispatcher.ts +111 -0
- package/src/core/parser/length.ts +114 -0
- package/src/core/parser/motion-dispatcher.ts +89 -0
- package/src/core/parser/property.ts +15 -0
- package/src/core/parser/safe-area.ts +404 -0
- package/src/core/parser/selector.ts +17 -0
- package/src/core/parser/shorthand.ts +182 -0
- package/src/core/parser/text-truncate.ts +79 -0
- package/src/core/parser/theme-vars.ts +465 -0
- package/src/core/parser/tokens.ts +456 -0
- package/src/core/parser/transform.ts +195 -0
- package/src/core/parser/tw-parser.ts +1828 -0
- package/src/core/parser/types.ts +45 -0
- package/src/core/parser/typography-dispatcher.ts +97 -0
- package/src/core/parser/typography.ts +83 -0
- package/src/core/style-builder/build-style.ts +500 -0
- package/src/core/style-builder/index.ts +3 -0
- package/src/core/style-builder/union-builder.ts +328 -0
- package/src/core/types.ts +15 -0
- package/src/metro/dts.ts +128 -0
- package/src/metro/index.ts +9 -0
- package/src/metro/resolver.ts +42 -0
- package/src/metro/state.ts +305 -0
- package/src/metro/transform-ast.ts +1729 -0
- package/src/metro/transformer.ts +372 -0
- package/src/metro/warn-unknown-classes.ts +79 -0
- package/src/metro/with-config.ts +251 -0
- package/src/runtime/chain-handlers.ts +47 -0
- package/src/runtime/components/rnwind-provider.tsx +144 -0
- package/src/runtime/gradient-types.ts +60 -0
- package/src/runtime/haptics.ts +120 -0
- package/src/runtime/hooks/use-css.ts +16 -0
- package/src/runtime/hooks/use-interact.ts +65 -0
- package/src/runtime/hooks/use-scheme.ts +63 -0
- package/src/runtime/index.ts +54 -0
- package/src/runtime/interactive-box.tsx +57 -0
- package/src/runtime/lookup-css.ts +628 -0
- package/src/runtime/types.ts +32 -0
- package/src/testing/index.ts +507 -0
- package/src/types/tailwindcss-node.d.ts +33 -0
- package/src/index.ts +0 -1
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import * as t from '@babel/types';
|
|
2
|
+
import { parse } from '@babel/parser';
|
|
3
|
+
import generate from '@babel/generator';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
import { realpathSync } from 'node:fs';
|
|
6
|
+
import { transformAst } from './transform-ast.mjs';
|
|
7
|
+
import { onThemeChange, getRnwindState, getRnwindCacheKey, getClassNamePrefixes, getHostSources, getHostComponents } from './state.mjs';
|
|
8
|
+
import { THEME_SIGNATURE_MODULE, STYLE_SPECIFIERS } from './resolver.mjs';
|
|
9
|
+
import { filterUnknownClassCandidates } from './warn-unknown-classes.mjs';
|
|
10
|
+
|
|
11
|
+
/** Env var that points at the upstream `babelTransformerPath` we override. */
|
|
12
|
+
const UPSTREAM_ENV = 'RNWIND_UPSTREAM_TRANSFORMER';
|
|
13
|
+
/** Cached upstream module — required once, reused across every transform call. */
|
|
14
|
+
let cachedUpstream = null;
|
|
15
|
+
const generateModule = generate.default ?? generate;
|
|
16
|
+
/**
|
|
17
|
+
* Parse user source with the broad plugin set (Flow + JSX + TypeScript
|
|
18
|
+
* + class properties). Permissive on purpose so we don't reject any
|
|
19
|
+
* file the upstream could have handled. Returns `null` when parse
|
|
20
|
+
* fails — caller falls back to the raw source string.
|
|
21
|
+
* @param source Source text.
|
|
22
|
+
* @returns Parsed AST, or null on parse failure.
|
|
23
|
+
*/
|
|
24
|
+
function parseUserSource(source) {
|
|
25
|
+
try {
|
|
26
|
+
return parse(source, {
|
|
27
|
+
sourceType: 'unambiguous',
|
|
28
|
+
allowReturnOutsideFunction: true,
|
|
29
|
+
allowImportExportEverywhere: true,
|
|
30
|
+
plugins: ['typescript', 'jsx'],
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
try {
|
|
35
|
+
return parse(source, {
|
|
36
|
+
sourceType: 'unambiguous',
|
|
37
|
+
allowReturnOutsideFunction: true,
|
|
38
|
+
allowImportExportEverywhere: true,
|
|
39
|
+
plugins: ['flow', 'jsx'],
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Print Tailwind-shaped candidates oxide picked up but the parser
|
|
49
|
+
* could NOT compile — typo, missing custom utility, or class not in
|
|
50
|
+
* the user's theme. Filtering by candidates that ALSO appear inside a
|
|
51
|
+
* `className="…"` literal eliminates false positives from imports,
|
|
52
|
+
* comments, and JSX prop values.
|
|
53
|
+
* @param source Original source text — searched for className literals.
|
|
54
|
+
* @param candidates Every candidate oxide surfaced from the source.
|
|
55
|
+
* @param atoms Successfully resolved atoms (keys are class names).
|
|
56
|
+
* @param filename Source path, prefixed onto the warning.
|
|
57
|
+
*/
|
|
58
|
+
function warnUnknownClasses(source, candidates, atoms, filename) {
|
|
59
|
+
const atomNames = new Set(atoms.keys());
|
|
60
|
+
const unknown = filterUnknownClassCandidates(source, candidates, atomNames);
|
|
61
|
+
if (unknown.length === 0)
|
|
62
|
+
return;
|
|
63
|
+
// eslint-disable-next-line no-console
|
|
64
|
+
console.warn(`rnwind: unknown class${unknown.length > 1 ? 'es' : ''} in ${filename}: ${unknown.join(', ')}`);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Extract the bare extension for oxide / internal switches.
|
|
68
|
+
* @param filename Absolute path.
|
|
69
|
+
* @returns Extension without the leading dot (`tsx` / `ts` / `js` / `jsx`).
|
|
70
|
+
*/
|
|
71
|
+
function extensionOf(filename) {
|
|
72
|
+
const index = filename.lastIndexOf('.');
|
|
73
|
+
if (index === -1)
|
|
74
|
+
return 'tsx';
|
|
75
|
+
return filename.slice(index + 1);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Read the project root Metro hands us per-transform. Falls back to
|
|
79
|
+
* `process.cwd()` only when the upstream harness doesn't set it (unit
|
|
80
|
+
* tests, standalone). Metro's production pipeline always sets it.
|
|
81
|
+
* @param args Metro transformer args.
|
|
82
|
+
* @returns Absolute project root.
|
|
83
|
+
*/
|
|
84
|
+
function projectRootOf(args) {
|
|
85
|
+
const fromOptions = args.options?.projectRoot;
|
|
86
|
+
if (typeof fromOptions === 'string' && fromOptions.length > 0)
|
|
87
|
+
return fromOptions;
|
|
88
|
+
return process.cwd();
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Whether a `.css` filename is the user's theme entry (the file
|
|
92
|
+
* `withRnwindConfig` pointed us at via `RNWIND_CSS_ENTRY_FILE`).
|
|
93
|
+
* Only the theme CSS should trigger a scheme rebuild — unrelated CSS
|
|
94
|
+
* files in the project stay invisible to rnwind.
|
|
95
|
+
* @param filename Absolute CSS path.
|
|
96
|
+
* @returns Whether the file is the configured theme entry.
|
|
97
|
+
*/
|
|
98
|
+
function isThemeCssEntry(filename) {
|
|
99
|
+
const cssEntry = process.env.RNWIND_CSS_ENTRY_FILE;
|
|
100
|
+
return typeof cssEntry === 'string' && cssEntry.length > 0 && cssEntry === filename;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Parse + run rnwind's JSX rewrite + regenerate source code. When
|
|
104
|
+
* parsing or transformation fails, fall back to the original source —
|
|
105
|
+
* we don't want a transient parse error to crash Metro for a file the
|
|
106
|
+
* upstream might handle fine.
|
|
107
|
+
* @param args Metro args; `src` is the original source text.
|
|
108
|
+
* @returns Rewritten source text (with `className=` rewrites applied).
|
|
109
|
+
*/
|
|
110
|
+
async function rewriteSource(args) {
|
|
111
|
+
const ast = parseUserSource(args.src);
|
|
112
|
+
if (!ast)
|
|
113
|
+
return args.src;
|
|
114
|
+
const state = getRnwindState(projectRootOf(args));
|
|
115
|
+
const extension = extensionOf(args.filename);
|
|
116
|
+
const parsed = await state.parser.parseAtoms({ content: args.src, extension });
|
|
117
|
+
warnUnknownClasses(args.src, parsed.candidates, parsed.atoms, args.filename);
|
|
118
|
+
const classNamePrefixes = getClassNamePrefixes();
|
|
119
|
+
const hostSources = getHostSources();
|
|
120
|
+
const hostComponents = getHostComponents();
|
|
121
|
+
if (parsed.atoms.size === 0) {
|
|
122
|
+
state.builder.dropFile(args.filename);
|
|
123
|
+
await state.builder.writeSchemes();
|
|
124
|
+
transformAst(ast, {
|
|
125
|
+
styleSpecifiers: [],
|
|
126
|
+
gradientAtoms: parsed.gradientAtoms,
|
|
127
|
+
hapticAtoms: parsed.hapticAtoms,
|
|
128
|
+
classNamePrefixes,
|
|
129
|
+
hostSources,
|
|
130
|
+
hostComponents,
|
|
131
|
+
});
|
|
132
|
+
injectThemeSignatureImport(ast);
|
|
133
|
+
return generateModule(ast).code;
|
|
134
|
+
}
|
|
135
|
+
const { changed } = await state.builder.recordFile(args.filename, parsed.atoms, parsed.keyframes);
|
|
136
|
+
if (changed)
|
|
137
|
+
await state.builder.writeSchemes();
|
|
138
|
+
transformAst(ast, {
|
|
139
|
+
styleSpecifiers: STYLE_SPECIFIERS,
|
|
140
|
+
gradientAtoms: parsed.gradientAtoms,
|
|
141
|
+
hapticAtoms: parsed.hapticAtoms,
|
|
142
|
+
classNamePrefixes,
|
|
143
|
+
hostSources,
|
|
144
|
+
hostComponents,
|
|
145
|
+
});
|
|
146
|
+
injectThemeSignatureImport(ast);
|
|
147
|
+
return generateModule(ast).code;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Prepend `import 'rnwind/__generated/theme-signature'` to every
|
|
151
|
+
* rnwind-transformed file. The resolver maps that specifier to the
|
|
152
|
+
* user's theme CSS so Metro's dependency graph carries a real edge
|
|
153
|
+
* from this JS file to the CSS. When the user edits `global.css`,
|
|
154
|
+
* the CSS module's SHA1 changes, and Metro invalidates every JS file
|
|
155
|
+
* holding this import — forcing them to re-transform with the new
|
|
156
|
+
* theme. The `.css` branch in {@link transform} returns an empty
|
|
157
|
+
* `export {}` module so the runtime cost is one extra `require()`.
|
|
158
|
+
* @param ast Babel File AST to mutate in place.
|
|
159
|
+
*/
|
|
160
|
+
function injectThemeSignatureImport(ast) {
|
|
161
|
+
const declaration = t.importDeclaration([], t.stringLiteral(THEME_SIGNATURE_MODULE));
|
|
162
|
+
ast.program.body.unshift(declaration);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Read the upstream transformer's `getCacheKey()` so our cache-key
|
|
166
|
+
* contribution composes with — rather than replaces — whatever the
|
|
167
|
+
* host framework wants to mix in.
|
|
168
|
+
* @returns Upstream cache key, or `null` when no upstream exposes one.
|
|
169
|
+
*/
|
|
170
|
+
function loadUpstreamCacheKey() {
|
|
171
|
+
const upstream = loadUpstream();
|
|
172
|
+
if (!upstream)
|
|
173
|
+
return null;
|
|
174
|
+
try {
|
|
175
|
+
return typeof upstream.getCacheKey === 'function' ? upstream.getCacheKey() : null;
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Invoke the upstream `babelTransformerPath` Metro originally had
|
|
183
|
+
* configured. The path is read from `RNWIND_UPSTREAM_TRANSFORMER`,
|
|
184
|
+
* which `withRnwindConfig` sets at Metro startup. When the env var is
|
|
185
|
+
* unset (unit tests, standalone use), fall back to a typescript+jsx
|
|
186
|
+
* parse.
|
|
187
|
+
* @param args Metro's per-file args.
|
|
188
|
+
* @returns Upstream transform result containing the post-babel AST.
|
|
189
|
+
*/
|
|
190
|
+
async function runUpstream(args) {
|
|
191
|
+
if (args.ast && !process.env[UPSTREAM_ENV])
|
|
192
|
+
return { ast: args.ast };
|
|
193
|
+
const upstream = loadUpstream();
|
|
194
|
+
if (upstream)
|
|
195
|
+
return await Promise.resolve(upstream.transform(args));
|
|
196
|
+
if (args.ast)
|
|
197
|
+
return { ast: args.ast };
|
|
198
|
+
return { ast: parseSource(args.src) };
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Lazily require the upstream transformer module. Cached after first
|
|
202
|
+
* load so per-file overhead is one cache lookup.
|
|
203
|
+
* @returns Upstream module, or null when env is unset.
|
|
204
|
+
*/
|
|
205
|
+
function loadUpstream() {
|
|
206
|
+
if (cachedUpstream)
|
|
207
|
+
return cachedUpstream;
|
|
208
|
+
const upstreamPath = process.env[UPSTREAM_ENV];
|
|
209
|
+
if (!upstreamPath || upstreamPath.length === 0)
|
|
210
|
+
return null;
|
|
211
|
+
try {
|
|
212
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
|
213
|
+
const required = require(upstreamPath);
|
|
214
|
+
const upstream = required.default ?? required;
|
|
215
|
+
if (typeof upstream.transform !== 'function')
|
|
216
|
+
return null;
|
|
217
|
+
cachedUpstream = upstream;
|
|
218
|
+
return upstream;
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
// eslint-disable-next-line no-console
|
|
222
|
+
if (process.env.RNWIND_DEBUG)
|
|
223
|
+
console.error('rnwind: failed to load upstream transformer:', error);
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Cheap guard — the file has to look JS/TS, live outside `node_modules`,
|
|
229
|
+
* and mention `className=` before we spend AST cycles on it.
|
|
230
|
+
*
|
|
231
|
+
* Symlink awareness: monorepo workspaces (yarn / pnpm / bun workspaces)
|
|
232
|
+
* symlink each package into the consumer's `node_modules/<name>`, so a
|
|
233
|
+
* file from `packages/ui/src/Foo.tsx` ends up reaching the transformer
|
|
234
|
+
* as `<root>/node_modules/ui/src/Foo.tsx`. The naïve `/node_modules/`
|
|
235
|
+
* check would skip every workspace UI file. We `realpath` the filename
|
|
236
|
+
* once and only bail when the resolved real path is ALSO under
|
|
237
|
+
* node_modules — true third-party installs.
|
|
238
|
+
* @param args Metro args.
|
|
239
|
+
* @returns Whether the file might need the rnwind pass.
|
|
240
|
+
*/
|
|
241
|
+
function isRewriteCandidate(args) {
|
|
242
|
+
if (!/\.(?:tsx|ts|jsx|js)$/i.test(args.filename))
|
|
243
|
+
return false;
|
|
244
|
+
// Case-insensitive so `<prefix>ClassName=` (e.g. `contentContainerClassName=`)
|
|
245
|
+
// — which has a capital `C` and so doesn't contain the lowercase
|
|
246
|
+
// `className=` — still routes the file through the rewrite pass.
|
|
247
|
+
if (!/classname=/i.test(args.src))
|
|
248
|
+
return false;
|
|
249
|
+
if (!args.filename.includes('/node_modules/'))
|
|
250
|
+
return true;
|
|
251
|
+
// node_modules in path → could be a workspace symlink; resolve it.
|
|
252
|
+
try {
|
|
253
|
+
return !realpathSync(args.filename).includes('/node_modules/');
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
// realpath failed (broken symlink, missing file). Fall back to skipping.
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Fallback parse when no upstream is configured AND Metro didn't hand
|
|
262
|
+
* us an AST. Used by unit tests and standalone setups.
|
|
263
|
+
* @param source Source text.
|
|
264
|
+
* @returns Parsed Babel File.
|
|
265
|
+
*/
|
|
266
|
+
function parseSource(source) {
|
|
267
|
+
return parse(source, { sourceType: 'module', plugins: ['typescript', 'jsx'] });
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* rnwind's Metro babel transformer. Two phases per source file:
|
|
271
|
+
*
|
|
272
|
+
* 1. **Pre-process the source string before handing it to the upstream
|
|
273
|
+
* babel pipeline.** babel-preset-expo / React's JSX transform run
|
|
274
|
+
* inside the upstream and convert `<View className="..."/>` into
|
|
275
|
+
* `React.createElement(View, {className})`. If we walked the AST
|
|
276
|
+
* AFTER the upstream, there'd be no JSX attributes left to
|
|
277
|
+
* rewrite. So we parse, run our pass, regenerate code, and feed
|
|
278
|
+
* THAT to the upstream as `src`.
|
|
279
|
+
* 2. **Delegate to the upstream `babelTransformerPath`** (Expo's
|
|
280
|
+
* default handles Flow stripping, expo-router macros, etc.).
|
|
281
|
+
*
|
|
282
|
+
* Skip both phases when the file isn't a JS/TS source under user
|
|
283
|
+
* code, or doesn't mention `className=` — hand straight to upstream.
|
|
284
|
+
* @param args Metro's per-file args.
|
|
285
|
+
* @returns Mutated AST + metadata.
|
|
286
|
+
*/
|
|
287
|
+
async function transform(args) {
|
|
288
|
+
// Short-circuit `.css` inputs: the theme CSS is pulled into the dep
|
|
289
|
+
// graph as a sentinel (see `THEME_SIGNATURE_MODULE` in resolver.ts)
|
|
290
|
+
// so Metro watches it and invalidates importers on edit, but the
|
|
291
|
+
// file's CSS syntax can't go through a JS babel transformer.
|
|
292
|
+
//
|
|
293
|
+
// When the CSS being transformed IS the user's theme entry, we
|
|
294
|
+
// piggyback on Metro's own file-watcher: Metro calls us here on
|
|
295
|
+
// every CSS save; we trigger `onThemeChange` to rebuild parser +
|
|
296
|
+
// rewrite scheme files with the new values. Metro's dep graph then
|
|
297
|
+
// HMRs the regenerated `common.style.js` to the running app.
|
|
298
|
+
//
|
|
299
|
+
// Emitting the CSS content hash in the fake JS output is what makes
|
|
300
|
+
// Metro propagate invalidation to downstream importers — constant
|
|
301
|
+
// `export {}` bytes would never look changed and Metro would skip
|
|
302
|
+
// the chain.
|
|
303
|
+
if (args.filename.endsWith('.css')) {
|
|
304
|
+
if (isThemeCssEntry(args.filename)) {
|
|
305
|
+
try {
|
|
306
|
+
await onThemeChange(projectRootOf(args));
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
// CSS edit happened outside a configured project (e.g. tests).
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
const themeHash = createHash('sha256').update(args.src).digest('hex').slice(0, 16);
|
|
313
|
+
const stub = `export const __rnwindThemeHash = ${JSON.stringify(themeHash)};\n`;
|
|
314
|
+
return { ast: parse(stub, { sourceType: 'module' }) };
|
|
315
|
+
}
|
|
316
|
+
if (!isRewriteCandidate(args)) {
|
|
317
|
+
if (/\.(?:tsx|ts|jsx|js)$/i.test(args.filename) && !args.filename.includes('/node_modules/')) {
|
|
318
|
+
try {
|
|
319
|
+
getRnwindState(projectRootOf(args)).builder.dropFile(args.filename);
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
// State not configured (e.g. test). Nothing to drop.
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return runUpstream(args);
|
|
326
|
+
}
|
|
327
|
+
const rewrittenSource = await rewriteSource(args);
|
|
328
|
+
return runUpstream({ ...args, src: rewrittenSource, ast: undefined });
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Metro's babel-transformer contract: a `getCacheKey()` export is
|
|
332
|
+
* sampled per-file and mixed into the transform cache key. Returning
|
|
333
|
+
* a string that includes the theme CSS content hash invalidates every
|
|
334
|
+
* cached transform on every CSS edit — so the bundle rebuilds with
|
|
335
|
+
* the new theme automatically on the next request.
|
|
336
|
+
* @returns Cache-key segment that includes rnwind's current theme hash.
|
|
337
|
+
*/
|
|
338
|
+
function getCacheKey() {
|
|
339
|
+
const upstreamKey = loadUpstreamCacheKey();
|
|
340
|
+
const ownKey = getRnwindCacheKey();
|
|
341
|
+
return upstreamKey ? `${upstreamKey}|${ownKey}` : ownKey;
|
|
342
|
+
}
|
|
343
|
+
/** Test-only — drop the cached upstream so a new env var picks up next call. */
|
|
344
|
+
function __resetUpstreamCache() {
|
|
345
|
+
cachedUpstream = null;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export { __resetUpstreamCache, getCacheKey, transform };
|
|
349
|
+
//# sourceMappingURL=transformer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformer.mjs","sources":["../../../../src/metro/transformer.ts"],"sourcesContent":["import type { File } from '@babel/types'\nimport * as t from '@babel/types'\nimport { parse } from '@babel/parser'\nimport generate from '@babel/generator'\nimport { createHash } from 'node:crypto'\nimport { realpathSync } from 'node:fs'\nimport { transformAst } from './transform-ast'\nimport { getClassNamePrefixes, getHostComponents, getHostSources, getRnwindCacheKey, getRnwindState, onThemeChange } from './state'\nimport { STYLE_SPECIFIERS, THEME_SIGNATURE_MODULE } from './resolver'\nimport { filterUnknownClassCandidates } from './warn-unknown-classes'\n\n/** The shape of the upstream module we delegate parsing/babel work to. */\ninterface UpstreamTransformer {\n transform: (args: BabelTransformerArgs) => Promise<BabelTransformerResult> | BabelTransformerResult\n}\n\n/** Env var that points at the upstream `babelTransformerPath` we override. */\nconst UPSTREAM_ENV = 'RNWIND_UPSTREAM_TRANSFORMER'\n\n/** Cached upstream module — required once, reused across every transform call. */\nlet cachedUpstream: UpstreamTransformer | null = null\n\nconst generateModule = (generate as unknown as { default?: typeof generate }).default ?? generate\n\n/**\n * Parse user source with the broad plugin set (Flow + JSX + TypeScript\n * + class properties). Permissive on purpose so we don't reject any\n * file the upstream could have handled. Returns `null` when parse\n * fails — caller falls back to the raw source string.\n * @param source Source text.\n * @returns Parsed AST, or null on parse failure.\n */\nfunction parseUserSource(source: string): File | null {\n try {\n return parse(source, {\n sourceType: 'unambiguous',\n allowReturnOutsideFunction: true,\n allowImportExportEverywhere: true,\n plugins: ['typescript', 'jsx'],\n }) as unknown as File\n } catch {\n try {\n return parse(source, {\n sourceType: 'unambiguous',\n allowReturnOutsideFunction: true,\n allowImportExportEverywhere: true,\n plugins: ['flow', 'jsx'],\n }) as unknown as File\n } catch {\n return null\n }\n }\n}\n\n/**\n * Print Tailwind-shaped candidates oxide picked up but the parser\n * could NOT compile — typo, missing custom utility, or class not in\n * the user's theme. Filtering by candidates that ALSO appear inside a\n * `className=\"…\"` literal eliminates false positives from imports,\n * comments, and JSX prop values.\n * @param source Original source text — searched for className literals.\n * @param candidates Every candidate oxide surfaced from the source.\n * @param atoms Successfully resolved atoms (keys are class names).\n * @param filename Source path, prefixed onto the warning.\n */\nfunction warnUnknownClasses(\n source: string,\n candidates: readonly string[],\n atoms: ReadonlyMap<string, unknown>,\n filename: string,\n): void {\n const atomNames = new Set(atoms.keys())\n const unknown = filterUnknownClassCandidates(source, candidates, atomNames)\n if (unknown.length === 0) return\n // eslint-disable-next-line no-console\n console.warn(`rnwind: unknown class${unknown.length > 1 ? 'es' : ''} in ${filename}: ${unknown.join(', ')}`)\n}\n\n/**\n * Extract the bare extension for oxide / internal switches.\n * @param filename Absolute path.\n * @returns Extension without the leading dot (`tsx` / `ts` / `js` / `jsx`).\n */\nfunction extensionOf(filename: string): string {\n const index = filename.lastIndexOf('.')\n if (index === -1) return 'tsx'\n return filename.slice(index + 1)\n}\n\n/**\n * Read the project root Metro hands us per-transform. Falls back to\n * `process.cwd()` only when the upstream harness doesn't set it (unit\n * tests, standalone). Metro's production pipeline always sets it.\n * @param args Metro transformer args.\n * @returns Absolute project root.\n */\nfunction projectRootOf(args: BabelTransformerArgs): string {\n const fromOptions = args.options?.projectRoot\n if (typeof fromOptions === 'string' && fromOptions.length > 0) return fromOptions\n return process.cwd()\n}\n\n/**\n * Whether a `.css` filename is the user's theme entry (the file\n * `withRnwindConfig` pointed us at via `RNWIND_CSS_ENTRY_FILE`).\n * Only the theme CSS should trigger a scheme rebuild — unrelated CSS\n * files in the project stay invisible to rnwind.\n * @param filename Absolute CSS path.\n * @returns Whether the file is the configured theme entry.\n */\nfunction isThemeCssEntry(filename: string): boolean {\n const cssEntry = process.env.RNWIND_CSS_ENTRY_FILE\n return typeof cssEntry === 'string' && cssEntry.length > 0 && cssEntry === filename\n}\n\n/**\n * Parse + run rnwind's JSX rewrite + regenerate source code. When\n * parsing or transformation fails, fall back to the original source —\n * we don't want a transient parse error to crash Metro for a file the\n * upstream might handle fine.\n * @param args Metro args; `src` is the original source text.\n * @returns Rewritten source text (with `className=` rewrites applied).\n */\nasync function rewriteSource(args: BabelTransformerArgs): Promise<string> {\n const ast = parseUserSource(args.src)\n if (!ast) return args.src\n\n const state = getRnwindState(projectRootOf(args))\n const extension = extensionOf(args.filename)\n const parsed = await state.parser.parseAtoms({ content: args.src, extension })\n\n warnUnknownClasses(args.src, parsed.candidates, parsed.atoms, args.filename)\n\n const classNamePrefixes = getClassNamePrefixes()\n const hostSources = getHostSources()\n const hostComponents = getHostComponents()\n if (parsed.atoms.size === 0) {\n state.builder.dropFile(args.filename)\n await state.builder.writeSchemes()\n transformAst(ast, {\n styleSpecifiers: [],\n gradientAtoms: parsed.gradientAtoms,\n hapticAtoms: parsed.hapticAtoms,\n classNamePrefixes,\n hostSources,\n hostComponents,\n })\n injectThemeSignatureImport(ast)\n return generateModule(ast).code\n }\n\n const { changed } = await state.builder.recordFile(args.filename, parsed.atoms, parsed.keyframes)\n if (changed) await state.builder.writeSchemes()\n\n transformAst(ast, {\n styleSpecifiers: STYLE_SPECIFIERS as unknown as readonly string[],\n gradientAtoms: parsed.gradientAtoms,\n hapticAtoms: parsed.hapticAtoms,\n classNamePrefixes,\n hostSources,\n hostComponents,\n })\n injectThemeSignatureImport(ast)\n return generateModule(ast).code\n}\n\n/**\n * Prepend `import 'rnwind/__generated/theme-signature'` to every\n * rnwind-transformed file. The resolver maps that specifier to the\n * user's theme CSS so Metro's dependency graph carries a real edge\n * from this JS file to the CSS. When the user edits `global.css`,\n * the CSS module's SHA1 changes, and Metro invalidates every JS file\n * holding this import — forcing them to re-transform with the new\n * theme. The `.css` branch in {@link transform} returns an empty\n * `export {}` module so the runtime cost is one extra `require()`.\n * @param ast Babel File AST to mutate in place.\n */\nfunction injectThemeSignatureImport(ast: File): void {\n const declaration = t.importDeclaration([], t.stringLiteral(THEME_SIGNATURE_MODULE))\n ast.program.body.unshift(declaration)\n}\n\n/**\n * Read the upstream transformer's `getCacheKey()` so our cache-key\n * contribution composes with — rather than replaces — whatever the\n * host framework wants to mix in.\n * @returns Upstream cache key, or `null` when no upstream exposes one.\n */\nfunction loadUpstreamCacheKey(): string | null {\n const upstream = loadUpstream() as (UpstreamTransformer & { getCacheKey?: () => string }) | null\n if (!upstream) return null\n try {\n return typeof upstream.getCacheKey === 'function' ? upstream.getCacheKey() : null\n } catch {\n return null\n }\n}\n\n/**\n * Invoke the upstream `babelTransformerPath` Metro originally had\n * configured. The path is read from `RNWIND_UPSTREAM_TRANSFORMER`,\n * which `withRnwindConfig` sets at Metro startup. When the env var is\n * unset (unit tests, standalone use), fall back to a typescript+jsx\n * parse.\n * @param args Metro's per-file args.\n * @returns Upstream transform result containing the post-babel AST.\n */\nasync function runUpstream(args: BabelTransformerArgs): Promise<BabelTransformerResult> {\n if (args.ast && !process.env[UPSTREAM_ENV]) return { ast: args.ast }\n const upstream = loadUpstream()\n if (upstream) return await Promise.resolve(upstream.transform(args))\n if (args.ast) return { ast: args.ast }\n return { ast: parseSource(args.src) }\n}\n\n/**\n * Lazily require the upstream transformer module. Cached after first\n * load so per-file overhead is one cache lookup.\n * @returns Upstream module, or null when env is unset.\n */\nfunction loadUpstream(): UpstreamTransformer | null {\n if (cachedUpstream) return cachedUpstream\n const upstreamPath = process.env[UPSTREAM_ENV]\n if (!upstreamPath || upstreamPath.length === 0) return null\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires\n const required = require(upstreamPath) as UpstreamTransformer | { default?: UpstreamTransformer }\n const upstream = (required as { default?: UpstreamTransformer }).default ?? (required as UpstreamTransformer)\n if (typeof upstream.transform !== 'function') return null\n cachedUpstream = upstream\n return upstream\n } catch (error) {\n // eslint-disable-next-line no-console\n if (process.env.RNWIND_DEBUG) console.error('rnwind: failed to load upstream transformer:', error)\n return null\n }\n}\n\n/**\n * Cheap guard — the file has to look JS/TS, live outside `node_modules`,\n * and mention `className=` before we spend AST cycles on it.\n *\n * Symlink awareness: monorepo workspaces (yarn / pnpm / bun workspaces)\n * symlink each package into the consumer's `node_modules/<name>`, so a\n * file from `packages/ui/src/Foo.tsx` ends up reaching the transformer\n * as `<root>/node_modules/ui/src/Foo.tsx`. The naïve `/node_modules/`\n * check would skip every workspace UI file. We `realpath` the filename\n * once and only bail when the resolved real path is ALSO under\n * node_modules — true third-party installs.\n * @param args Metro args.\n * @returns Whether the file might need the rnwind pass.\n */\nfunction isRewriteCandidate(args: BabelTransformerArgs): boolean {\n if (!/\\.(?:tsx|ts|jsx|js)$/i.test(args.filename)) return false\n // Case-insensitive so `<prefix>ClassName=` (e.g. `contentContainerClassName=`)\n // — which has a capital `C` and so doesn't contain the lowercase\n // `className=` — still routes the file through the rewrite pass.\n if (!/classname=/i.test(args.src)) return false\n if (!args.filename.includes('/node_modules/')) return true\n // node_modules in path → could be a workspace symlink; resolve it.\n try {\n return !realpathSync(args.filename).includes('/node_modules/')\n } catch {\n // realpath failed (broken symlink, missing file). Fall back to skipping.\n return false\n }\n}\n\n/**\n * Fallback parse when no upstream is configured AND Metro didn't hand\n * us an AST. Used by unit tests and standalone setups.\n * @param source Source text.\n * @returns Parsed Babel File.\n */\nfunction parseSource(source: string): File {\n return parse(source, { sourceType: 'module', plugins: ['typescript', 'jsx'] }) as unknown as File\n}\n\n/** Metro's babel transformer signature. */\nexport interface BabelTransformerArgs {\n filename: string\n src: string\n options: { projectRoot?: string; [key: string]: unknown }\n ast?: File\n plugins?: readonly unknown[]\n}\n\n/** Return shape Metro expects from a babel transformer. */\nexport interface BabelTransformerResult {\n ast: File\n metadata?: unknown\n}\n\n/**\n * rnwind's Metro babel transformer. Two phases per source file:\n *\n * 1. **Pre-process the source string before handing it to the upstream\n * babel pipeline.** babel-preset-expo / React's JSX transform run\n * inside the upstream and convert `<View className=\"...\"/>` into\n * `React.createElement(View, {className})`. If we walked the AST\n * AFTER the upstream, there'd be no JSX attributes left to\n * rewrite. So we parse, run our pass, regenerate code, and feed\n * THAT to the upstream as `src`.\n * 2. **Delegate to the upstream `babelTransformerPath`** (Expo's\n * default handles Flow stripping, expo-router macros, etc.).\n *\n * Skip both phases when the file isn't a JS/TS source under user\n * code, or doesn't mention `className=` — hand straight to upstream.\n * @param args Metro's per-file args.\n * @returns Mutated AST + metadata.\n */\nexport async function transform(args: BabelTransformerArgs): Promise<BabelTransformerResult> {\n // Short-circuit `.css` inputs: the theme CSS is pulled into the dep\n // graph as a sentinel (see `THEME_SIGNATURE_MODULE` in resolver.ts)\n // so Metro watches it and invalidates importers on edit, but the\n // file's CSS syntax can't go through a JS babel transformer.\n //\n // When the CSS being transformed IS the user's theme entry, we\n // piggyback on Metro's own file-watcher: Metro calls us here on\n // every CSS save; we trigger `onThemeChange` to rebuild parser +\n // rewrite scheme files with the new values. Metro's dep graph then\n // HMRs the regenerated `common.style.js` to the running app.\n //\n // Emitting the CSS content hash in the fake JS output is what makes\n // Metro propagate invalidation to downstream importers — constant\n // `export {}` bytes would never look changed and Metro would skip\n // the chain.\n if (args.filename.endsWith('.css')) {\n if (isThemeCssEntry(args.filename)) {\n try {\n await onThemeChange(projectRootOf(args))\n } catch {\n // CSS edit happened outside a configured project (e.g. tests).\n }\n }\n const themeHash = createHash('sha256').update(args.src).digest('hex').slice(0, 16)\n const stub = `export const __rnwindThemeHash = ${JSON.stringify(themeHash)};\\n`\n return { ast: parse(stub, { sourceType: 'module' }) as unknown as File }\n }\n if (!isRewriteCandidate(args)) {\n if (/\\.(?:tsx|ts|jsx|js)$/i.test(args.filename) && !args.filename.includes('/node_modules/')) {\n try {\n getRnwindState(projectRootOf(args)).builder.dropFile(args.filename)\n } catch {\n // State not configured (e.g. test). Nothing to drop.\n }\n }\n return runUpstream(args)\n }\n\n const rewrittenSource = await rewriteSource(args)\n return runUpstream({ ...args, src: rewrittenSource, ast: undefined })\n}\n\n/**\n * Metro's babel-transformer contract: a `getCacheKey()` export is\n * sampled per-file and mixed into the transform cache key. Returning\n * a string that includes the theme CSS content hash invalidates every\n * cached transform on every CSS edit — so the bundle rebuilds with\n * the new theme automatically on the next request.\n * @returns Cache-key segment that includes rnwind's current theme hash.\n */\nexport function getCacheKey(): string {\n const upstreamKey = loadUpstreamCacheKey()\n const ownKey = getRnwindCacheKey()\n return upstreamKey ? `${upstreamKey}|${ownKey}` : ownKey\n}\n\n/** Test-only — drop the cached upstream so a new env var picks up next call. */\nexport function __resetUpstreamCache(): void {\n cachedUpstream = null\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filter Tailwind candidate strings down to ones that actually appear
|
|
3
|
+
* as a token inside a `className="…"` literal in the source. Oxide
|
|
4
|
+
* scans the entire file and surfaces anything Tailwind-shaped — that
|
|
5
|
+
* includes import specifiers (`'expo-router'`), comment markers
|
|
6
|
+
* (`/* @rnwind-theme=… *\/`), JSX prop values (`keyboardType="email-
|
|
7
|
+
* address"`), and bare suffixes Tailwind splits out from compound
|
|
8
|
+
* utilities (`bg-sky-500` also produces `sky-500`). None of those are
|
|
9
|
+
* "unknown classes" worth nagging the user about; the genuine signal
|
|
10
|
+
* is a typo in a real `className`, e.g. `bg-srface` for `bg-surface`.
|
|
11
|
+
*
|
|
12
|
+
* The filter walks every `className="…" / {'…'} / {`…`}` literal in the
|
|
13
|
+
* source, splits each on whitespace, and unions the tokens. Only
|
|
14
|
+
* candidates in that token set survive. Then known-good atoms are
|
|
15
|
+
* subtracted, leaving genuine typos.
|
|
16
|
+
* @param source Raw source text the transformer received from Metro.
|
|
17
|
+
* @param candidates Every candidate oxide picked up.
|
|
18
|
+
* @param atoms Set of atom names the parser successfully resolved.
|
|
19
|
+
* @returns Candidates that look like real "unknown classes" the user typed.
|
|
20
|
+
*/
|
|
21
|
+
export declare function filterUnknownClassCandidates(source: string, candidates: readonly string[], atoms: ReadonlySet<string>): string[];
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Yield every literal-text segment found at a `className=` site.
|
|
3
|
+
* @param source Raw source text.
|
|
4
|
+
* @yields Each literal's inner text.
|
|
5
|
+
*/
|
|
6
|
+
function* iterateClassNameLiterals(source) {
|
|
7
|
+
// className="..." / className='...'
|
|
8
|
+
for (const match of source.matchAll(/className\s*=\s*"([^"]*)"/g))
|
|
9
|
+
yield match[1] ?? '';
|
|
10
|
+
for (const match of source.matchAll(/className\s*=\s*'([^']*)'/g))
|
|
11
|
+
yield match[1] ?? '';
|
|
12
|
+
// className={"..."} / className={'...'}
|
|
13
|
+
for (const match of source.matchAll(/className\s*=\s*\{\s*"([^"]*)"\s*\}/g))
|
|
14
|
+
yield match[1] ?? '';
|
|
15
|
+
for (const match of source.matchAll(/className\s*=\s*\{\s*'([^']*)'\s*\}/g))
|
|
16
|
+
yield match[1] ?? '';
|
|
17
|
+
// className={`...`} — yield each static quasi between `${...}` substitutions.
|
|
18
|
+
for (const match of source.matchAll(/className\s*=\s*\{\s*`([^`]*)`\s*\}/g)) {
|
|
19
|
+
const body = match[1] ?? '';
|
|
20
|
+
for (const part of body.split(/\$\{[^}]*\}/))
|
|
21
|
+
yield part;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Pull every `className="…"`, `className={'…'}`, `className={"…"}`, and
|
|
26
|
+
* `className={\`…\`}` literal out of the source and union their
|
|
27
|
+
* whitespace-separated tokens. A regex-based scan is enough — the
|
|
28
|
+
* warning is best-effort, not load-bearing, and a regex sidesteps
|
|
29
|
+
* having to re-parse the file.
|
|
30
|
+
*
|
|
31
|
+
* Skipped on purpose:
|
|
32
|
+
* - `className={someExpression}` with no inline literal (we can't
|
|
33
|
+
* introspect the runtime value at build time).
|
|
34
|
+
* - Template literals with substitutions (`` `text-${size}` ``) — we
|
|
35
|
+
* only union the static-quasi parts, which is fine because the
|
|
36
|
+
* warning fires only on candidates that ARE in the static parts.
|
|
37
|
+
* @param source Raw source text.
|
|
38
|
+
* @returns Set of whitespace-separated tokens drawn from every literal.
|
|
39
|
+
*/
|
|
40
|
+
function collectClassNameTokens(source) {
|
|
41
|
+
const out = new Set();
|
|
42
|
+
for (const literal of iterateClassNameLiterals(source)) {
|
|
43
|
+
for (const token of literal.split(/\s+/)) {
|
|
44
|
+
if (token.length > 0)
|
|
45
|
+
out.add(token);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Filter Tailwind candidate strings down to ones that actually appear
|
|
52
|
+
* as a token inside a `className="…"` literal in the source. Oxide
|
|
53
|
+
* scans the entire file and surfaces anything Tailwind-shaped — that
|
|
54
|
+
* includes import specifiers (`'expo-router'`), comment markers
|
|
55
|
+
* (`/* @rnwind-theme=… *\/`), JSX prop values (`keyboardType="email-
|
|
56
|
+
* address"`), and bare suffixes Tailwind splits out from compound
|
|
57
|
+
* utilities (`bg-sky-500` also produces `sky-500`). None of those are
|
|
58
|
+
* "unknown classes" worth nagging the user about; the genuine signal
|
|
59
|
+
* is a typo in a real `className`, e.g. `bg-srface` for `bg-surface`.
|
|
60
|
+
*
|
|
61
|
+
* The filter walks every `className="…" / {'…'} / {`…`}` literal in the
|
|
62
|
+
* source, splits each on whitespace, and unions the tokens. Only
|
|
63
|
+
* candidates in that token set survive. Then known-good atoms are
|
|
64
|
+
* subtracted, leaving genuine typos.
|
|
65
|
+
* @param source Raw source text the transformer received from Metro.
|
|
66
|
+
* @param candidates Every candidate oxide picked up.
|
|
67
|
+
* @param atoms Set of atom names the parser successfully resolved.
|
|
68
|
+
* @returns Candidates that look like real "unknown classes" the user typed.
|
|
69
|
+
*/
|
|
70
|
+
function filterUnknownClassCandidates(source, candidates, atoms) {
|
|
71
|
+
const literalTokens = collectClassNameTokens(source);
|
|
72
|
+
const out = [];
|
|
73
|
+
for (const candidate of candidates) {
|
|
74
|
+
if (atoms.has(candidate))
|
|
75
|
+
continue;
|
|
76
|
+
if (!literalTokens.has(candidate))
|
|
77
|
+
continue;
|
|
78
|
+
out.push(candidate);
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export { filterUnknownClassCandidates };
|
|
84
|
+
//# sourceMappingURL=warn-unknown-classes.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"warn-unknown-classes.mjs","sources":["../../../../src/metro/warn-unknown-classes.ts"],"sourcesContent":["/**\n * Yield every literal-text segment found at a `className=` site.\n * @param source Raw source text.\n * @yields Each literal's inner text.\n */\nfunction* iterateClassNameLiterals(source: string): Iterable<string> {\n // className=\"...\" / className='...'\n for (const match of source.matchAll(/className\\s*=\\s*\"([^\"]*)\"/g)) yield match[1] ?? ''\n for (const match of source.matchAll(/className\\s*=\\s*'([^']*)'/g)) yield match[1] ?? ''\n // className={\"...\"} / className={'...'}\n for (const match of source.matchAll(/className\\s*=\\s*\\{\\s*\"([^\"]*)\"\\s*\\}/g)) yield match[1] ?? ''\n for (const match of source.matchAll(/className\\s*=\\s*\\{\\s*'([^']*)'\\s*\\}/g)) yield match[1] ?? ''\n // className={`...`} — yield each static quasi between `${...}` substitutions.\n for (const match of source.matchAll(/className\\s*=\\s*\\{\\s*`([^`]*)`\\s*\\}/g)) {\n const body = match[1] ?? ''\n for (const part of body.split(/\\$\\{[^}]*\\}/)) yield part\n }\n}\n\n/**\n * Pull every `className=\"…\"`, `className={'…'}`, `className={\"…\"}`, and\n * `className={\\`…\\`}` literal out of the source and union their\n * whitespace-separated tokens. A regex-based scan is enough — the\n * warning is best-effort, not load-bearing, and a regex sidesteps\n * having to re-parse the file.\n *\n * Skipped on purpose:\n * - `className={someExpression}` with no inline literal (we can't\n * introspect the runtime value at build time).\n * - Template literals with substitutions (`` `text-${size}` ``) — we\n * only union the static-quasi parts, which is fine because the\n * warning fires only on candidates that ARE in the static parts.\n * @param source Raw source text.\n * @returns Set of whitespace-separated tokens drawn from every literal.\n */\nfunction collectClassNameTokens(source: string): Set<string> {\n const out = new Set<string>()\n for (const literal of iterateClassNameLiterals(source)) {\n for (const token of literal.split(/\\s+/)) {\n if (token.length > 0) out.add(token)\n }\n }\n return out\n}\n\n/**\n * Filter Tailwind candidate strings down to ones that actually appear\n * as a token inside a `className=\"…\"` literal in the source. Oxide\n * scans the entire file and surfaces anything Tailwind-shaped — that\n * includes import specifiers (`'expo-router'`), comment markers\n * (`/* @rnwind-theme=… *\\/`), JSX prop values (`keyboardType=\"email-\n * address\"`), and bare suffixes Tailwind splits out from compound\n * utilities (`bg-sky-500` also produces `sky-500`). None of those are\n * \"unknown classes\" worth nagging the user about; the genuine signal\n * is a typo in a real `className`, e.g. `bg-srface` for `bg-surface`.\n *\n * The filter walks every `className=\"…\" / {'…'} / {`…`}` literal in the\n * source, splits each on whitespace, and unions the tokens. Only\n * candidates in that token set survive. Then known-good atoms are\n * subtracted, leaving genuine typos.\n * @param source Raw source text the transformer received from Metro.\n * @param candidates Every candidate oxide picked up.\n * @param atoms Set of atom names the parser successfully resolved.\n * @returns Candidates that look like real \"unknown classes\" the user typed.\n */\nexport function filterUnknownClassCandidates(\n source: string,\n candidates: readonly string[],\n atoms: ReadonlySet<string>,\n): string[] {\n const literalTokens = collectClassNameTokens(source)\n const out: string[] = []\n for (const candidate of candidates) {\n if (atoms.has(candidate)) continue\n if (!literalTokens.has(candidate)) continue\n out.push(candidate)\n }\n return out\n}\n"],"names":[],"mappings":"AAAA;;;;AAIG;AACH,UAAU,wBAAwB,CAAC,MAAc,EAAA;;IAE/C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,4BAA4B,CAAC;AAAE,QAAA,MAAM,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;IACvF,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,4BAA4B,CAAC;AAAE,QAAA,MAAM,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;;IAEvF,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,sCAAsC,CAAC;AAAE,QAAA,MAAM,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;IACjG,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,sCAAsC,CAAC;AAAE,QAAA,MAAM,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;;IAEjG,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,sCAAsC,CAAC,EAAE;QAC3E,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;QAC3B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC;AAAE,YAAA,MAAM,IAAI;IAC1D;AACF;AAEA;;;;;;;;;;;;;;;AAeG;AACH,SAAS,sBAAsB,CAAC,MAAc,EAAA;AAC5C,IAAA,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU;IAC7B,KAAK,MAAM,OAAO,IAAI,wBAAwB,CAAC,MAAM,CAAC,EAAE;QACtD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;AACxC,YAAA,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;AAAE,gBAAA,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC;QACtC;IACF;AACA,IAAA,OAAO,GAAG;AACZ;AAEA;;;;;;;;;;;;;;;;;;;AAmBG;SACa,4BAA4B,CAC1C,MAAc,EACd,UAA6B,EAC7B,KAA0B,EAAA;AAE1B,IAAA,MAAM,aAAa,GAAG,sBAAsB,CAAC,MAAM,CAAC;IACpD,MAAM,GAAG,GAAa,EAAE;AACxB,IAAA,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE;AAClC,QAAA,IAAI,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC;YAAE;AAC1B,QAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC;YAAE;AACnC,QAAA,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;IACrB;AACA,IAAA,OAAO,GAAG;AACZ;;;;"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { type ResolveRequestFn } from './resolver';
|
|
2
|
+
/** User-facing options for `withRnwindConfig`. */
|
|
3
|
+
export interface RnwindMetroOptions {
|
|
4
|
+
/** Path to the theme CSS (absolute or relative to `projectRoot`). Required. */
|
|
5
|
+
cssEntryFile: string;
|
|
6
|
+
/** Where rnwind writes the `.d.ts` file. Set `false` to disable. Defaults to `<projectRoot>/rnwind-types.d.ts`. */
|
|
7
|
+
dtsFile?: string | false;
|
|
8
|
+
/** Optional project-root override — defaults to `metroConfig.projectRoot` then `process.cwd()`. */
|
|
9
|
+
projectRoot?: string;
|
|
10
|
+
/** Cache directory. Absolute, or relative to `projectRoot`. Default: `.rnwind` at project root. */
|
|
11
|
+
cacheDir?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Extra JSX prop-name prefixes that rnwind should rewrite. Each
|
|
14
|
+
* prefix `P` turns `<Tag PClassName="…">` into `<Tag
|
|
15
|
+
* PStyle={lookupCss(…)}>`. The built-in `'contentContainer'` prefix
|
|
16
|
+
* is always on (covers ScrollView / FlatList / SectionList); user
|
|
17
|
+
* entries merge on top.
|
|
18
|
+
*/
|
|
19
|
+
classNamePrefixes?: readonly string[];
|
|
20
|
+
/**
|
|
21
|
+
* Extra module specifiers whose JSX exports rnwind should treat as
|
|
22
|
+
* "host components" — i.e. tags whose `className="…"` attribute is
|
|
23
|
+
* rewritten to `style={lookupCss(…)}` at build time (zero runtime
|
|
24
|
+
* cost). Merged with the built-in defaults: `react-native`,
|
|
25
|
+
* `react-native-reanimated`, `react-native-svg`,
|
|
26
|
+
* `react-native-gesture-handler`, `expo-linear-gradient`, `expo-image`.
|
|
27
|
+
*
|
|
28
|
+
* Anything NOT marked as a host has its `className` left untouched —
|
|
29
|
+
* the importing component receives the raw string and decides what
|
|
30
|
+
* to do with it. Use this option to opt your design-system / UI
|
|
31
|
+
* primitive packages into the zero-runtime path.
|
|
32
|
+
*/
|
|
33
|
+
hostSources?: readonly string[];
|
|
34
|
+
/**
|
|
35
|
+
* Extra JSX tag names (verbatim — may include `.` for member access
|
|
36
|
+
* like `'Animated.View'`) rnwind should treat as host components,
|
|
37
|
+
* regardless of where they're imported from. Useful for one-off
|
|
38
|
+
* escape-hatches: `import { View as MyBox } from 'react-native'`
|
|
39
|
+
* doesn't change the local name → `'MyBox'` here picks it up.
|
|
40
|
+
*/
|
|
41
|
+
hostComponents?: readonly string[];
|
|
42
|
+
}
|
|
43
|
+
/** Shape we mutate on Metro's config. Loose so we don't pin Metro's internal types. */
|
|
44
|
+
export interface MetroConfigLike {
|
|
45
|
+
projectRoot?: string;
|
|
46
|
+
watchFolders?: string[];
|
|
47
|
+
transformer?: {
|
|
48
|
+
babelTransformerPath?: string;
|
|
49
|
+
[key: string]: unknown;
|
|
50
|
+
};
|
|
51
|
+
resolver?: {
|
|
52
|
+
resolveRequest?: ResolveRequestFn | null;
|
|
53
|
+
[key: string]: unknown;
|
|
54
|
+
};
|
|
55
|
+
[key: string]: unknown;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Wrap a Metro config with rnwind's pipeline:
|
|
59
|
+
* - Install the rnwind babel transformer.
|
|
60
|
+
* - Chain a `resolveRequest` hook that serves
|
|
61
|
+
* `rnwind/__generated/schemes` from `<cacheDir>/schemes.js`.
|
|
62
|
+
* - Write the `.d.ts` so TypeScript accepts `className=` on RN components.
|
|
63
|
+
* - Publish the theme CSS path + cache dir via env so Metro workers
|
|
64
|
+
* can rebuild their local state.
|
|
65
|
+
* - Ensure the cache dir is a watched folder Metro's haste-map indexes.
|
|
66
|
+
*
|
|
67
|
+
* Theme-edit hot reload happens implicitly: every transformed file
|
|
68
|
+
* imports `rnwind/__generated/schemes`, and that module eager-imports
|
|
69
|
+
* `common.style.js`. When the theme changes, the per-scheme files
|
|
70
|
+
* regenerate with new bytes; Metro's content SHA1 dedup detects the
|
|
71
|
+
* change and invalidates every importer automatically.
|
|
72
|
+
* `getCacheKey()` on the transformer covers the per-file transform
|
|
73
|
+
* cache. No file watcher / source-padding hack needed — the dep graph
|
|
74
|
+
* carries the signal.
|
|
75
|
+
* @param metroConfig Config from `getDefaultConfig(__dirname)` or equivalent.
|
|
76
|
+
* @param options rnwind options.
|
|
77
|
+
* @returns The same config, mutated.
|
|
78
|
+
*/
|
|
79
|
+
export declare function withRnwindConfig<C extends MetroConfigLike>(metroConfig: C, options: RnwindMetroOptions): C;
|