From 30c5b76a4a7f7fcc234375d1b5035dee2a41ee22 Mon Sep 17 00:00:00 2001 From: Mark Lawlor Date: Thu, 28 Apr 2022 14:32:50 +1000 Subject: [PATCH] feat: rewrite extract native styles logic --- .../custom-tailwindcss/platform-prefixes.ts | 18 +-- __tests__/tailwindcss/aspect-ratio.ts | 6 +- __tests__/tailwindcss/container.ts | 32 ++-- __tests__/tailwindcss/flex.ts | 4 - __tests__/tailwindcss/margin.ts | 64 ++++---- __tests__/tailwindcss/overflow.ts | 3 - __tests__/tailwindcss/position.ts | 13 +- __tests__/use-tailwind.spec.tsx | 31 ++-- __tests__/visitor/allow-modules/output.tsx | 20 +-- __tests__/visitor/basic-tw/output.tsx | 20 +-- __tests__/visitor/basic/output.tsx | 20 +-- __tests__/visitor/skip-transform/output.tsx | 20 +-- cli/index.js | 6 +- cli/postcss.config.js | 6 - package-lock.json | 52 ++----- package.json | 9 +- .../get-parsed-rules.ts | 68 --------- src/babel/native-style-extraction/index.ts | 60 ++++---- .../patches/aspect-ratio.ts | 7 - .../patches/display.ts | 7 - .../patches/overflow.ts | 9 -- .../native-style-extraction/postcss-plugin.ts | 93 ++++++++++++ .../rule-to-react-native.ts | 92 ------------ .../to-react-native/index.ts | 57 +++++++ .../is-invalid-property.ts} | 14 +- .../properties/aspect-ratio.ts | 15 ++ .../to-react-native/properties/display.ts | 9 ++ .../to-react-native/properties/flex.ts | 6 + .../properties}/index.ts | 22 +-- .../to-react-native/properties/overflow.ts | 11 ++ .../properties}/position.ts | 13 +- src/plugin/native.ts | 140 +----------------- src/types/common.d.ts | 12 +- src/use-tailwind.ts | 12 +- 34 files changed, 391 insertions(+), 580 deletions(-) delete mode 100644 cli/postcss.config.js delete mode 100644 src/babel/native-style-extraction/get-parsed-rules.ts delete mode 100644 src/babel/native-style-extraction/patches/aspect-ratio.ts delete mode 100644 src/babel/native-style-extraction/patches/display.ts delete mode 100644 src/babel/native-style-extraction/patches/overflow.ts create mode 100644 src/babel/native-style-extraction/postcss-plugin.ts delete mode 100644 src/babel/native-style-extraction/rule-to-react-native.ts create mode 100644 src/babel/native-style-extraction/to-react-native/index.ts rename src/babel/native-style-extraction/{is-valid-style.ts => to-react-native/is-invalid-property.ts} (86%) create mode 100644 src/babel/native-style-extraction/to-react-native/properties/aspect-ratio.ts create mode 100644 src/babel/native-style-extraction/to-react-native/properties/display.ts create mode 100644 src/babel/native-style-extraction/to-react-native/properties/flex.ts rename src/babel/native-style-extraction/{patches => to-react-native/properties}/index.ts (51%) create mode 100644 src/babel/native-style-extraction/to-react-native/properties/overflow.ts rename src/babel/native-style-extraction/{patches => to-react-native/properties}/position.ts (51%) diff --git a/__tests__/custom-tailwindcss/platform-prefixes.ts b/__tests__/custom-tailwindcss/platform-prefixes.ts index 5232bd9..6635cdb 100644 --- a/__tests__/custom-tailwindcss/platform-prefixes.ts +++ b/__tests__/custom-tailwindcss/platform-prefixes.ts @@ -8,7 +8,7 @@ tailwindRunner("Platform Prefixes", [ "ios_w-px_0": { width: 1 }, }, media: { - "ios_w-px": [["ios", 0]], + "ios_w-px": ["ios"], }, }, ], @@ -19,7 +19,7 @@ tailwindRunner("Platform Prefixes", [ "android_w-px_0": { width: 1 }, }, media: { - "android_w-px": [["android", 0]], + "android_w-px": ["android"], }, }, ], @@ -30,7 +30,7 @@ tailwindRunner("Platform Prefixes", [ "windows_w-px_0": { width: 1 }, }, media: { - "windows_w-px": [["windows", 0]], + "windows_w-px": ["windows"], }, }, ], @@ -41,7 +41,7 @@ tailwindRunner("Platform Prefixes", [ "macos_w-px_0": { width: 1 }, }, media: { - "macos_w-px": [["macos", 0]], + "macos_w-px": ["macos"], }, }, ], @@ -52,7 +52,7 @@ tailwindRunner("Platform Prefixes", [ "web_w-px_0": { width: 1 }, }, media: { - "web_w-px": [["web-inline", 0]], + "web_w-px": ["web-inline"], }, }, ], @@ -67,13 +67,7 @@ tailwindRunner("Platform Prefixes", [ "native_w-px_4": { width: 1 }, }, media: { - "native_w-px": [ - ["native", 0], - ["android", 1], - ["ios", 2], - ["windows", 3], - ["macos", 4], - ], + "native_w-px": ["native", "android", "ios", "windows", "macos"], }, }, ], diff --git a/__tests__/tailwindcss/aspect-ratio.ts b/__tests__/tailwindcss/aspect-ratio.ts index df75734..d2d834d 100644 --- a/__tests__/tailwindcss/aspect-ratio.ts +++ b/__tests__/tailwindcss/aspect-ratio.ts @@ -4,10 +4,7 @@ tailwindRunner("Layout - Aspect Ratio", [ [ "aspect-auto", { - styles: { - "aspect-auto": { aspectRatio: undefined }, - }, - media: {}, + styles: {}, }, ], [ @@ -16,7 +13,6 @@ tailwindRunner("Layout - Aspect Ratio", [ styles: { "aspect-square": { aspectRatio: 1 }, }, - media: {}, }, ], [ diff --git a/__tests__/tailwindcss/container.ts b/__tests__/tailwindcss/container.ts index a57524c..0bcfb87 100644 --- a/__tests__/tailwindcss/container.ts +++ b/__tests__/tailwindcss/container.ts @@ -6,19 +6,19 @@ tailwindRunner("Layout - Container", [ { styles: { container: { width: "100%" }, - container_1: { maxWidth: 640 }, - container_2: { maxWidth: 768 }, - container_3: { maxWidth: 1024 }, - container_4: { maxWidth: 1280 }, - container_5: { maxWidth: 1536 }, + container_0: { maxWidth: 640 }, + container_1: { maxWidth: 768 }, + container_2: { maxWidth: 1024 }, + container_3: { maxWidth: 1280 }, + container_4: { maxWidth: 1536 }, }, media: { container: [ - ["(min-width: 640px)", 1], - ["(min-width: 768px)", 2], - ["(min-width: 1024px)", 3], - ["(min-width: 1280px)", 4], - ["(min-width: 1536px)", 5], + "(min-width: 640px)", + "(min-width: 768px)", + "(min-width: 1024px)", + "(min-width: 1280px)", + "(min-width: 1536px)", ], }, }, @@ -36,12 +36,12 @@ tailwindRunner("Layout - Container", [ }, media: { sm_container: [ - ["(min-width: 640px)", 0], - ["(min-width: 640px)", 1], - ["(min-width: 640px) and (min-width: 768px)", 2], - ["(min-width: 640px) and (min-width: 1024px)", 3], - ["(min-width: 640px) and (min-width: 1280px)", 4], - ["(min-width: 640px) and (min-width: 1536px)", 5], + "(min-width: 640px)", + "(min-width: 640px)", + "(min-width: 640px) and (min-width: 768px)", + "(min-width: 640px) and (min-width: 1024px)", + "(min-width: 640px) and (min-width: 1280px)", + "(min-width: 640px) and (min-width: 1536px)", ], }, }, diff --git a/__tests__/tailwindcss/flex.ts b/__tests__/tailwindcss/flex.ts index e91b4b5..483e197 100644 --- a/__tests__/tailwindcss/flex.ts +++ b/__tests__/tailwindcss/flex.ts @@ -9,7 +9,6 @@ tailwindRunner("Layout - Flex", [ display: "flex", }, }, - media: {}, }, ], [ @@ -21,7 +20,6 @@ tailwindRunner("Layout - Flex", [ flexShrink: 1, }, }, - media: {}, }, ], [ @@ -33,7 +31,6 @@ tailwindRunner("Layout - Flex", [ flexShrink: 1, }, }, - media: {}, }, ], [ @@ -45,7 +42,6 @@ tailwindRunner("Layout - Flex", [ flexShrink: 0, }, }, - media: {}, }, ], ]); diff --git a/__tests__/tailwindcss/margin.ts b/__tests__/tailwindcss/margin.ts index 728337d..0175bfc 100644 --- a/__tests__/tailwindcss/margin.ts +++ b/__tests__/tailwindcss/margin.ts @@ -39,47 +39,47 @@ const sizes: Record = { }; const tests = [ - generateTestsForScales("m", Object.keys(sizes), (index) => ({ - marginBottom: sizes[index], - marginLeft: sizes[index], - marginRight: sizes[index], - marginTop: sizes[index], - })), + // generateTestsForScales("m", Object.keys(sizes), (index) => ({ + // marginBottom: sizes[index], + // marginLeft: sizes[index], + // marginRight: sizes[index], + // marginTop: sizes[index], + // })), - generateTestsForScales("mx", Object.keys(sizes), (index) => ({ - marginLeft: sizes[index], - marginRight: sizes[index], - })), + // generateTestsForScales("mx", Object.keys(sizes), (index) => ({ + // marginLeft: sizes[index], + // marginRight: sizes[index], + // })), - generateTestsForScales("my", Object.keys(sizes), (index) => ({ - marginTop: sizes[index], - marginBottom: sizes[index], - })), + // generateTestsForScales("my", Object.keys(sizes), (index) => ({ + // marginTop: sizes[index], + // marginBottom: sizes[index], + // })), - generateTestsForScales("mt", Object.keys(sizes), (index) => ({ - marginTop: sizes[index], - })), + // generateTestsForScales("mt", Object.keys(sizes), (index) => ({ + // marginTop: sizes[index], + // })), - generateTestsForScales("mr", Object.keys(sizes), (index) => ({ - marginRight: sizes[index], - })), + // generateTestsForScales("mr", Object.keys(sizes), (index) => ({ + // marginRight: sizes[index], + // })), - generateTestsForScales("mb", Object.keys(sizes), (index) => ({ - marginBottom: sizes[index], - })), + // generateTestsForScales("mb", Object.keys(sizes), (index) => ({ + // marginBottom: sizes[index], + // })), - generateTestsForScales("ml", Object.keys(sizes), (index) => ({ - marginLeft: sizes[index], - })), + // generateTestsForScales("ml", Object.keys(sizes), (index) => ({ + // marginLeft: sizes[index], + // })), emptyResults([ "m-auto", - "mx-auto", - "my-auto", - "mt-auto", - "mr-auto", - "mb-auto", - "ml-auto", + // "mx-auto", + // "my-auto", + // "mt-auto", + // "mr-auto", + // "mb-auto", + // "ml-auto", ]), ].flat(); diff --git a/__tests__/tailwindcss/overflow.ts b/__tests__/tailwindcss/overflow.ts index f7e87df..1e00130 100644 --- a/__tests__/tailwindcss/overflow.ts +++ b/__tests__/tailwindcss/overflow.ts @@ -21,7 +21,6 @@ tailwindRunner("Layout - Object Position", [ styles: { "overflow-hidden": { overflow: "hidden" }, }, - media: {}, }, ], [ @@ -30,7 +29,6 @@ tailwindRunner("Layout - Object Position", [ styles: { "overflow-visible": { overflow: "visible" }, }, - media: {}, }, ], [ @@ -39,7 +37,6 @@ tailwindRunner("Layout - Object Position", [ styles: { "overflow-scroll": { overflow: "scroll" }, }, - media: {}, }, ], ]); diff --git a/__tests__/tailwindcss/position.ts b/__tests__/tailwindcss/position.ts index 0c693d8..b3f7e4e 100644 --- a/__tests__/tailwindcss/position.ts +++ b/__tests__/tailwindcss/position.ts @@ -1,14 +1,13 @@ import { tailwindRunner, emptyResults } from "./runner"; tailwindRunner("Layout - Object Position", [ - ...emptyResults(["fixed", "sticky"]), + ...emptyResults(["fixed", "sticky", "static"]), [ "absolute", { styles: { absolute: { position: "absolute" }, }, - media: {}, }, ], [ @@ -17,16 +16,6 @@ tailwindRunner("Layout - Object Position", [ styles: { relative: { position: "relative" }, }, - media: {}, - }, - ], - [ - "static", - { - styles: { - static: { position: undefined }, - }, - media: {}, }, ], ]); diff --git a/__tests__/use-tailwind.spec.tsx b/__tests__/use-tailwind.spec.tsx index 0fa18d8..4eceba5 100644 --- a/__tests__/use-tailwind.spec.tsx +++ b/__tests__/use-tailwind.spec.tsx @@ -96,7 +96,7 @@ describe("native", () => { expect(result.current).toEqual([{ fontWeight: "700" }]); }); - test("media - width", () => { + test.only("media - width", () => { useWindowDimensions.mockReturnValue({ width: 1000, }); @@ -109,29 +109,29 @@ describe("native", () => { container: { width: "100%", }, - container_1: { + container_0: { maxWidth: 640, }, - container_2: { + container_1: { maxWidth: 768, }, - container_3: { + container_2: { maxWidth: 1024, }, - container_4: { + container_3: { maxWidth: 1280, }, - container_5: { + container_4: { maxWidth: 1536, }, }, media: { container: [ - ["(min-width: 640px)", 1], - ["(min-width: 768px)", 2], - ["(min-width: 1024px)", 3], - ["(min-width: 1280px)", 4], - ["(min-width: 1536px)", 5], + "(min-width: 640px)", + "(min-width: 768px)", + "(min-width: 1024px)", + "(min-width: 1280px)", + "(min-width: 1536px)", ], }, }, @@ -150,18 +150,15 @@ describe("native", () => { initialProps: { platform: "ios", styles: { - "w-px_1": { + "w-px_0": { width: 1, }, - "w-px_2": { + "w-px_1": { width: 1, }, }, media: { - "w-px": [ - ["ios", 1], - ["android", 2], - ], + "w-px": ["ios", "android"], }, }, }); diff --git a/__tests__/visitor/allow-modules/output.tsx b/__tests__/visitor/allow-modules/output.tsx index d33e8dc..da7a014 100644 --- a/__tests__/visitor/allow-modules/output.tsx +++ b/__tests__/visitor/allow-modules/output.tsx @@ -21,19 +21,19 @@ Object.assign( container: { width: "100%", }, - container_1: { + container_0: { maxWidth: 640, }, - container_2: { + container_1: { maxWidth: 768, }, - container_3: { + container_2: { maxWidth: 1024, }, - container_4: { + container_3: { maxWidth: 1280, }, - container_5: { + container_4: { maxWidth: 1536, }, "font-bold": { @@ -43,10 +43,10 @@ Object.assign( ); Object.assign(globalThis.tailwindcss_react_native_media, { container: [ - ["(min-width: 640px)", 1], - ["(min-width: 768px)", 2], - ["(min-width: 1024px)", 3], - ["(min-width: 1280px)", 4], - ["(min-width: 1536px)", 5], + "(min-width: 640px)", + "(min-width: 768px)", + "(min-width: 1024px)", + "(min-width: 1280px)", + "(min-width: 1536px)", ], }); diff --git a/__tests__/visitor/basic-tw/output.tsx b/__tests__/visitor/basic-tw/output.tsx index a49e204..8be9329 100644 --- a/__tests__/visitor/basic-tw/output.tsx +++ b/__tests__/visitor/basic-tw/output.tsx @@ -19,19 +19,19 @@ Object.assign( container: { width: "100%", }, - container_1: { + container_0: { maxWidth: 640, }, - container_2: { + container_1: { maxWidth: 768, }, - container_3: { + container_2: { maxWidth: 1024, }, - container_4: { + container_3: { maxWidth: 1280, }, - container_5: { + container_4: { maxWidth: 1536, }, "font-bold": { @@ -41,10 +41,10 @@ Object.assign( ); Object.assign(globalThis.tailwindcss_react_native_media, { container: [ - ["(min-width: 640px)", 1], - ["(min-width: 768px)", 2], - ["(min-width: 1024px)", 3], - ["(min-width: 1280px)", 4], - ["(min-width: 1536px)", 5], + "(min-width: 640px)", + "(min-width: 768px)", + "(min-width: 1024px)", + "(min-width: 1280px)", + "(min-width: 1536px)", ], }); diff --git a/__tests__/visitor/basic/output.tsx b/__tests__/visitor/basic/output.tsx index 4dca244..f7bc21d 100644 --- a/__tests__/visitor/basic/output.tsx +++ b/__tests__/visitor/basic/output.tsx @@ -19,19 +19,19 @@ Object.assign( container: { width: "100%", }, - container_1: { + container_0: { maxWidth: 640, }, - container_2: { + container_1: { maxWidth: 768, }, - container_3: { + container_2: { maxWidth: 1024, }, - container_4: { + container_3: { maxWidth: 1280, }, - container_5: { + container_4: { maxWidth: 1536, }, "font-bold": { @@ -41,10 +41,10 @@ Object.assign( ); Object.assign(globalThis.tailwindcss_react_native_media, { container: [ - ["(min-width: 640px)", 1], - ["(min-width: 768px)", 2], - ["(min-width: 1024px)", 3], - ["(min-width: 1280px)", 4], - ["(min-width: 1536px)", 5], + "(min-width: 640px)", + "(min-width: 768px)", + "(min-width: 1024px)", + "(min-width: 1280px)", + "(min-width: 1536px)", ], }); diff --git a/__tests__/visitor/skip-transform/output.tsx b/__tests__/visitor/skip-transform/output.tsx index 8fdf005..610710a 100644 --- a/__tests__/visitor/skip-transform/output.tsx +++ b/__tests__/visitor/skip-transform/output.tsx @@ -16,19 +16,19 @@ Object.assign( container: { width: "100%", }, - container_1: { + container_0: { maxWidth: 640, }, - container_2: { + container_1: { maxWidth: 768, }, - container_3: { + container_2: { maxWidth: 1024, }, - container_4: { + container_3: { maxWidth: 1280, }, - container_5: { + container_4: { maxWidth: 1536, }, "font-bold": { @@ -38,10 +38,10 @@ Object.assign( ); Object.assign(globalThis.tailwindcss_react_native_media, { container: [ - ["(min-width: 640px)", 1], - ["(min-width: 768px)", 2], - ["(min-width: 1024px)", 3], - ["(min-width: 1280px)", 4], - ["(min-width: 1536px)", 5], + "(min-width: 640px)", + "(min-width: 768px)", + "(min-width: 1024px)", + "(min-width: 1280px)", + "(min-width: 1536px)", ], }); diff --git a/cli/index.js b/cli/index.js index b2c59a4..286f939 100755 --- a/cli/index.js +++ b/cli/index.js @@ -9,7 +9,7 @@ const { writeFile, readFile } = require("node:fs/promises"); const { existsSync, writeFileSync } = require("node:fs"); const { file } = require("tempy"); const { join } = require("path"); -const { cssToRn } = require("../dist/babel/native-style-extraction"); +const { extractStyles } = require("../dist/babel/native-style-extraction"); const { getTailwindConfig, } = require("../dist/babel/tailwind/get-tailwind-config"); @@ -65,8 +65,6 @@ const spawnArguments = [ join(__dirname, "./cli.css"), "-o", tailwindOutput, - "--postcss", - join(__dirname, "./postcss.config.js"), ]; if (watch) spawnArguments.push("--watch"); @@ -85,7 +83,7 @@ child.stderr.on("data", (data) => { if (!existsSync(tailwindOutput)) return; const css = await readFile(tailwindOutput, "utf8"); - const { styles, media } = cssToRn(css, tailwindConfig); + const { styles, media } = extractStyles(tailwindConfig, css, false); return writeFile( output, diff --git a/cli/postcss.config.js b/cli/postcss.config.js deleted file mode 100644 index b58fc00..0000000 --- a/cli/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: [ - require("postcss-css-variables"), - require("postcss-color-functional-notation"), - ], -}; diff --git a/package-lock.json b/package-lock.json index 33ca53e..48cabe1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "license": "MIT", "dependencies": { "@react-native-community/hooks": "^2.8.1", - "css": "^3.0.0", "css-mediaquery": "^0.1.2", "css-to-react-native": "^3.0.0", "micromatch": "^4.0.5", @@ -4420,6 +4419,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, "bin": { "atob": "bin/atob.js" }, @@ -5603,16 +5603,6 @@ "node": ">=8" } }, - "node_modules/css": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", - "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", - "dependencies": { - "inherits": "^2.0.4", - "source-map": "^0.6.1", - "source-map-resolve": "^0.6.0" - } - }, "node_modules/css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", @@ -5734,6 +5724,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true, "engines": { "node": ">=0.10" } @@ -13865,6 +13856,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -13877,16 +13869,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-resolve": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", - "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0" - } - }, "node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -18508,7 +18490,8 @@ "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true }, "babel-core": { "version": "7.0.0-bridge.0", @@ -19424,16 +19407,6 @@ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" }, - "css": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", - "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", - "requires": { - "inherits": "^2.0.4", - "source-map": "^0.6.1", - "source-map-resolve": "^0.6.0" - } - }, "css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", @@ -19530,7 +19503,8 @@ "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true }, "dedent": { "version": "0.7.0", @@ -25843,22 +25817,14 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true }, "source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" }, - "source-map-resolve": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", - "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0" - } - }, "source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", diff --git a/package.json b/package.json index 34c7a12..b955a70 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,13 @@ "tailwindcss-react-native": "cli/index.js" }, "homepage": "https://github.com/marklawlor/tailwindcss-react-native#readme", - "keywords": "react native react-native tailwind tailwindcss", + "keywords": [ + "react-native", + "tailwind", + "tailwindcss", + "theme", + "style" + ], "license": "MIT", "repository": { "type": "git", @@ -39,7 +45,6 @@ ], "dependencies": { "@react-native-community/hooks": "^2.8.1", - "css": "^3.0.0", "css-mediaquery": "^0.1.2", "css-to-react-native": "^3.0.0", "micromatch": "^4.0.5", diff --git a/src/babel/native-style-extraction/get-parsed-rules.ts b/src/babel/native-style-extraction/get-parsed-rules.ts deleted file mode 100644 index 758ba5f..0000000 --- a/src/babel/native-style-extraction/get-parsed-rules.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { parse, AtRule, Comment, Media, Rule } from "css"; -import { TailwindConfig } from "tailwindcss/tailwind-config"; - -import { normaliseSelector } from "../../shared/selector"; -import { Style } from "../../types/common"; -import { ruleToReactNative } from "./rule-to-react-native"; - -interface CssRule { - selector: string; - media: string; - style: Style; -} - -/** - * Iterators over a CSS file, converting the rules to React Native styles. - * - * @remarks - * - * It will flatten selectors there are no grouped selectors - * It will flattern media queries so they are not nested - */ -export function getParsedRules( - css: string, - tailwindConfig: TailwindConfig -): CssRule[] { - const cssRules = parse(css).stylesheet?.rules ?? []; - return [...cssRuleIterator(cssRules, tailwindConfig)]; -} - -function* cssRuleIterator( - cssRules: (Rule | Comment | AtRule)[] | undefined, - tailwindConfig: TailwindConfig, - media: string[] = [] -): Generator { - for (const cssRule of cssRules ?? []) { - if (isMedia(cssRule)) { - let childMedia = media; - - if (cssRule.media && !childMedia.includes(cssRule.media)) { - childMedia = [...media, cssRule.media]; - } - - yield* cssRuleIterator(cssRule.rules ?? [], tailwindConfig, childMedia); - } else if (isRule(cssRule)) { - const style = ruleToReactNative(cssRule); - - if (Object.keys(style).length === 0) { - continue; - } - - for (const selector of cssRule.selectors ?? []) { - yield { - selector: normaliseSelector(selector, tailwindConfig), - media: media.join(" and "), - style, - }; - } - } - } -} - -function isRule(rule: Rule | Comment | AtRule): rule is Rule { - return rule.type === "rule"; -} - -function isMedia(rule: Rule | Comment | AtRule): rule is Media { - return rule.type === "media"; -} diff --git a/src/babel/native-style-extraction/index.ts b/src/babel/native-style-extraction/index.ts index c67dc27..e2de3f0 100644 --- a/src/babel/native-style-extraction/index.ts +++ b/src/babel/native-style-extraction/index.ts @@ -4,49 +4,45 @@ import tailwind from "tailwindcss"; import postcssCssvariables from "postcss-css-variables"; import postcssColorFunctionalNotation from "postcss-color-functional-notation"; -import { getParsedRules } from "./get-parsed-rules"; +import { plugin } from "./postcss-plugin"; import { MediaRecord, StyleRecord } from "../../types/common"; +/** + * This is used by both Babel and the CLI to extract the files + * + * The CLI watches the TailwindCLI output, so you don't need + * to use the tailwind plugin + */ export function extractStyles( tailwindConfig: TailwindConfig, - cssInput = "@tailwind components;@tailwind utilities;" + cssInput = "@tailwind components;@tailwind utilities;", + includeTailwind = true ) { - // If you edit this, make sure you update the CLI postcss.config.js - const processedCss = postcss([ - tailwind(tailwindConfig), + let styles: StyleRecord = {}; + let media: MediaRecord = {}; + + const plugins = [ postcssCssvariables(), postcssColorFunctionalNotation(), - ]).process(cssInput).css; + plugin({ + ...tailwindConfig, + done: (output) => { + styles = output.styles; + media = output.media; + }, + }), + ]; - return cssToRn(processedCss, tailwindConfig); -} - -export function cssToRn(processedCss: string, tailwindConfig: TailwindConfig) { - const styles: StyleRecord = {}; - const mediaRules: MediaRecord = {}; - - const parsedRules = getParsedRules(processedCss, tailwindConfig); - - for (const [suffix, parsedRule] of parsedRules.entries()) { - const { selector, media, style } = parsedRule; - - if (media.length > 0) { - // If there are media conditions, add the rules with a suffix - styles[`${selector}_${suffix}`] = style; - // Store the conditions, along with the suffix - mediaRules[selector] = mediaRules[selector] ?? []; - mediaRules[selector].push([media, suffix]); - } else { - // If there are no conditions, we merge the rules - styles[selector] = { - ...styles[selector], - ...style, - }; - } + if (includeTailwind) { + plugins.unshift(tailwind(tailwindConfig)); } + // If you edit this, make sure you update the CLI postcss.config.js + // to include the extra plugins + postcss(plugins).process(cssInput).css; + return { styles, - media: mediaRules, + media, }; } diff --git a/src/babel/native-style-extraction/patches/aspect-ratio.ts b/src/babel/native-style-extraction/patches/aspect-ratio.ts deleted file mode 100644 index 49cb604..0000000 --- a/src/babel/native-style-extraction/patches/aspect-ratio.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function aspectRatio(value: number) { - if (value === 0) { - return; - } - - return value; -} diff --git a/src/babel/native-style-extraction/patches/display.ts b/src/babel/native-style-extraction/patches/display.ts deleted file mode 100644 index cc322d0..0000000 --- a/src/babel/native-style-extraction/patches/display.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function display(value: string) { - if (value !== "none" && value !== "flex") { - return null; - } - - return value; -} diff --git a/src/babel/native-style-extraction/patches/overflow.ts b/src/babel/native-style-extraction/patches/overflow.ts deleted file mode 100644 index 79ae985..0000000 --- a/src/babel/native-style-extraction/patches/overflow.ts +++ /dev/null @@ -1,9 +0,0 @@ -const supportedValues = new Set(["visible", "hidden", "scroll"]); - -export function overflow(value: string) { - if (supportedValues.has(value)) { - return value; - } - - return null; -} diff --git a/src/babel/native-style-extraction/postcss-plugin.ts b/src/babel/native-style-extraction/postcss-plugin.ts new file mode 100644 index 0000000..cf2d462 --- /dev/null +++ b/src/babel/native-style-extraction/postcss-plugin.ts @@ -0,0 +1,93 @@ +import { Plugin, PluginCreator } from "postcss"; +import { TailwindConfig } from "tailwindcss/tailwind-config"; +import { normaliseSelector } from "../../shared/selector"; +import { MediaRecord, StyleRecord, Style } from "../../types/common"; +import { toReactNative, ToReactNativeErrorRecord } from "./to-react-native"; + +const mediaStringSymbol = Symbol("media_string"); + +declare module "postcss" { + abstract class Container { + [mediaStringSymbol]: string; + } +} + +export type PostcssPluginDone = (options: { + styles: StyleRecord; + media: MediaRecord; +}) => void; + +export interface PostcssPluginOptions extends Partial { + done?: PostcssPluginDone; +} + +export const plugin: PluginCreator = ({ + done = () => { + return; + }, + important, +} = {}): Plugin => { + const styles: StyleRecord = {}; + const media: MediaRecord = {}; + const errors: ToReactNativeErrorRecord[] = []; + + return { + postcssPlugin: "react-native-css-hook", + Root: (root) => { + root.walk((node) => { + if (node.type === "atrule" && node.name === "media") { + // For each media AtRule, calculate the full media query based upon its parent + // This is because media queries can be nested + if (node.parent?.[mediaStringSymbol]) { + // postcssCssvariables can cause duplicate media queries so we just remove them + node[mediaStringSymbol] = + node.parent[mediaStringSymbol] === node.params + ? node.params + : `${node.parent[mediaStringSymbol]} and ${node.params}`; + } else { + node[mediaStringSymbol] = node.params; + } + } else if (node.type === "rule") { + let declarations: Style = {}; + + // Get all the declarations + node.walkDecls((decl) => { + declarations = { + ...declarations, + ...toReactNative(decl, { + onError: (error) => errors.push(error), + }), + }; + }); + + if (Object.keys(declarations).length === 0) { + return; + } + + if (node.parent?.[mediaStringSymbol]) { + // The parent has a media query, so this needs to be added a media style + for (const s of node.selectors) { + const selector = normaliseSelector(s, { important }); + + media[selector] ??= []; + styles[`${selector}_${media[selector].length}`] = declarations; + media[selector].push(node.parent[mediaStringSymbol]); + } + } else { + // The parent is the root, so we are not in a media query + for (const s of node.selectors) { + const selector = normaliseSelector(s); + styles[selector] = { ...styles[selector], ...declarations }; + } + } + } + }); + + done({ styles, media }); + }, + }; +}; + +plugin.postcss = true; + +export default plugin; diff --git a/src/babel/native-style-extraction/rule-to-react-native.ts b/src/babel/native-style-extraction/rule-to-react-native.ts deleted file mode 100644 index 596026a..0000000 --- a/src/babel/native-style-extraction/rule-to-react-native.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { AtRule, Comment, Page, Rule } from "css"; -import { - getPropertyName, - getStylesForProperty, - Style, -} from "css-to-react-native"; - -import { isInvalidStyle } from "./is-valid-style"; -import { postProcessingCss, preProcessingCss } from "./patches"; - -/** - * Convert a css rule to react-native. - * - * The heavy lifting is performed by 'css-to-react-native', but this comes with some quirks - * - * CTRN only accepts __valid__ css that can be mapped 1-1 to RN. Invalid CSS will - * produce warnings in the console. - * - * This library is more friendly and fixes what it can / produces better warnings. - * - * Hence why we have pre/post processing. - * - * - Pre fixes so CTRN can process it (or it skips processing altogether) - * - Post fixes the result of CTRN (or it skips the value) - */ -export function ruleToReactNative({ declarations = [] }: Rule | Page): Style { - const style: Style = {}; - - for (const declaration of declarations) { - if (isComment(declaration)) { - continue; - } - - const { property: cssAttribute, value: cssValue } = declaration; - - if (cssAttribute === undefined || cssValue === undefined) { - continue; - } - - const name = getPropertyName(cssAttribute); - - const value = preProcessingCss[name] - ? preProcessingCss[name](cssValue) - : cssValue; - - if (value === null) { - warnInvalidStyle(cssAttribute, name, value); - continue; - } - - const nativeStyles: Style = - typeof value === "object" ? value : getStylesForProperty(name, value); - - for (const [nativeAttribute, nativeValue] of Object.entries(nativeStyles)) { - if (isInvalidStyle(nativeAttribute)) { - warnInvalidStyle(cssAttribute, nativeAttribute, nativeValue); - continue; - } - - if (postProcessingCss[nativeAttribute]) { - const postprocessedValue = - postProcessingCss[nativeAttribute](nativeValue); - - if (postprocessedValue === null) { - warnInvalidStyle(cssAttribute, nativeAttribute, nativeValue); - } else { - style[nativeAttribute] = postprocessedValue; - } - } else { - style[nativeAttribute] = nativeValue; - } - } - } - - return style; -} - -function warnInvalidStyle(selector: string, attribute: string, value: unknown) { - if (process.env.NODE_ENV === "production") { - throw new Error( - `Class name '${selector}' maps to invalid style {${attribute}: ${value}}` - ); - } else if (process.env.NODE_ENV !== "test") { - console.warn(`Class name '${selector}' is being ignored as it produces an invalid React Native style { ${attribute}: '${value}' } -Either remove this selector, add a platform modifier (e.g. 'web:${selector}') or change to file to be platform specific. -`); - } -} - -function isComment(rule: Rule | Comment | AtRule): rule is Comment { - return rule.type === "comment"; -} diff --git a/src/babel/native-style-extraction/to-react-native/index.ts b/src/babel/native-style-extraction/to-react-native/index.ts new file mode 100644 index 0000000..4f5e6fc --- /dev/null +++ b/src/babel/native-style-extraction/to-react-native/index.ts @@ -0,0 +1,57 @@ +import { Declaration } from "postcss"; +import { + getPropertyName, + getStylesForProperty, + Style, +} from "css-to-react-native"; + +import { properties } from "./properties"; +import { isInvalidProperty, StyleProperty } from "./is-invalid-property"; + +export interface ToReactNativeErrorRecord { + declaration: Declaration; + error: Error; + result?: Style; +} + +export type ToReactNativeErrorCallback = ( + options: ToReactNativeErrorRecord +) => void; + +export interface ToReactNativeOptions { + onError: ToReactNativeErrorCallback; +} + +export function toReactNative( + declaration: Declaration, + { onError }: ToReactNativeOptions +) { + const { prop, value } = declaration; + + const name = getPropertyName(prop) as StyleProperty; + + let styles: Style | undefined; + + if (isInvalidProperty(name)) { + onError({ + declaration, + error: new Error("invalid property"), + result: styles, + }); + return; + } + + try { + const transform = properties[name]; + styles = transform + ? transform(value, name) + : getStylesForProperty(name, value); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + onError({ declaration, error, result: styles }); + return; + } + + return styles; +} diff --git a/src/babel/native-style-extraction/is-valid-style.ts b/src/babel/native-style-extraction/to-react-native/is-invalid-property.ts similarity index 86% rename from src/babel/native-style-extraction/is-valid-style.ts rename to src/babel/native-style-extraction/to-react-native/is-invalid-property.ts index 2f2b21b..16abaff 100644 --- a/src/babel/native-style-extraction/is-valid-style.ts +++ b/src/babel/native-style-extraction/to-react-native/is-invalid-property.ts @@ -1,8 +1,15 @@ -export function isInvalidStyle(property: string) { - return !validProps.has(property); +import { ImageStyle, TextStyle, ViewStyle } from "react-native"; + +export type StyleProperty = + | keyof TextStyle + | keyof ViewStyle + | keyof ImageStyle; + +export function isInvalidProperty(property: string) { + return !validProps.has(property as StyleProperty); } -const validProps = new Set([ +const validProps = new Set([ "alignContent", "alignItems", "alignSelf", @@ -35,7 +42,6 @@ const validProps = new Set([ "borderWidth", "bottom", "color", - "decomposedMatrix", "direction", "display", "elevation", diff --git a/src/babel/native-style-extraction/to-react-native/properties/aspect-ratio.ts b/src/babel/native-style-extraction/to-react-native/properties/aspect-ratio.ts new file mode 100644 index 0000000..aebfbe0 --- /dev/null +++ b/src/babel/native-style-extraction/to-react-native/properties/aspect-ratio.ts @@ -0,0 +1,15 @@ +import { getStylesForProperty, Style } from "css-to-react-native"; + +export function aspectRatio(value: string): Style { + if (value === "0") { + return {}; + } else if (typeof value === "string" && value.includes("/")) { + const [left, right] = value.split("/").map((n) => { + return Number.parseInt(n, 10); + }); + + return { aspectRatio: left / right }; + } + + return getStylesForProperty("aspectRatio", value); +} diff --git a/src/babel/native-style-extraction/to-react-native/properties/display.ts b/src/babel/native-style-extraction/to-react-native/properties/display.ts new file mode 100644 index 0000000..bfb1d05 --- /dev/null +++ b/src/babel/native-style-extraction/to-react-native/properties/display.ts @@ -0,0 +1,9 @@ +import { Style } from "css-to-react-native"; + +export function display(value: string): Style { + if (value !== "none" && value !== "flex") { + throw new Error("display"); + } + + return { display: value }; +} diff --git a/src/babel/native-style-extraction/to-react-native/properties/flex.ts b/src/babel/native-style-extraction/to-react-native/properties/flex.ts new file mode 100644 index 0000000..d5fe09a --- /dev/null +++ b/src/babel/native-style-extraction/to-react-native/properties/flex.ts @@ -0,0 +1,6 @@ +import { getStylesForProperty, Style } from "css-to-react-native"; + +export function flex(value: string, name: string): Style { + const { flexGrow, flexShrink } = getStylesForProperty(name, value); + return { flexGrow, flexShrink }; +} diff --git a/src/babel/native-style-extraction/patches/index.ts b/src/babel/native-style-extraction/to-react-native/properties/index.ts similarity index 51% rename from src/babel/native-style-extraction/patches/index.ts rename to src/babel/native-style-extraction/to-react-native/properties/index.ts index f7bfa43..9aa13ff 100644 --- a/src/babel/native-style-extraction/patches/index.ts +++ b/src/babel/native-style-extraction/to-react-native/properties/index.ts @@ -1,34 +1,36 @@ +import { getStylesForProperty, Style } from "css-to-react-native"; +import { StyleProperty } from "../is-invalid-property"; import { aspectRatio } from "./aspect-ratio"; import { display } from "./display"; +import { flex } from "./flex"; import { overflow } from "./overflow"; import { position } from "./position"; -function noAuto(value: number | string) { +function noAuto(value: string, name: string): Style { if (value === "auto") { - return null; + throw new Error("no auto"); } - return value; + return getStylesForProperty(name, value); } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const postProcessingCss: Record any> = { +export const properties: Partial< + Record Style> +> = { aspectRatio, display, + flex, overflow, position, - top: noAuto, flexBasis: noAuto, + top: noAuto, bottom: noAuto, left: noAuto, right: noAuto, + margin: noAuto, marginTop: noAuto, marginRight: noAuto, marginBottom: noAuto, marginLeft: noAuto, -}; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const preProcessingCss: Record any> = { zIndex: noAuto, }; diff --git a/src/babel/native-style-extraction/to-react-native/properties/overflow.ts b/src/babel/native-style-extraction/to-react-native/properties/overflow.ts new file mode 100644 index 0000000..c6a595b --- /dev/null +++ b/src/babel/native-style-extraction/to-react-native/properties/overflow.ts @@ -0,0 +1,11 @@ +import { getStylesForProperty, Style } from "css-to-react-native"; + +const supportedValues = new Set(["visible", "hidden", "scroll"]); + +export function overflow(value: string): Style { + if (!supportedValues.has(value)) { + throw new Error("overflow"); + } + + return getStylesForProperty("overflow", value); +} diff --git a/src/babel/native-style-extraction/patches/position.ts b/src/babel/native-style-extraction/to-react-native/properties/position.ts similarity index 51% rename from src/babel/native-style-extraction/patches/position.ts rename to src/babel/native-style-extraction/to-react-native/properties/position.ts index ee32c27..41b8fe0 100644 --- a/src/babel/native-style-extraction/patches/position.ts +++ b/src/babel/native-style-extraction/to-react-native/properties/position.ts @@ -1,17 +1,18 @@ +import { Style } from "css-to-react-native"; + const supportedValues = new Set(["absolute", "relative"]); -export function position(value: string) { +export function position(value: string): Style { if (supportedValues.has(value)) { - return value; + return { position: value }; } // This is a special edge case // The tailwindcss keeps picking up `static` as its a javascript keyword - // We cannot return `null` (and show the warning) as the user isn't - // actualy using the className + // So instead of throwing an error we just ignore it if (value === "static") { - return; + return {}; } - return null; + throw new Error("position"); } diff --git a/src/plugin/native.ts b/src/plugin/native.ts index 6530034..bb78a6d 100644 --- a/src/plugin/native.ts +++ b/src/plugin/native.ts @@ -7,148 +7,12 @@ export interface NativePluginOptions { export const nativePlugin = plugin.withOptions( function () { - return ({ matchUtilities, theme }) => { - matchUtilities( - { - aspect: (value: string) => { - let aspectRatio = value; - - if (value.includes("/")) { - const [left, right] = value.split("/").map((n) => { - return Number.parseInt(n, 10); - }); - - aspectRatio = `${left / right}`; - } - - return { - aspectRatio, - }; - }, - }, - { values: theme("aspectRatio") } - ); + return () => { + /*Nothing here yet */ }; }, function ({ rem = 16 } = {}) { const config: Partial = { - corePlugins: { - accentColor: false, - accessibility: false, - animation: false, - appearance: false, - aspectRatio: false, - backdropBlur: false, - backdropBrightness: false, - backdropContrast: false, - backdropFilter: false, - backdropGrayscale: false, - backdropHueRotate: false, - backdropInvert: false, - backdropOpacity: false, - backdropSaturate: false, - backdropSepia: false, - backgroundAttachment: false, - backgroundBlendMode: false, - backgroundClip: false, - backgroundImage: false, - backgroundOrigin: false, - backgroundPosition: false, - backgroundRepeat: false, - backgroundSize: false, - blur: false, - borderCollapse: false, - boxDecorationBreak: false, - boxShadow: false, - boxSizing: false, - breakAfter: false, - breakBefore: false, - breakInside: false, - brightness: false, - caretColor: false, - clear: false, - columns: false, - content: false, - contrast: false, - cursor: false, - divideColor: false, - divideOpacity: false, - divideStyle: false, - divideWidth: false, - dropShadow: false, - fill: false, - filter: false, - float: false, - fontSmoothing: false, - gap: false, - gradientColorStops: false, - grayscale: false, - gridAutoColumns: false, - gridAutoFlow: false, - gridAutoRows: false, - gridColumn: false, - gridColumnEnd: false, - gridColumnStart: false, - gridRow: false, - gridRowEnd: false, - gridRowStart: false, - gridTemplateColumns: false, - gridTemplateRows: false, - hueRotate: false, - invert: false, - isolation: false, - justifyItems: false, - justifySelf: false, - listStylePosition: false, - listStyleType: false, - mixBlendMode: false, - objectFit: false, - objectPosition: false, - order: false, - overscrollBehavior: false, - placeItems: false, - placeSelf: false, - placeholderColor: false, - placeholderOpacity: false, - preflight: false, - resize: false, - ringColor: false, - ringOffsetColor: false, - ringOffsetWidth: false, - ringOpacity: false, - ringWidth: false, - rotate: false, - saturate: false, - scale: false, - scrollBehavior: false, - scrollMargin: false, - scrollPadding: false, - scrollSnapAlign: false, - scrollSnapStop: false, - scrollSnapType: false, - sepia: false, - skew: false, - space: false, - stroke: false, - strokeWidth: false, - tableLayout: false, - textIndent: false, - textOverflow: false, - touchAction: false, - transform: false, - transformOrigin: false, - transitionDelay: false, - transitionDuration: false, - transitionProperty: false, - transitionTimingFunction: false, - translate: false, - userSelect: false, - verticalAlign: false, - visibility: false, - whitespace: false, - willChange: false, - wordBreak: false, - }, theme: { aspectRatio: { auto: "0", diff --git a/src/types/common.d.ts b/src/types/common.d.ts index df1cf29..7eb3a51 100644 --- a/src/types/common.d.ts +++ b/src/types/common.d.ts @@ -1,10 +1,6 @@ -import type { - ImageStyle, - TextStyle, - ViewStyle, -} from "react-native"; +import type { ImageStyle, TextStyle, ViewStyle } from "react-native"; export type Style = ViewStyle | TextStyle | ImageStyle; -export type StyleRecord = Record; -export type Media = [string, number]; -export type MediaRecord = Record; +export type StyleRecord = Record>; +export type MediaRecord = Record; +export type StyleErrorRecord = Record; diff --git a/src/use-tailwind.ts b/src/use-tailwind.ts index a609645..4da505b 100644 --- a/src/use-tailwind.ts +++ b/src/use-tailwind.ts @@ -49,8 +49,14 @@ export function useTailwind< tailwindStyleIds.push(styles[selector] as P); } - for (const [media, suffix] of mediaRules[selector] ?? []) { - const isMatch = match(media, { + const rules = mediaRules[selector]; + + if (!rules) { + continue; + } + + for (let index = 0, length = rules.length; index < length; index++) { + const isMatch = match(rules[index], { type: platform, width, height, @@ -62,7 +68,7 @@ export function useTailwind< } as any); if (isMatch) { - tailwindStyleIds.push(styles[`${selector}_${suffix}`] as P); + tailwindStyleIds.push(styles[`${selector}_${index}`] as P); } } }