rnwind 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/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 +30 -78
- 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/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 +30 -76
- 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/dts.ts +7 -19
- package/src/metro/state.ts +29 -74
- 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/src/testing/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { File as BabelFile } from '@babel/types'
|
|
3
3
|
import { parse } from '@babel/parser'
|
|
4
4
|
import generateImport from '@babel/generator'
|
|
5
|
-
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
5
|
+
import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
6
6
|
import { createRequire } from 'node:module'
|
|
7
7
|
import { tmpdir } from 'node:os'
|
|
8
8
|
import path from 'node:path'
|
|
@@ -13,10 +13,16 @@ import type {
|
|
|
13
13
|
RenderHookOptions,
|
|
14
14
|
} from '@testing-library/react-native'
|
|
15
15
|
import { configureRnwindState, resetRnwindState, transform as metroTransform } from '../metro'
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
__resetLookupCssState,
|
|
18
|
+
registerAtoms,
|
|
19
|
+
registerBreakpoints,
|
|
20
|
+
registerSchemeLoader,
|
|
21
|
+
} from '../runtime/lookup-css'
|
|
22
|
+
import { __resetResolveState, registerGradients, registerHaptics, registerMolecules } from '../runtime/resolve'
|
|
23
|
+
import * as rnwindRuntime from '../runtime'
|
|
17
24
|
import type { Insets } from '../runtime/components/rnwind-provider'
|
|
18
|
-
import { RnwindProvider
|
|
19
|
-
import { triggerHaptic, useMountHaptic } from '../runtime/haptics'
|
|
25
|
+
import { RnwindProvider } from '../runtime/components/rnwind-provider'
|
|
20
26
|
import type { OnHaptics } from '../core/parser/haptics'
|
|
21
27
|
import type { Scheme } from '../runtime/types'
|
|
22
28
|
|
|
@@ -41,29 +47,6 @@ const DEFAULT_THEME_CSS = `@import 'tailwindcss';
|
|
|
41
47
|
/** `StyleSheet.create` stub the evaluated bundle calls — identity at test time. */
|
|
42
48
|
const BUNDLE_STYLE_SHEET = { create: <T>(styles: T): T => styles, hairlineWidth: 1 }
|
|
43
49
|
|
|
44
|
-
/** Stubbed interactive-state hook return matching `useInteract()`. */
|
|
45
|
-
const NOOP_INTERACT = {
|
|
46
|
-
state: undefined,
|
|
47
|
-
onPressIn: (): void => undefined,
|
|
48
|
-
onPressOut: (): void => undefined,
|
|
49
|
-
onFocus: (): void => undefined,
|
|
50
|
-
onBlur: (): void => undefined,
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Identity chain helper matching `chainPress` / `chainFocus`. Returns a
|
|
55
|
-
* single callback that invokes every supplied handler in turn.
|
|
56
|
-
* @param handlers Handlers to chain.
|
|
57
|
-
* @returns Combined callback.
|
|
58
|
-
*/
|
|
59
|
-
const NOOP_CHAIN =
|
|
60
|
-
(...handlers: ReadonlyArray<((...args: unknown[]) => unknown) | undefined>) =>
|
|
61
|
-
(...args: unknown[]): void => {
|
|
62
|
-
for (const handler of handlers) {
|
|
63
|
-
if (typeof handler === 'function') handler(...args)
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
50
|
// Synthesize a require rooted at the consumer's cwd so optional peer
|
|
68
51
|
// lookups (`@testing-library/react-native`, `esbuild`) resolve from THEIR
|
|
69
52
|
// node_modules, not rnwind's. A workspace-local rnwind install resolves
|
|
@@ -149,28 +132,104 @@ function compileToJs(source: string): string {
|
|
|
149
132
|
}
|
|
150
133
|
|
|
151
134
|
/**
|
|
152
|
-
* Evaluate
|
|
153
|
-
* so its `registerAtoms`
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
135
|
+
* Evaluate one generated registry file (`common.style.js`,
|
|
136
|
+
* `<variant>.style.js`, or `schemes.js`) so its `registerAtoms` /
|
|
137
|
+
* `registerBreakpoints` / `registerGradients` / `registerHaptics` /
|
|
138
|
+
* `registerSchemeLoader` calls land in the process-global registries the
|
|
139
|
+
* runtime resolver reads. Imports (`'rnwind'`, `'react-native'`, relative
|
|
140
|
+
* `./common.style`), `require(...)` loaders, and `export {...}` are
|
|
141
|
+
* stripped — every scheme file is evaluated directly, so lazy loaders are
|
|
142
|
+
* inert.
|
|
143
|
+
* @param filePath Absolute path to the generated file.
|
|
157
144
|
*/
|
|
158
|
-
function
|
|
159
|
-
const filePath = path.join(cacheDir, 'style.js')
|
|
145
|
+
function evaluateGeneratedFile(filePath: string): void {
|
|
160
146
|
if (!existsSync(filePath)) return
|
|
161
147
|
const body = readFileSync(filePath, 'utf8')
|
|
162
|
-
.replaceAll(/import
|
|
163
|
-
.replaceAll(/import
|
|
164
|
-
|
|
148
|
+
.replaceAll(/import\s+\{[^}]*\}\s+from\s+['"][^'"]+['"];?\s*\n?/g, '')
|
|
149
|
+
.replaceAll(/import\s+['"][^'"]+['"];?\s*\n?/g, '')
|
|
150
|
+
.replaceAll(/export\s+\{[^}]*\}\s*;?\s*\n?/g, '')
|
|
151
|
+
// Generated body is produced by rnwind itself — not user-controlled.
|
|
152
|
+
// `require` is neutralised: each variant file is evaluated directly,
|
|
153
|
+
// so the manifest's lazy `require('./x.style')` loaders are no-ops.
|
|
165
154
|
// eslint-disable-next-line sonarjs/code-eval
|
|
166
|
-
new Function(
|
|
155
|
+
new Function(
|
|
156
|
+
'StyleSheet',
|
|
157
|
+
'registerAtoms',
|
|
158
|
+
'registerMolecules',
|
|
159
|
+
'registerBreakpoints',
|
|
160
|
+
'registerGradients',
|
|
161
|
+
'registerHaptics',
|
|
162
|
+
'registerSchemeLoader',
|
|
163
|
+
'require',
|
|
164
|
+
body,
|
|
165
|
+
)(
|
|
166
|
+
BUNDLE_STYLE_SHEET,
|
|
167
|
+
registerAtoms,
|
|
168
|
+
registerMolecules,
|
|
169
|
+
registerBreakpoints,
|
|
170
|
+
registerGradients,
|
|
171
|
+
registerHaptics,
|
|
172
|
+
registerSchemeLoader,
|
|
173
|
+
() => {},
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Sort comparator that floats `common.style.js` to the front, rest alpha.
|
|
179
|
+
* @param a
|
|
180
|
+
* @param b
|
|
181
|
+
*/
|
|
182
|
+
function commonFirst(a: string, b: string): number {
|
|
183
|
+
if (a === 'common.style.js') return -1
|
|
184
|
+
if (b === 'common.style.js') return 1
|
|
185
|
+
return a.localeCompare(b)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Evaluate every generated registry the transform wrote (`common` +
|
|
190
|
+
* every variant scheme + the manifest) so the runtime resolver sees the
|
|
191
|
+
* full atom / molecule / gradient / haptic registries — same state a
|
|
192
|
+
* production bundle would hold once all scheme files load.
|
|
193
|
+
* @param cacheDir The rnwind cache dir holding the generated files.
|
|
194
|
+
*/
|
|
195
|
+
function evaluateGeneratedRegistries(cacheDir: string): void {
|
|
196
|
+
if (!existsSync(cacheDir)) return
|
|
197
|
+
const files = readdirSync(cacheDir).filter((name) => name.endsWith('.style.js'))
|
|
198
|
+
// common first so variants layer their diffs on top of it.
|
|
199
|
+
for (const name of files.toSorted(commonFirst)) {
|
|
200
|
+
evaluateGeneratedFile(path.join(cacheDir, name))
|
|
201
|
+
}
|
|
202
|
+
evaluateGeneratedFile(path.join(cacheDir, 'schemes.js'))
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Move every `const {…} = __rnwind;` / `const {…} = __reactNative;`
|
|
207
|
+
* binding line to the top of the source, preserving order, so they
|
|
208
|
+
* initialise before the transformer's `const View = _rnwWrap(_rnw0)`
|
|
209
|
+
* wrap declarations reference them. Mirrors ESM import hoisting.
|
|
210
|
+
* @param source Source with imports already converted to const-destructures.
|
|
211
|
+
* @returns Source with binding consts hoisted to the front.
|
|
212
|
+
*/
|
|
213
|
+
function hoistBindingConsts(source: string): string {
|
|
214
|
+
const hoisted: string[] = []
|
|
215
|
+
const rest: string[] = []
|
|
216
|
+
for (const line of source.split('\n')) {
|
|
217
|
+
if (/=\s*__(?:rnwind|reactNative);\s*$/.test(line)) hoisted.push(line)
|
|
218
|
+
else rest.push(line)
|
|
219
|
+
}
|
|
220
|
+
return [...hoisted, ...rest].join('\n')
|
|
167
221
|
}
|
|
168
222
|
|
|
169
223
|
/**
|
|
170
224
|
* Evaluate the transformer's rewritten source as a standalone module:
|
|
171
|
-
* strip synthetic imports, forward `import ... from 'rnwind'`
|
|
172
|
-
* `'react-native'` to local
|
|
173
|
-
* capture the default export.
|
|
225
|
+
* strip synthetic generated imports, forward `import ... from 'rnwind'`
|
|
226
|
+
* (incl. the injected `wrap as _rnwWrap`) and `'react-native'` to local
|
|
227
|
+
* bindings, compile JSX via the compiler, and capture the default export.
|
|
228
|
+
* Aliased named imports (`{ View as _rnw0 }`) are converted to valid
|
|
229
|
+
* destructuring (`{ View: _rnw0 }`). The import-derived `const`s are then
|
|
230
|
+
* hoisted above the body: the transformer injects `const View =
|
|
231
|
+
* _rnwWrap(_rnw0)` ahead of the (real-ESM-hoisted) `import … as _rnw0`,
|
|
232
|
+
* so without re-hoisting the binding the eval would hit a TDZ on `_rnw0`.
|
|
174
233
|
* @param transformedSource Post-transformer source.
|
|
175
234
|
* @param reactNative `react-native` namespace bindings to forward.
|
|
176
235
|
* @returns The default-exported component.
|
|
@@ -181,25 +240,15 @@ function evaluateRewrittenModule(
|
|
|
181
240
|
): React.ComponentType<Record<string, unknown>> {
|
|
182
241
|
const prepared = transformedSource
|
|
183
242
|
.replaceAll(/import\s+["']rnwind\/__generated\/[^"']+["'];?\s*\n?/g, '')
|
|
184
|
-
.replaceAll(/import\s+\{([^}]+)\}\s+from\s+["']rnwind["'];?/g,
|
|
185
|
-
.replaceAll(/import\s+\{([^}]+)\}\s+from\s+["']react-native["'];?/g,
|
|
243
|
+
.replaceAll(/import\s+\{([^}]+)\}\s+from\s+["']rnwind["'];?/g, (_m, spec: string) => `const {${spec.replaceAll(/\bas\b/g, ':')}} = __rnwind;`)
|
|
244
|
+
.replaceAll(/import\s+\{([^}]+)\}\s+from\s+["']react-native["'];?/g, (_m, spec: string) => `const {${spec.replaceAll(/\bas\b/g, ':')}} = __reactNative;`)
|
|
186
245
|
.replace(/export\s+default\s+/, 'module.exports.default = ')
|
|
187
246
|
|
|
188
|
-
const compiled = compileToJs(prepared)
|
|
189
|
-
const rnwindEnv = {
|
|
190
|
-
lookupCss,
|
|
191
|
-
useRnwind,
|
|
192
|
-
useR_,
|
|
193
|
-
useMountHaptic,
|
|
194
|
-
triggerHaptic,
|
|
195
|
-
useInteract: () => NOOP_INTERACT,
|
|
196
|
-
chainPress: NOOP_CHAIN,
|
|
197
|
-
chainFocus: NOOP_CHAIN,
|
|
198
|
-
}
|
|
247
|
+
const compiled = compileToJs(hoistBindingConsts(prepared))
|
|
199
248
|
const moduleObject: { exports: { default?: React.ComponentType<Record<string, unknown>> } } = { exports: {} }
|
|
200
249
|
// Compiled source originates from rnwind's own transformer + the JSX compiler.
|
|
201
250
|
// eslint-disable-next-line sonarjs/code-eval
|
|
202
|
-
new Function('React', '__rnwind', '__reactNative', 'module', compiled)(React,
|
|
251
|
+
new Function('React', '__rnwind', '__reactNative', 'module', compiled)(React, rnwindRuntime, reactNative, moduleObject)
|
|
203
252
|
if (!moduleObject.exports.default) {
|
|
204
253
|
throw new Error('rnwind/testing: evaluated module did not export a default component.')
|
|
205
254
|
}
|
|
@@ -287,11 +336,12 @@ async function bootstrapRnwindRuntime(options: { themeCss?: string; source?: str
|
|
|
287
336
|
const ast: BabelFile = parse(options.source, { sourceType: 'module', plugins: ['typescript', 'jsx'] })
|
|
288
337
|
const result = await metroTransform({ filename, src: options.source, options: { projectRoot }, ast })
|
|
289
338
|
transformedSource = generate(result.ast).code
|
|
290
|
-
|
|
339
|
+
evaluateGeneratedRegistries(cacheDir)
|
|
291
340
|
}
|
|
292
341
|
|
|
293
342
|
const cleanup = (): void => {
|
|
294
343
|
__resetLookupCssState()
|
|
344
|
+
__resetResolveState()
|
|
295
345
|
resetRnwindState()
|
|
296
346
|
if (existsSync(projectRoot)) rmSync(projectRoot, { recursive: true, force: true })
|
|
297
347
|
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Text-truncate atom detector.
|
|
5
|
-
*
|
|
6
|
-
* React Native's `<Text>` exposes two props that cover every case
|
|
7
|
-
* Tailwind's text-truncation utilities express:
|
|
8
|
-
*
|
|
9
|
-
* numberOfLines — clamp after N lines (1 for single-line ellipsis)
|
|
10
|
-
* ellipsizeMode — 'tail' (ellipsis) or 'clip' at the truncation point
|
|
11
|
-
*
|
|
12
|
-
* rnwind treats the Tailwind classes as **metadata**: the transformer
|
|
13
|
-
* strips the truncate atoms from the JSX site's className and emits
|
|
14
|
-
* `numberOfLines={N}` / `ellipsizeMode="tail"` props on the element.
|
|
15
|
-
*
|
|
16
|
-
* Covered atoms:
|
|
17
|
-
* truncate → { numberOfLines: 1, ellipsizeMode: 'tail' }
|
|
18
|
-
* text-ellipsis → { ellipsizeMode: 'tail' }
|
|
19
|
-
* text-clip → { ellipsizeMode: 'clip' }
|
|
20
|
-
* line-clamp-<N> → { numberOfLines: <N> }
|
|
21
|
-
* line-clamp-none → { numberOfLines: 0 } (reset — overrides prior)
|
|
22
|
-
*
|
|
23
|
-
* Detection is pure name matching — no CSS inspection — because every
|
|
24
|
-
* relevant piece of data lives in the class name itself.
|
|
25
|
-
*/
|
|
26
|
-
/** Regex matching `line-clamp-<N>` with a positive-integer N. */
|
|
27
|
-
const LINE_CLAMP_RE = /^line-clamp-(\d+)$/;
|
|
28
|
-
/** Regex matching Tailwind v4's `line-clamp-[<value>]` arbitrary form. */
|
|
29
|
-
const LINE_CLAMP_ARBITRARY_RE = /^line-clamp-\[([^\]]+)\]$/;
|
|
30
|
-
/**
|
|
31
|
-
* Inspect one class-name token and return the text-truncate metadata it
|
|
32
|
-
* contributes, or `null` when the atom isn't a truncate utility.
|
|
33
|
-
* @param atom Single class-name token (no variant prefix).
|
|
34
|
-
* @returns The atom's contribution, or null.
|
|
35
|
-
*/
|
|
36
|
-
function detectTextTruncate(atom) {
|
|
37
|
-
if (atom === 'truncate')
|
|
38
|
-
return { numberOfLines: 1, ellipsizeMode: 'tail' };
|
|
39
|
-
if (atom === 'text-ellipsis')
|
|
40
|
-
return { ellipsizeMode: 'tail' };
|
|
41
|
-
if (atom === 'text-clip')
|
|
42
|
-
return { ellipsizeMode: 'clip' };
|
|
43
|
-
if (atom === 'line-clamp-none')
|
|
44
|
-
return { numberOfLines: 0 };
|
|
45
|
-
const numeric = LINE_CLAMP_RE.exec(atom);
|
|
46
|
-
if (numeric)
|
|
47
|
-
return { numberOfLines: Number(numeric[1]) };
|
|
48
|
-
const arbitrary = LINE_CLAMP_ARBITRARY_RE.exec(atom);
|
|
49
|
-
if (arbitrary) {
|
|
50
|
-
const n = Number.parseInt(arbitrary[1], 10);
|
|
51
|
-
if (Number.isFinite(n) && n >= 0)
|
|
52
|
-
return { numberOfLines: n };
|
|
53
|
-
}
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Fast pre-check — returns true when ANY atom in the list could be a
|
|
58
|
-
* truncate utility, false when none can. Lets callers skip the
|
|
59
|
-
* allocation of the merge pass for the common "no truncate" case.
|
|
60
|
-
* @param atoms Tokenised atom list from a literal className.
|
|
61
|
-
* @returns Whether to run the full per-atom detection.
|
|
62
|
-
*/
|
|
63
|
-
function mayContainTextTruncate(atoms) {
|
|
64
|
-
for (const atom of atoms) {
|
|
65
|
-
if (atom === 'truncate' ||
|
|
66
|
-
atom === 'text-ellipsis' ||
|
|
67
|
-
atom === 'text-clip' ||
|
|
68
|
-
atom === 'line-clamp-none' ||
|
|
69
|
-
atom.startsWith('line-clamp-')) {
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
exports.detectTextTruncate = detectTextTruncate;
|
|
77
|
-
exports.mayContainTextTruncate = mayContainTextTruncate;
|
|
78
|
-
//# sourceMappingURL=text-truncate.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"text-truncate.cjs","sources":["../../../../../src/core/parser/text-truncate.ts"],"sourcesContent":["/**\n * Text-truncate atom detector.\n *\n * React Native's `<Text>` exposes two props that cover every case\n * Tailwind's text-truncation utilities express:\n *\n * numberOfLines — clamp after N lines (1 for single-line ellipsis)\n * ellipsizeMode — 'tail' (ellipsis) or 'clip' at the truncation point\n *\n * rnwind treats the Tailwind classes as **metadata**: the transformer\n * strips the truncate atoms from the JSX site's className and emits\n * `numberOfLines={N}` / `ellipsizeMode=\"tail\"` props on the element.\n *\n * Covered atoms:\n * truncate → { numberOfLines: 1, ellipsizeMode: 'tail' }\n * text-ellipsis → { ellipsizeMode: 'tail' }\n * text-clip → { ellipsizeMode: 'clip' }\n * line-clamp-<N> → { numberOfLines: <N> }\n * line-clamp-none → { numberOfLines: 0 } (reset — overrides prior)\n *\n * Detection is pure name matching — no CSS inspection — because every\n * relevant piece of data lives in the class name itself.\n */\n\n/** Text-truncate metadata produced by a single atom. */\nexport interface TextTruncateInfo {\n readonly numberOfLines?: number\n readonly ellipsizeMode?: 'tail' | 'clip'\n}\n\n/** Regex matching `line-clamp-<N>` with a positive-integer N. */\nconst LINE_CLAMP_RE = /^line-clamp-(\\d+)$/\n/** Regex matching Tailwind v4's `line-clamp-[<value>]` arbitrary form. */\nconst LINE_CLAMP_ARBITRARY_RE = /^line-clamp-\\[([^\\]]+)\\]$/\n\n/**\n * Inspect one class-name token and return the text-truncate metadata it\n * contributes, or `null` when the atom isn't a truncate utility.\n * @param atom Single class-name token (no variant prefix).\n * @returns The atom's contribution, or null.\n */\nfunction detectTextTruncate(atom: string): TextTruncateInfo | null {\n if (atom === 'truncate') return { numberOfLines: 1, ellipsizeMode: 'tail' }\n if (atom === 'text-ellipsis') return { ellipsizeMode: 'tail' }\n if (atom === 'text-clip') return { ellipsizeMode: 'clip' }\n if (atom === 'line-clamp-none') return { numberOfLines: 0 }\n const numeric = LINE_CLAMP_RE.exec(atom)\n if (numeric) return { numberOfLines: Number(numeric[1]) }\n const arbitrary = LINE_CLAMP_ARBITRARY_RE.exec(atom)\n if (arbitrary) {\n const n = Number.parseInt(arbitrary[1]!, 10)\n if (Number.isFinite(n) && n >= 0) return { numberOfLines: n }\n }\n return null\n}\n\n/**\n * Fast pre-check — returns true when ANY atom in the list could be a\n * truncate utility, false when none can. Lets callers skip the\n * allocation of the merge pass for the common \"no truncate\" case.\n * @param atoms Tokenised atom list from a literal className.\n * @returns Whether to run the full per-atom detection.\n */\nfunction mayContainTextTruncate(atoms: readonly string[]): boolean {\n for (const atom of atoms) {\n if (\n atom === 'truncate' ||\n atom === 'text-ellipsis' ||\n atom === 'text-clip' ||\n atom === 'line-clamp-none' ||\n atom.startsWith('line-clamp-')\n ) {\n return true\n }\n }\n return false\n}\n\nexport { detectTextTruncate, mayContainTextTruncate }\n"],"names":[],"mappings":";;AAAA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AAQH;AACA,MAAM,aAAa,GAAG,oBAAoB;AAC1C;AACA,MAAM,uBAAuB,GAAG,2BAA2B;AAE3D;;;;;AAKG;AACH,SAAS,kBAAkB,CAAC,IAAY,EAAA;IACtC,IAAI,IAAI,KAAK,UAAU;QAAE,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE;IAC3E,IAAI,IAAI,KAAK,eAAe;AAAE,QAAA,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE;IAC9D,IAAI,IAAI,KAAK,WAAW;AAAE,QAAA,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE;IAC1D,IAAI,IAAI,KAAK,iBAAiB;AAAE,QAAA,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE;IAC3D,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;AACxC,IAAA,IAAI,OAAO;QAAE,OAAO,EAAE,aAAa,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE;IACzD,MAAM,SAAS,GAAG,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC;IACpD,IAAI,SAAS,EAAE;AACb,QAAA,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAE,EAAE,EAAE,CAAC;QAC5C,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;AAAE,YAAA,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE;IAC/D;AACA,IAAA,OAAO,IAAI;AACb;AAEA;;;;;;AAMG;AACH,SAAS,sBAAsB,CAAC,KAAwB,EAAA;AACtD,IAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;QACxB,IACE,IAAI,KAAK,UAAU;AACnB,YAAA,IAAI,KAAK,eAAe;AACxB,YAAA,IAAI,KAAK,WAAW;AACpB,YAAA,IAAI,KAAK,iBAAiB;AAC1B,YAAA,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAC9B;AACA,YAAA,OAAO,IAAI;QACb;IACF;AACA,IAAA,OAAO,KAAK;AACd;;;;;"}
|