diff --git a/packages/nativewind/__tests__/extract.ts b/packages/nativewind/__tests__/extract.ts index 3927a1a..c9a53df 100644 --- a/packages/nativewind/__tests__/extract.ts +++ b/packages/nativewind/__tests__/extract.ts @@ -16,8 +16,6 @@ const expectStyle = (style: string, config?: Partial, css?: string) => { `@tailwind components;@tailwind utilities;${css ?? ""}` ); - console.log(createOptions); - return expect(createOptions); }; @@ -36,6 +34,68 @@ type OutputObject = type CaseOutput = Atom | CreateOptions | OutputObject; const cases: Record = { + "text-white": { + css: `:root { + --number: 255; + --string: string; + --unit: 123vw; + --rgb: rgb(255, 255, 255); + --default-value: var(--value, 2); + --rgb-var: rgb(255, 255, var(--number)); + --inline-theme-value: var(--error-color,platformColor(ios__systemRed,android__colorError,default__red)); + font-size: 16; + padding: 1px; + }`, + output: { + ":root": { + variables: [ + { + "--number": 255, + "--string": "string", + "--rem": 16, + "--rgb": "rgb(255, 255, 255)", + "--unit": { + function: "vw", + values: [123], + }, + "--rgb-var": { + function: "inbuilt", + values: [ + "rgb", + 255, + 255, + { + function: "var", + values: ["--number"], + }, + ], + }, + "--default-value": { + function: "var", + values: ["--value", 2], + }, + "--inline-theme-value": { + function: "var", + values: [ + "--error-color", + { + function: "platformColor", + values: [ + "ios__systemRed", + "android__colorError", + "default__red", + ], + }, + ], + }, + }, + ], + }, + "text-white": { + styles: [{ color: "#fff" }], + }, + }, + }, // "text-apply": { // config: { // plugins: [ @@ -95,30 +155,30 @@ const cases: Record = { // styles: [{ marginLeft: 8, marginTop: 8 }], // }, // }, - "text-[color:hsl(var(--hue),var(--saturation),var(--lightness))]": { - css: `:root { --hue: 255; }`, - output: { - ":root": { - variables: [{ "--hue": 255 }], - }, - "text-[color:hsl(var(--hue),var(--saturation),var(--lightness))]": { - styles: [ - { - color: { - function: "inbuilt", - values: [ - "hsl", - { function: "var", values: ["--hue"] }, - { function: "var", values: ["--saturation"] }, - { function: "var", values: ["--lightness"] }, - ], - }, - }, - ], - topics: ["--hue", "--saturation", "--lightness"], - }, - }, - }, + // "text-[color:hsl(var(--hue),var(--saturation),var(--lightness))]": { + // css: `:root { --hue: 255; }`, + // output: { + // ":root": { + // variables: [{ "--hue": 255 }], + // }, + // "text-[color:hsl(var(--hue),var(--saturation),var(--lightness))]": { + // styles: [ + // { + // color: { + // function: "inbuilt", + // values: [ + // "hsl", + // { function: "var", values: ["--hue"] }, + // { function: "var", values: ["--saturation"] }, + // { function: "var", values: ["--lightness"] }, + // ], + // }, + // }, + // ], + // topics: ["--hue", "--saturation", "--lightness"], + // }, + // }, + // }, // "w-screen": { // styles: [{ width: { function: "vw", values: [100] } }], // }, diff --git a/packages/nativewind/src/postcss/declarations.ts b/packages/nativewind/src/postcss/declarations.ts index 33c9143..dae3541 100644 --- a/packages/nativewind/src/postcss/declarations.ts +++ b/packages/nativewind/src/postcss/declarations.ts @@ -1,9 +1,10 @@ /* eslint-disable unicorn/no-lonely-if */ -import { Block, CssNode, Declaration, walk } from "css-tree"; -import { Atom, AtomStyle, StyleWithFunction } from "../style-sheet"; +import { Block, CssNode, Declaration, walk, parse } from "css-tree"; +import { Atom, StyleWithFunction, VariableValue } from "../style-sheet"; import { validProperties } from "./valid-styles"; import { TransformsStyle } from "react-native"; +import { flatten } from "./flatten"; export type StylesAndTopics = Required< Pick @@ -58,7 +59,20 @@ export function getDeclarations(block: Block) { default: { if (node.value.type === "Raw") { if (node.property.startsWith("--")) { - atom.variables.push({ [node.property]: node.value.value.trim() }); + const ast = parse(node.value.value.trim(), { context: "value" }); + + if (ast.type !== "Value") { + return; + } + + const value = parseStyleValue(ast.children.toArray()[0], []); + if (typeof value === "object" && !("function" in value)) { + return; + } + + if (value !== undefined) { + atom.variables.push({ [node.property]: value }); + } } } else { return pushStyle( @@ -72,17 +86,9 @@ export function getDeclarations(block: Block) { }, }); - let styles: AtomStyle = {}; - - for (const style of atom.styles) { - for (const [key, value] of Object.entries(style)) { - styles = setValue(styles, key, value); - } - } - return { ...atom, - styles, + styles: flatten(atom.styles), }; } @@ -116,10 +122,11 @@ function parseStyleValue( node: StyleValue | null | undefined, topics: string[] ): StyleValue | StyleValue[] | undefined { - if (!node) return []; + if (!node) return; if (typeof node === "string") { - return node; + const maybeNumber = Number.parseFloat(node); + return Number.isNaN(maybeNumber) ? node : maybeNumber; } if (typeof node === "number") { @@ -163,16 +170,54 @@ function parseStyleValue( switch (node.name) { case "pixelRatio": return { function: "pixelRatio", values: [] }; + case "platformColor": { + const children = node.children + .toArray() + .map((child) => parseStyleValue(child, topics)) + .filter((child) => Boolean(child)); + + return { + function: "platformColor", + values: children as unknown as VariableValue[], + }; + } case "var": { - const value = parseStyleValue(node.children.shift()?.data, topics); + const children = node.children.toArray(); + const variableName = parseStyleValue(children[0], topics); - if (typeof value !== "string") return []; + if (typeof variableName !== "string") return []; - topics.push(value); + const values: StyleWithFunction["values"] = [variableName]; + topics.push(variableName); + + if (children.length === 3) { + const defaultChild = children[2]; + + if (defaultChild.type === "Raw") { + const ast = parse(defaultChild.value, { + context: "value", + }); + + if (ast.type === "Value") { + const defaultValue = parseStyleValue( + ast.children.toArray()[0], + [] + ); + + if (typeof defaultValue === "object") { + if ("function" in defaultValue) { + values.push(defaultValue); + } + } else if (defaultValue) { + values.push(defaultValue); + } + } + } + } return { function: "var", - values: [value], + values, }; } default: { @@ -204,26 +249,6 @@ function parseStyleValue( ) as unknown as Transform; } -function setValue>( - object: T, - is: string | string[], - value: unknown -): T { - if (typeof is == "string") { - return setValue(object, is.split("."), value); - } else if (is.length == 1) { - (object as Record)[is[0]] = value; - return object; - } else { - (object as Record)[is[0]] = setValue( - (object[is[0]] || {}) as T, - is.slice(1), - value - ); - return object; - } -} - function border(atom: StylesAndTopics, node: Declaration) { if (node.value.type !== "Value") { return; diff --git a/packages/nativewind/src/postcss/extract.ts b/packages/nativewind/src/postcss/extract.ts index 5801581..b26a1ea 100644 --- a/packages/nativewind/src/postcss/extract.ts +++ b/packages/nativewind/src/postcss/extract.ts @@ -8,6 +8,7 @@ import { CreateOptions } from "../style-sheet"; import { parseMediaQuery, MediaQueryMeta } from "./media-query"; import { getDeclarations } from "./declarations"; import { getSelector } from "./selector"; +import { flatten } from "./flatten"; const skip = (walk as unknown as Record).skip; @@ -105,13 +106,11 @@ function addRule( // Invalid selector, skip it if (!selector) return; - if (selector === ":root") { - createOptions[selector] ??= { variables }; - return; - } - - if (selector === "dark") { - createOptions[selector] ??= { variables }; + if (selector === ":root" || selector === "dark") { + if (styles.fontSize) { + variables.push({ "--rem": styles.fontSize }); + } + createOptions[selector] ??= { variables: [flatten(variables)] }; return; } diff --git a/packages/nativewind/src/postcss/flatten.ts b/packages/nativewind/src/postcss/flatten.ts new file mode 100644 index 0000000..44a429f --- /dev/null +++ b/packages/nativewind/src/postcss/flatten.ts @@ -0,0 +1,32 @@ +export function flatten>( + objectArray: T[] +): T { + let returnObject = {} as T; + for (const object of objectArray) { + for (const [key, value] of Object.entries(object)) { + returnObject = setValue(returnObject, key, value); + } + } + + return returnObject; +} + +function setValue>( + object: T, + is: string | string[], + value: unknown +): T { + if (typeof is == "string") { + return setValue(object, is.split("."), value); + } else if (is.length == 1) { + (object as Record)[is[0]] = value; + return object; + } else { + (object as Record)[is[0]] = setValue( + (object[is[0]] || {}) as T, + is.slice(1), + value + ); + return object; + } +} diff --git a/packages/nativewind/src/style-sheet/index.ts b/packages/nativewind/src/style-sheet/index.ts index 1279e26..f5a68e1 100644 --- a/packages/nativewind/src/style-sheet/index.ts +++ b/packages/nativewind/src/style-sheet/index.ts @@ -24,7 +24,7 @@ export type AtomStyle = { export type StyleWithFunction = { function: string; - values: Array; + values: Array; }; export type VariableValue =