diff --git a/.vscode/settings.json b/.vscode/settings.json index ad92582..3a19d2f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "editor.formatOnSave": true + "editor.formatOnSave": true, + "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/package.json b/package.json index d317606..1539054 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,6 @@ { "name": "@aet/tailwind", - "version": "0.0.1-beta.21", - "main": "dist/index.js", + "version": "0.0.1-beta.30", "license": "MIT", "scripts": { "build": "./scripts/index.ts", @@ -10,6 +9,14 @@ "files": [ "dist" ], + "exports": { + ".": "./dist/index.js", + "./package.json": "./package.json", + "./classed": "./dist/classed.mjs", + "./macro": { + "types": "./dist/macro.d.ts" + } + }, "devDependencies": { "@aet/eslint-rules": "^0.0.34", "@types/babel__core": "^7.20.5", @@ -19,16 +26,15 @@ "@types/node": "^20.14.9", "@types/postcss-safe-parser": "^5.0.4", "@types/react": "^18.3.3", + "@types/stylis": "^4.2.6", "cli-highlight": "^2.1.11", "clsx": "^2.1.1", "colord": "^2.9.3", "css-what": "^6.1.0", "dedent": "^1.5.3", - "esbuild": "^0.21.5", "esbuild-register": "^3.5.0", "eslint": "^8.57.0", "postcss-nested": "^6.0.1", - "postcss-safe-parser": "^7.0.0", "prettier": "^3.3.2", "tailwindcss": "^3.4.4", "tsup": "^8.1.0", @@ -42,9 +48,12 @@ "dependencies": { "@babel/core": "^7.24.7", "@emotion/hash": "^0.9.1", + "esbuild": "^0.23.0", + "json5": "^2.2.3", "lodash": "^4.17.21", "postcss": "^8.4.39", "postcss-selector-parser": "^6.1.0", + "stylis": "^4.3.2", "tiny-invariant": "^1.3.3", "type-fest": "^4.21.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4b8f366..4177272 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,12 +14,24 @@ importers: '@emotion/hash': specifier: ^0.9.1 version: 0.9.1 + esbuild: + specifier: ^0.23.0 + version: 0.23.0 + json5: + specifier: ^2.2.3 + version: 2.2.3 lodash: specifier: ^4.17.21 version: 4.17.21 postcss: specifier: ^8.4.39 version: 8.4.39 + postcss-selector-parser: + specifier: ^6.1.0 + version: 6.1.0 + stylis: + specifier: ^4.3.2 + version: 4.3.2 tiny-invariant: specifier: ^1.3.3 version: 1.3.3 @@ -51,6 +63,9 @@ importers: '@types/react': specifier: ^18.3.3 version: 18.3.3 + '@types/stylis': + specifier: ^4.2.6 + version: 4.2.6 cli-highlight: specifier: ^2.1.11 version: 2.1.11 @@ -66,24 +81,15 @@ importers: dedent: specifier: ^1.5.3 version: 1.5.3 - esbuild: - specifier: ^0.21.5 - version: 0.21.5 esbuild-register: specifier: ^3.5.0 - version: 3.5.0(esbuild@0.21.5) + version: 3.5.0(esbuild@0.23.0) eslint: specifier: ^8.57.0 version: 8.57.0 postcss-nested: specifier: ^6.0.1 version: 6.0.1(postcss@8.4.39) - postcss-safe-parser: - specifier: ^7.0.0 - version: 7.0.0(postcss@8.4.39) - postcss-selector-parser: - specifier: ^6.1.0 - version: 6.1.0 prettier: specifier: ^3.3.2 version: 3.3.2 @@ -249,138 +255,282 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.23.0': + resolution: {integrity: sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.21.5': resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.23.0': + resolution: {integrity: sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.21.5': resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} cpu: [arm] os: [android] + '@esbuild/android-arm@0.23.0': + resolution: {integrity: sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.21.5': resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} cpu: [x64] os: [android] + '@esbuild/android-x64@0.23.0': + resolution: {integrity: sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.21.5': resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.23.0': + resolution: {integrity: sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.21.5': resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.23.0': + resolution: {integrity: sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.21.5': resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.23.0': + resolution: {integrity: sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.21.5': resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.23.0': + resolution: {integrity: sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.21.5': resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.23.0': + resolution: {integrity: sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.21.5': resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.23.0': + resolution: {integrity: sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.21.5': resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.23.0': + resolution: {integrity: sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.21.5': resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.23.0': + resolution: {integrity: sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.21.5': resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.23.0': + resolution: {integrity: sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.21.5': resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.23.0': + resolution: {integrity: sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.21.5': resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.23.0': + resolution: {integrity: sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.21.5': resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.23.0': + resolution: {integrity: sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.21.5': resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.23.0': + resolution: {integrity: sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-x64@0.21.5': resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.23.0': + resolution: {integrity: sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.23.0': + resolution: {integrity: sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.21.5': resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.23.0': + resolution: {integrity: sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/sunos-x64@0.21.5': resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.23.0': + resolution: {integrity: sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.21.5': resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.23.0': + resolution: {integrity: sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.21.5': resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.23.0': + resolution: {integrity: sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.21.5': resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.23.0': + resolution: {integrity: sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.4.0': resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -586,6 +736,9 @@ packages: '@types/react@18.3.3': resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} + '@types/stylis@4.2.6': + resolution: {integrity: sha512-4nebF2ZJGzQk0ka0O6+FZUWceyFv4vWq/0dXBMmrSeAwzOuOd/GxE5Pa64d/ndeNLG73dXoBsRzvtsVsYUv6Uw==} + '@types/ws@8.5.10': resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} @@ -988,6 +1141,11 @@ packages: engines: {node: '>=12'} hasBin: true + esbuild@0.23.0: + resolution: {integrity: sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==} + engines: {node: '>=18'} + hasBin: true + escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} @@ -1685,12 +1843,6 @@ packages: peerDependencies: postcss: ^8.2.14 - postcss-safe-parser@7.0.0: - resolution: {integrity: sha512-ovehqRNVCpuFzbXoTb4qLtyzK3xn3t/CUBxOs8LsnQjQrShaB4lKiHoVqY8ANaC0hBMHq5QVWk77rwGklFUDrg==} - engines: {node: '>=18.0'} - peerDependencies: - postcss: ^8.4.31 - postcss-selector-parser@6.1.0: resolution: {integrity: sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==} engines: {node: '>=4'} @@ -1893,6 +2045,9 @@ packages: strip-literal@2.1.0: resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} + stylis@4.3.2: + resolution: {integrity: sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==} + sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} @@ -2383,72 +2538,144 @@ snapshots: '@esbuild/aix-ppc64@0.21.5': optional: true + '@esbuild/aix-ppc64@0.23.0': + optional: true + '@esbuild/android-arm64@0.21.5': optional: true + '@esbuild/android-arm64@0.23.0': + optional: true + '@esbuild/android-arm@0.21.5': optional: true + '@esbuild/android-arm@0.23.0': + optional: true + '@esbuild/android-x64@0.21.5': optional: true + '@esbuild/android-x64@0.23.0': + optional: true + '@esbuild/darwin-arm64@0.21.5': optional: true + '@esbuild/darwin-arm64@0.23.0': + optional: true + '@esbuild/darwin-x64@0.21.5': optional: true + '@esbuild/darwin-x64@0.23.0': + optional: true + '@esbuild/freebsd-arm64@0.21.5': optional: true + '@esbuild/freebsd-arm64@0.23.0': + optional: true + '@esbuild/freebsd-x64@0.21.5': optional: true + '@esbuild/freebsd-x64@0.23.0': + optional: true + '@esbuild/linux-arm64@0.21.5': optional: true + '@esbuild/linux-arm64@0.23.0': + optional: true + '@esbuild/linux-arm@0.21.5': optional: true + '@esbuild/linux-arm@0.23.0': + optional: true + '@esbuild/linux-ia32@0.21.5': optional: true + '@esbuild/linux-ia32@0.23.0': + optional: true + '@esbuild/linux-loong64@0.21.5': optional: true + '@esbuild/linux-loong64@0.23.0': + optional: true + '@esbuild/linux-mips64el@0.21.5': optional: true + '@esbuild/linux-mips64el@0.23.0': + optional: true + '@esbuild/linux-ppc64@0.21.5': optional: true + '@esbuild/linux-ppc64@0.23.0': + optional: true + '@esbuild/linux-riscv64@0.21.5': optional: true + '@esbuild/linux-riscv64@0.23.0': + optional: true + '@esbuild/linux-s390x@0.21.5': optional: true + '@esbuild/linux-s390x@0.23.0': + optional: true + '@esbuild/linux-x64@0.21.5': optional: true + '@esbuild/linux-x64@0.23.0': + optional: true + '@esbuild/netbsd-x64@0.21.5': optional: true + '@esbuild/netbsd-x64@0.23.0': + optional: true + + '@esbuild/openbsd-arm64@0.23.0': + optional: true + '@esbuild/openbsd-x64@0.21.5': optional: true + '@esbuild/openbsd-x64@0.23.0': + optional: true + '@esbuild/sunos-x64@0.21.5': optional: true + '@esbuild/sunos-x64@0.23.0': + optional: true + '@esbuild/win32-arm64@0.21.5': optional: true + '@esbuild/win32-arm64@0.23.0': + optional: true + '@esbuild/win32-ia32@0.21.5': optional: true + '@esbuild/win32-ia32@0.23.0': + optional: true + '@esbuild/win32-x64@0.21.5': optional: true + '@esbuild/win32-x64@0.23.0': + optional: true + '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': dependencies: eslint: 8.57.0 @@ -2651,6 +2878,8 @@ snapshots: '@types/prop-types': 15.7.12 csstype: 3.1.3 + '@types/stylis@4.2.6': {} + '@types/ws@8.5.10': dependencies: '@types/node': 20.14.9 @@ -3034,10 +3263,10 @@ snapshots: es-module-lexer@1.5.4: {} - esbuild-register@3.5.0(esbuild@0.21.5): + esbuild-register@3.5.0(esbuild@0.23.0): dependencies: debug: 4.3.4 - esbuild: 0.21.5 + esbuild: 0.23.0 transitivePeerDependencies: - supports-color @@ -3067,6 +3296,33 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 + esbuild@0.23.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.0 + '@esbuild/android-arm': 0.23.0 + '@esbuild/android-arm64': 0.23.0 + '@esbuild/android-x64': 0.23.0 + '@esbuild/darwin-arm64': 0.23.0 + '@esbuild/darwin-x64': 0.23.0 + '@esbuild/freebsd-arm64': 0.23.0 + '@esbuild/freebsd-x64': 0.23.0 + '@esbuild/linux-arm': 0.23.0 + '@esbuild/linux-arm64': 0.23.0 + '@esbuild/linux-ia32': 0.23.0 + '@esbuild/linux-loong64': 0.23.0 + '@esbuild/linux-mips64el': 0.23.0 + '@esbuild/linux-ppc64': 0.23.0 + '@esbuild/linux-riscv64': 0.23.0 + '@esbuild/linux-s390x': 0.23.0 + '@esbuild/linux-x64': 0.23.0 + '@esbuild/netbsd-x64': 0.23.0 + '@esbuild/openbsd-arm64': 0.23.0 + '@esbuild/openbsd-x64': 0.23.0 + '@esbuild/sunos-x64': 0.23.0 + '@esbuild/win32-arm64': 0.23.0 + '@esbuild/win32-ia32': 0.23.0 + '@esbuild/win32-x64': 0.23.0 + escalade@3.1.2: {} escape-string-regexp@1.0.5: {} @@ -3750,10 +4006,6 @@ snapshots: postcss: 8.4.39 postcss-selector-parser: 6.1.0 - postcss-safe-parser@7.0.0(postcss@8.4.39): - dependencies: - postcss: 8.4.39 - postcss-selector-parser@6.1.0: dependencies: cssesc: 3.0.0 @@ -3948,6 +4200,8 @@ snapshots: dependencies: js-tokens: 9.0.0 + stylis@4.3.2: {} + sucrase@3.35.0: dependencies: '@jridgewell/gen-mapping': 0.3.5 diff --git a/scripts/index.ts b/scripts/index.ts index ea768fb..1c5ded4 100755 --- a/scripts/index.ts +++ b/scripts/index.ts @@ -28,7 +28,7 @@ await Promise.all([ build({ ...tsupConfig, entry: ["src/index.ts"], - external: ["postcss-selector-parser", "postcss"], + external: ["postcss-selector-parser", "postcss", "stylis"], }), Bun.write( "dist/package.json", @@ -44,6 +44,7 @@ await Promise.all([ await Promise.all([ fs.copyFile("README.md", "dist/README.md"), fs.copyFile("LICENSE.md", "dist/LICENSE.md"), + fs.copyFile("src/macro.d.ts", "dist/macro.d.ts"), ]); process.exit(0); diff --git a/src/__tests__/styleObject.test.ts b/src/__tests__/styleObject.test.ts index 33671e6..a0472bd 100644 --- a/src/__tests__/styleObject.test.ts +++ b/src/__tests__/styleObject.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest"; import { getBuild } from "./utils"; import { getClassName } from "../index"; -describe.only("babel-tailwind", () => { +describe("babel-tailwind", () => { const compileESBuild = getBuild("styleObject"); it("supports conversion into CSSProperties", async () => { @@ -10,9 +10,11 @@ describe.only("babel-tailwind", () => { clsx: "emotion", expectFiles: 1, javascript: ` + import { tws } from "@aet/tailwind/macro"; + export function Hello() { return ( -
+
Hello, world!
); @@ -20,9 +22,23 @@ describe.only("babel-tailwind", () => { `, }); - const clsName = getClassName("p-2 text-center").replace(/^tw-/, "tw_"); + const clsName = getClassName("p-2 text-center hover:font-semibold sm:p-1").replace( + /^tw-/, + "tw_" + ); expect(files.js.text).toContain( - `var ${clsName} = {\n "padding": "0.5rem",\n "textAlign": "center"\n}` + [ + `var ${clsName} = {`, + ' padding: "0.5rem",', + ' textAlign: "center",', + ' "&:hover": {', + ' fontWeight: "600"', + " },", + ' "@media (min-width: 640px)": {', + ' padding: "0.25rem"', + " }", + "}", + ].join("\n") ); expect(files.js.text).toContain(`style: ${clsName}`); }); diff --git a/src/__tests__/tw.test.ts b/src/__tests__/tw.test.ts index 5a1c705..053cea5 100644 --- a/src/__tests__/tw.test.ts +++ b/src/__tests__/tw.test.ts @@ -4,11 +4,14 @@ import { getBuild } from "./utils"; describe("babel-tailwind", () => { const compileESBuild = getBuild("tw"); + it("supports grouped tw", async () => { const { files } = await compileESBuild({ clsx: "emotion", expectFiles: 2, javascript: ` + import { tw } from "@aet/tailwind/macro"; + export default tw("text-sm", \`flex\`, { "group-hover": "text-center", "[&>div]": \`font-semibold\`, diff --git a/src/babel-tailwind.ts b/src/babel-tailwind.ts index 24ff431..254d076 100644 --- a/src/babel-tailwind.ts +++ b/src/babel-tailwind.ts @@ -4,10 +4,11 @@ import hash from "@emotion/hash"; import { isPlainObject } from "lodash"; import invariant from "tiny-invariant"; import { type NodePath, type types as t } from "@babel/core"; -import type { SourceLocation, StyleMapEntry } from "./shared"; +import { type SourceLocation, type StyleMapEntry, macroName } from "./shared"; import { type ResolveTailwindOptions, getClassName } from "./index"; export type ClassNameCollector = (path: string, entries: StyleMapEntry[]) => void; +type BabelTypes = typeof babel.types; type Type = "css" | "js"; export function babelTailwind( @@ -15,8 +16,6 @@ export function babelTailwind( styleMap, clsx, getClassName: getClass = getClassName, - macroFunction, - macroStyleFunction, jsxAttributeAction = "delete", jsxAttributeName = "css", vite, @@ -25,11 +24,7 @@ export function babelTailwind( ) { type BabelPluginState = ReturnType; - function getState( - path: NodePath, - state: babel.PluginPass, - t: typeof babel.types - ) { + function getState(path: NodePath, state: babel.PluginPass, t: BabelTypes) { let cx: t.Identifier; let styleImport: t.Identifier; @@ -118,69 +113,62 @@ export function babelTailwind( }; } - const getType = (fnName: string): Type | undefined => - fnName === macroFunction ? "css" : fnName === macroStyleFunction ? "js" : undefined; - return definePlugin(({ types: t }) => ({ Program: { enter(path, state) { - Object.assign(state, getState(path, state, t)); + const _ = getState(path, state, t); + Object.assign(state, _); + + for (const { + local: { parentPath: local }, + imported, + } of getMacros(t, path, macroName)) { + const type = imported === "tw" ? "css" : imported === "tws" ? "js" : undefined; + if (!type) continue; + + if (isNodePath(local, t.isTaggedTemplateExpression)) { + const { node } = local; + const { tag, quasi } = node; + if (!t.isIdentifier(tag)) continue; + + invariant( + !quasi.expressions.length, + `Macro call should not contain expressions` + ); + + const value = quasi.quasis[0].value.cooked; + if (value) { + const trimmed = trim(value); + const className = _.getClass(type, trimmed); + _.recordIfAbsent(type, { + key: className, + className: trimmed, + location: _.sliceText(node), + }); + _.replaceWithImport(type, local, className); + } + } else if (isNodePath(local, t.isCallExpression)) { + const { node } = local; + const { callee } = node; + if (!t.isIdentifier(callee)) continue; + + const trimmed = local.get("arguments").flatMap(evaluateArgs).join(" "); + const className = getClass(trimmed); + _.recordIfAbsent(type, { + key: className, + className: trimmed, + location: _.sliceText(node), + }); + _.replaceWithImport(type, local, className); + } + } }, + exit({ node }, _) { _.finish(node); }, }, - TaggedTemplateExpression(path, _) { - if (macroFunction == null && macroStyleFunction == null) return; - const { node } = path; - - const { - tag, - quasi: { quasis, expressions }, - } = node; - if (!t.isIdentifier(tag)) return; - - invariant( - !expressions.length, - `${macroFunction}\`\` should not contain expressions` - ); - - const type = getType(tag.name); - if (!type) return; - - const value = quasis[0].value.cooked; - if (value) { - const trimmed = trim(value); - const className = _.getClass(type, trimmed); - _.recordIfAbsent(type, { - key: className, - className: trimmed, - location: _.sliceText(node), - }); - _.replaceWithImport(type, path, className); - } - }, - - CallExpression(path, _) { - if (macroFunction == null) return; - const { node } = path; - const { callee } = node; - if (!t.isIdentifier(callee)) return; - - const type = getType(callee.name); - if (!type) return; - - const trimmed = path.get("arguments").flatMap(evaluateArgs).join(" "); - const className = getClass(trimmed); - _.recordIfAbsent(type, { - key: className, - className: trimmed, - location: _.sliceText(node), - }); - _.replaceWithImport(type, path, className); - }, - JSXAttribute(path, _) { const { name } = path.node; if (name.name !== jsxAttributeName) return; @@ -296,7 +284,7 @@ export function babelTailwind( })); } -function getClsxImport(t: typeof babel.types, cx: t.Identifier, clsx: string) { +function getClsxImport(t: BabelTypes, cx: t.Identifier, clsx: string) { switch (clsx) { case "emotion": return t.importDeclaration( @@ -346,6 +334,56 @@ function evaluateArgs(path: NodePath) { throw new Error("Invalid argument type"); } +function getName(t: BabelTypes, exp: t.Node) { + if (t.isIdentifier(exp)) { + return exp.name; + } else if (t.isStringLiteral(exp)) { + return exp.value; + } +} + +function getMacros( + t: BabelTypes, + programPath: NodePath, + importSource: string +) { + const importDecs = programPath + .get("body") + .filter(x => isNodePath(x, t.isImportDeclaration)) + .filter(x => x.node.source.value === importSource); + + const macros = importDecs + .flatMap(x => x.get("specifiers")) + .map(x => { + const local = x.get("local"); + if (isNodePath(x, t.isImportNamespaceSpecifier)) { + return local.scope + .getOwnBinding(local.node.name)! + .referencePaths.map(p => p.parentPath) + .filter(p => isNodePath(p, t.isMemberExpression)) + .map(p => ({ + local: p, + imported: getName(t, p.node.property)!, + })) + .filter(p => p.imported); + } else if (t.isImportSpecifier(x.node)) { + const imported = x.node.imported; + return local.scope.getOwnBinding(local.node.name)!.referencePaths.map(p => ({ + local: p as NodePath, + imported: getName(t, imported)!, + })); + } + }) + .filter(Boolean) + .flat(1); + + for (const x of importDecs) { + x.remove(); + } + + return macros; +} + const definePlugin = (fn: (runtime: typeof babel) => babel.Visitor) => (runtime: typeof babel) => { @@ -367,6 +405,11 @@ function matchPath( fn?.(nodePath); } +const isNodePath = ( + nodePath: NodePath | null, + predicate: (node: t.Node) => node is T +): nodePath is NodePath => Boolean(nodePath?.node && predicate(nodePath.node)); + function getSuffix(add: boolean | undefined, entries: StyleMapEntry[]) { if (!add) return ""; diff --git a/src/css-to-js.ts b/src/css-to-js.ts index bccc214..dfe38d1 100755 --- a/src/css-to-js.ts +++ b/src/css-to-js.ts @@ -3,14 +3,15 @@ import { type AtRule, type Builder, - type ChildNode, type Node, type Root, type Rule, parse, } from "postcss"; +import JSON5 from "json5"; import parseSelector from "postcss-selector-parser"; import Stringifier from "postcss/lib/stringifier"; +import { type Element, compile } from "stylis"; import { camelCase } from "lodash"; function getSelectorScope(selector: string): string { @@ -99,10 +100,6 @@ function getCssIndexedByScope(css: string) { } for (const scope of scopesStack.at(-1)!) { - if (!cssIndexedByScope.has(scope)) { - cssIndexedByScope.set(scope, ""); - } - if ( flag === "start" && isNode(node, "rule") && @@ -113,7 +110,7 @@ function getCssIndexedByScope(css: string) { .join(", ")} {`; } - cssIndexedByScope.set(scope, cssIndexedByScope.get(scope) + output); + cssIndexedByScope.set(scope, (cssIndexedByScope.get(scope) || "") + output); } if (flag === "end") { @@ -124,23 +121,6 @@ function getCssIndexedByScope(css: string) { return cssIndexedByScope; } -function convertSelectorForEmotion( - selector: string, - scope: string, - knownScopes: Set, - mapClassNames: (className: string) => string -): string { - return parseSelector(nodes => { - nodes.first.walkClasses(node => { - if (node.toString() === scope) { - node.toString = () => "&"; - } else if (knownScopes.has(node.toString())) { - node.toString = () => `.\${${mapClassNames(node.value)}}`; - } - }); - }).processSync(selector); -} - const convertScopeToModuleName = (scope: string) => camelCase(scope) .replace(/^(\d)/, "_$1") @@ -149,141 +129,214 @@ const convertScopeToModuleName = (scope: string) => "_$1" ); -function convertScopedCssForEmotion( - scopedCss: string, - scope: string, - knownScopes: Set, - mapClassNames: (className: string) => string -): string { - let scopedCssForEmotion = ""; - - stringify(scopedCss, (output, node, flag) => { - if ((flag === "start" || flag === "end") && isNode(node, "rule")) { - if (node.selector === scope) { - if (isNode(node.parent, "root")) { - return; - } else if (flag === "start") { - output = "& {"; - } - } else if (flag === "start") { - const selectors = new Set( - node.selectors.map(selector => - convertSelectorForEmotion(selector, scope, knownScopes, mapClassNames) - ) - ); - - // TODO remove join usage once https://github.com/prettier/prettier/issues/2883 is resolved - output = `${[...selectors].join(", ")} {`; - } - } - - scopedCssForEmotion += output; - }); - - return scopedCssForEmotion.replace(/\\/g, "\\\\").replace(/`/g, "\\`"); -} - export function convertCssToJS( css: string, mapClassNames: (className: string) => string = convertScopeToModuleName ): string { - let cssForEmotion = ""; + let res = ""; - const cssIndexedByScope = getCssIndexedByScope(css); - - if (cssIndexedByScope.has("root") && cssIndexedByScope.get("root")!.trim()) { - cssForEmotion += 'import { injectGlobal } from "@emotion/css";\n'; + const map = getCssIndexedByScope(css); + if (map.has("root") && map.get("root")!.trim()) { + res += 'import { injectGlobal } from "@emotion/css";\n'; } - const knownScopes = new Set(cssIndexedByScope.keys()); + const knownScopes = new Set(map.keys()); const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base", }); + function convertScopedCss(scope: string): string { + let scopedCssText = ""; + const scopedCss = map.get(scope)!; + + stringify(scopedCss, (output, node, flag) => { + if ((flag === "start" || flag === "end") && isNode(node, "rule")) { + if (node.selector === scope) { + if (isNode(node.parent, "root")) { + return; + } else if (flag === "start") { + output = "& {"; + } + } else if (flag === "start") { + const selectors = new Set( + node.selectors.map(selector => + parseSelector(nodes => { + nodes.first.walkClasses(node => { + if (node.toString() === scope) { + node.toString = () => "&"; + } else if (knownScopes.has(node.toString())) { + node.toString = () => `.\${${mapClassNames(node.value)}}`; + } + }); + }).processSync(selector) + ) + ); + + // TODO remove join usage once https://github.com/prettier/prettier/issues/2883 is resolved + output = `${[...selectors].join(", ")} {`; + } + } + + scopedCssText += output; + if (node?.type === "decl" && output.at(-1) !== ";") { + scopedCssText += ";"; + } + }); + + return scopedCssText.replace(/\\/g, "\\\\").replace(/`/g, "\\`"); + } + const sortedKnownScopes = [...knownScopes] .sort((scopeA, scopeB) => (scopeA === "root" ? -1 : collator.compare(scopeA, scopeB))) - .reduce((previousSortedKnownScopes, knownScope) => { + .reduce((accum, knownScope) => { for (const requiredScope of getRequiredScopes( - cssIndexedByScope.get(knownScope)!, + map.get(knownScope)!, knownScope, knownScopes )) { - previousSortedKnownScopes.add(requiredScope); + accum.add(requiredScope); } - previousSortedKnownScopes.add(knownScope); - return previousSortedKnownScopes; + accum.add(knownScope); + return accum; }, new Set()); for (const scope of sortedKnownScopes) { - cssForEmotion += "\n"; + const style = convertScopedCss(scope).trimEnd(); + if (!style.trim()) continue; - const convertedScopedCssForEmotion = convertScopedCssForEmotion( - cssIndexedByScope.get(scope)!, - scope, - knownScopes, - mapClassNames - ).trimEnd(); - - if (!convertedScopedCssForEmotion.trim()) continue; - - cssForEmotion += + res += scope === "root" - ? `injectGlobal\`${convertedScopedCssForEmotion}\n\`;\n` + ? `injectGlobal\`${style}\n\`;\n` : `\nexport const ${mapClassNames( scope - )} = ${JSON.stringify(asJSObject(convertedScopedCssForEmotion), null, 2)};\n`; + )} = ${JSON5.stringify(asJSObject(style), null, 2)};\n`; } - return cssForEmotion.trim(); + return res.trim(); } +// https://github.com/facebook/react/blob/3db98c917701d59f62cf1fbe45cbf01b0b61c704/packages/react-dom-bindings/src/shared/isUnitlessNumber.js#L13 +const unitlessNumbers = new Set([ + "animationIterationCount", + "aspectRatio", + "borderImageOutset", + "borderImageSlice", + "borderImageWidth", + "boxFlex", + "boxFlexGroup", + "boxOrdinalGroup", + "columnCount", + "columns", + "flex", + "flexGrow", + "flexPositive", + "flexShrink", + "flexNegative", + "flexOrder", + "gridArea", + "gridRow", + "gridRowEnd", + "gridRowSpan", + "gridRowStart", + "gridColumn", + "gridColumnEnd", + "gridColumnSpan", + "gridColumnStart", + "fontWeight", + "lineClamp", + "lineHeight", + "opacity", + "order", + "orphans", + "scale", + "tabSize", + "widows", + "zIndex", + "zoom", + "fillOpacity", // SVG-related properties + "floodOpacity", + "stopOpacity", + "strokeDasharray", + "strokeDashoffset", + "strokeMiterlimit", + "strokeOpacity", + "strokeWidth", + "MozAnimationIterationCount", // Known Prefixed Properties + "MozBoxFlex", // TODO: Remove these since they shouldn't be used in modern code + "MozBoxFlexGroup", + "MozLineClamp", + "msAnimationIterationCount", + "msFlex", + "msZoom", + "msFlexGrow", + "msFlexNegative", + "msFlexOrder", + "msFlexPositive", + "msFlexShrink", + "msGridColumn", + "msGridColumnSpan", + "msGridRow", + "msGridRowSpan", + "WebkitAnimationIterationCount", + "WebkitBoxFlex", + "WebKitBoxFlexGroup", + "WebkitBoxOrdinalGroup", + "WebkitColumnCount", + "WebkitColumns", + "WebkitFlex", + "WebkitFlexGrow", + "WebkitFlexPositive", + "WebkitFlexShrink", + "WebkitLineClamp", +]); -const candidates = new Set(["fontSize"]); function simplifyValue(propName: string, value: string) { const num = value.match(/^(\d+(\.\d+)?)px$/)?.[1]; - if (num != null && candidates.has(propName)) { + if (num != null && unitlessNumbers.has(propName)) { return parseFloat(num); } return value; } function asJSObject(inputCssText: string) { - const css = parse(`a{${inputCssText}}`); + const css = compile(inputCssText); const result: Record = {}; - if (css.nodes.length !== 1) { - throw new Error("Expected exactly one root node"); - } - const node = css.first!; - if (node.type !== "rule") return; - - function walk(collect: Record, node: ChildNode) { + function walk(collect: Record, node: Element) { switch (node.type) { - case "atrule": - const obj = (collect[`@${node.name} ${node.params}`] ??= {}); - node.each(child => { + case "decl": { + const prop = node.props as string; + const propName = prop.startsWith("--") + ? prop + : prop.replace(/(-.)/g, v => v[1].toUpperCase()); + collect[propName] = simplifyValue(propName, node.children as string); + break; + } + + case "rule": { + const ruleName = node.value.replaceAll("\f", ""); + const obj = ruleName === "&" ? collect : (collect[ruleName] ??= {}); + for (const child of node.children as Element[]) { walk(obj, child); - }); + } + break; + } + + case "comm": break; - case "decl": - const propName = node.prop.replace(/(-.)/g, v => v[1].toUpperCase()); - collect[propName] = simplifyValue(propName, node.value); - break; - - case "rule": - node.each(declaration => { - walk(collect, declaration); - }); - break; - - case "comment": + case "@media": + const media = (collect[node.value] ??= {}); + for (const child of node.children as Element[]) { + walk(media, child); + } break; } } - walk(result, node); - + for (const node of css) { + walk(result, node); + } return result as React.CSSProperties; } diff --git a/src/esbuild-postcss.ts b/src/esbuild-postcss.ts index a57e1fd..c511a10 100644 --- a/src/esbuild-postcss.ts +++ b/src/esbuild-postcss.ts @@ -59,6 +59,7 @@ export const esbuildPlugin = ({ const entry = styles.find(s => s.key === cls)!; if (!entry) { + console.error(e); throw new Error("Could not find entry for CSS"); } diff --git a/src/index.ts b/src/index.ts index dd5ffd0..09987e1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,30 +17,6 @@ export type BuildStyleFile = ( path: string ) => Promise; -interface RecursiveStringObject { - [modifier: string]: string | RecursiveStringObject; -} - -export type CSSAttributeValue = string | (string | RecursiveStringObject)[]; - -/** - * Tagged template macro function combining Tailwind classes - * @example "tw" => tw`p-2 text-center` - */ -export interface TailwindFunction { - (strings: TemplateStringsArray): string; - (...args: (string | RecursiveStringObject)[]): string; -} - -/** - * Tagged template macro function compiling Tailwind styles - * @example "tws" => tws`p-2 text-center` // { padding: 2, textAlign: "center" } - */ -export interface TailwindStyleFunction { - (strings: TemplateStringsArray): Output; - (...args: (string | RecursiveStringObject)[]): Output; -} - export interface TailwindPluginOptions { /** * Tailwind CSS configuration @@ -74,22 +50,6 @@ export interface TailwindPluginOptions { */ jsxAttributeAction?: "delete" | "preserve" | ["rename", string]; - /** - * Template macro function to combine Tailwind classes - * @example - * declare const tw: TailwindFunction; - * "tw" => tw`p-2 text-center` - */ - macroFunction?: string | undefined; - - /** - * Template macro function to compile Tailwind classes - * @example - * declare const tws: TailwindStyleFunction; - * "tws" => tws`p-2 text-center` // { padding: 2, textAlign: "center" } - */ - macroStyleFunction?: string | undefined; - /** * The prefix to use for the generated class names. * @default className => `tw-${hash(className)}` @@ -127,8 +87,6 @@ export type ResolveTailwindOptions = SetRequired< | "postCSSPlugins" | "styleMap" | "tailwindConfig" - | "macroFunction" - | "macroStyleFunction" >; /** @@ -160,8 +118,6 @@ export function getTailwindPlugins(options: TailwindPluginOptions) { jsxAttributeName: "css", postCSSPlugins: [], styleMap: new Map(), - macroFunction: undefined, - macroStyleFunction: undefined, tailwindConfig: {}, ...options, }; diff --git a/src/macro.d.ts b/src/macro.d.ts new file mode 100644 index 0000000..95a657e --- /dev/null +++ b/src/macro.d.ts @@ -0,0 +1,43 @@ +interface RecursiveStringObject { + [modifier: string]: string | RecursiveStringObject; +} + +type CSSAttributeValue = string | (string | RecursiveStringObject)[]; + +/** + * Tagged template macro function combining Tailwind classes + * @example "tw" => tw`p-2 text-center` + */ +export interface TailwindFunction { + (strings: TemplateStringsArray): string; + (...args: (string | RecursiveStringObject)[]): string; +} + +/** + * Tagged template macro function compiling Tailwind styles + * @example "tws" => tws`p-2 text-center` // { padding: 2, textAlign: "center" } + */ +export interface TailwindStyleFunction { + (strings: TemplateStringsArray): TailwindStyleFunctionReturn; + (...args: (string | RecursiveStringObject)[]): TailwindStyleFunctionReturn; +} + +export const tw: TailwindFunction; +export const tws: TailwindStyleFunction; + +type TailwindStyleFunctionReturn = Config extends { tws: infer T } ? T : never; + +/** + * Type configuration for the Tailwind style macro + * @example + * + * ```ts + * declare module "_tailwind_lib/macro" { + * interface Config { + * tws: React.CSSProperties; + * } + * } + * ``` + */ +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface Config {} diff --git a/src/shared.ts b/src/shared.ts index 1b42742..6385ea3 100644 --- a/src/shared.ts +++ b/src/shared.ts @@ -3,6 +3,7 @@ import postcss from "postcss"; import type { ResolveTailwindOptions } from "./index"; export const { name: pkgName } = [require][0]("../package.json"); +export const macroName = `${pkgName}/macro`; interface LineColumn { line: number; diff --git a/src/vite-plugin.ts b/src/vite-plugin.ts index 10e7177..86642fd 100644 --- a/src/vite-plugin.ts +++ b/src/vite-plugin.ts @@ -1,6 +1,6 @@ import { dirname, join } from "node:path"; import type * as vite from "vite"; -import { type Compile, type StyleMap, pkgName } from "./shared"; +import { type Compile, type StyleMap, macroName, pkgName } from "./shared"; import type { BuildStyleFile } from "./index"; const ROLLUP_PREFIX = "\0tailwind:"; @@ -15,6 +15,11 @@ export const vitePlugin = ({ buildStyleFile: BuildStyleFile; }): vite.Plugin => ({ name: "tailwind", + + configResolved(config) { + (config.optimizeDeps.exclude ?? []).push(macroName, `${pkgName}/base`); + }, + resolveId(id, importer) { if (id === `${pkgName}/base`) { return { diff --git a/tsconfig.json b/tsconfig.json index 6845890..70a51d3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ "exactOptionalPropertyTypes": true, "jsx": "react-jsx", "module": "esnext", - "moduleResolution": "node", + "moduleResolution": "bundler", "noImplicitOverride": true, "noUnusedLocals": true, "noUnusedParameters": true,