diff --git a/editor/jest.config.js b/editor/jest.config.js index bc651b6d6b29..0a99d49a3e88 100644 --- a/editor/jest.config.js +++ b/editor/jest.config.js @@ -112,6 +112,8 @@ module.exports = { 'react-dnd-html5-backend': '/test/jest/__mocks__/react-dnd-html5-backend.js', '^react$': '/node_modules/react/index.js', '^react-dom$': '/node_modules/react-dom/index.js', + '^@mhsdesign/jit-browser-tailwindcss$': + '/src/utils/__mocks__/jit-browser-tailwindcss.js', '\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '/test/jest/__mocks__/styleMock.js', }, diff --git a/editor/package.json b/editor/package.json index 91b7e6b6f694..9da259561b23 100644 --- a/editor/package.json +++ b/editor/package.json @@ -152,6 +152,7 @@ "@liveblocks/react": "1.10.0", "@liveblocks/react-comments": "1.10.0", "@liveblocks/yjs": "1.10.0", + "@mhsdesign/jit-browser-tailwindcss": "0.4.1", "@popperjs/core": "2.4.4", "@radix-ui/react-dropdown-menu": "2.1.1", "@radix-ui/react-select": "2.1.1", @@ -164,9 +165,6 @@ "@svgr/plugin-jsx": "5.5.0", "@testing-library/user-event": "14.5.2", "@tippyjs/react": "4.1.0", - "@twind/core": "1.1.3", - "@twind/preset-autoprefix": "1.0.7", - "@twind/preset-tailwind": "1.1.4", "@types/fontfaceobserver": "0.0.6", "@types/lodash.findlastindex": "4.6.7", "@types/react-syntax-highlighter": "11.0.4", diff --git a/editor/pnpm-lock.yaml b/editor/pnpm-lock.yaml index a624524d7631..9c062e509ccd 100644 --- a/editor/pnpm-lock.yaml +++ b/editor/pnpm-lock.yaml @@ -65,6 +65,7 @@ specifiers: '@liveblocks/react': 1.10.0 '@liveblocks/react-comments': 1.10.0 '@liveblocks/yjs': 1.10.0 + '@mhsdesign/jit-browser-tailwindcss': 0.4.1 '@originjs/vite-plugin-commonjs': 1.0.3 '@peculiar/webcrypto': 1.4.3 '@popperjs/core': 2.4.4 @@ -84,9 +85,6 @@ specifiers: '@testing-library/react': 14.0.0 '@testing-library/user-event': 14.5.2 '@tippyjs/react': 4.1.0 - '@twind/core': 1.1.3 - '@twind/preset-autoprefix': 1.0.7 - '@twind/preset-tailwind': 1.1.4 '@types/babel__traverse': 7.18.3 '@types/chai': 3.5.1 '@types/chroma-js': 2.0.0 @@ -370,6 +368,7 @@ dependencies: '@liveblocks/react': 1.10.0_react@18.1.0 '@liveblocks/react-comments': 1.10.0_wgqxz3kjpz4ixw4iz4mbg2tk4i '@liveblocks/yjs': 1.10.0_yjs@13.6.8 + '@mhsdesign/jit-browser-tailwindcss': 0.4.1 '@popperjs/core': 2.4.4 '@radix-ui/react-dropdown-menu': 2.1.1_wgqxz3kjpz4ixw4iz4mbg2tk4i '@radix-ui/react-select': 2.1.1_wgqxz3kjpz4ixw4iz4mbg2tk4i @@ -382,9 +381,6 @@ dependencies: '@svgr/plugin-jsx': 5.5.0 '@testing-library/user-event': 14.5.2 '@tippyjs/react': 4.1.0_ef5jwxihqo6n7gxfmzogljlgcm - '@twind/core': 1.1.3_typescript@5.5.4 - '@twind/preset-autoprefix': 1.0.7_gmrgfak3invo7trrvnu65or3fu - '@twind/preset-tailwind': 1.1.4_gmrgfak3invo7trrvnu65or3fu '@types/fontfaceobserver': 0.0.6 '@types/lodash.findlastindex': 4.6.7 '@types/react-syntax-highlighter': 11.0.4 @@ -670,6 +666,11 @@ packages: resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} engines: {node: '>=0.10.0'} + /@alloc/quick-lru/5.2.0: + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + dev: false + /@alloc/types/1.3.0: resolution: {integrity: sha512-mH7LiFiq9g6rX2tvt1LtwsclfG5hnsmtIfkZiauAGrm1AwXhoRS0sF2WrN9JGN7eV5vFXqNaB0eXZ3IvMsVi9g==} dev: false @@ -2716,7 +2717,6 @@ packages: strip-ansi-cjs: /strip-ansi/6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: /wrap-ansi/7.0.0 - dev: true /@istanbuljs/load-nyc-config/1.1.0: resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} @@ -3166,6 +3166,23 @@ packages: react: 18.1.0_47cciibm4ysmleigs33s763fqu dev: false + /@mhsdesign/jit-browser-tailwindcss/0.4.1: + resolution: {integrity: sha512-n08EkCXPP6XADdXtZ+zLRyF5qkEsoC37FrC7pAceREQRCT+SB9oI7qXMS2hNPwTXdBwUdR3d2TSk9R7imMyMRQ==} + dependencies: + color-name: 1.1.4 + didyoumean: 1.2.2 + dlv: 1.1.3 + postcss: 8.4.27 + postcss-js: 4.0.1_postcss@8.4.27 + postcss-nested: 5.0.6_postcss@8.4.27 + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 + quick-lru: 5.1.1 + tailwindcss: 3.4.13 + transitivePeerDependencies: + - ts-node + dev: false + /@nicolo-ribaudo/chokidar-2/2.1.8-no-fsevents.3: resolution: {integrity: sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==} requiresBuild: true @@ -3226,7 +3243,6 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} requiresBuild: true - dev: true optional: true /@polka/url/1.0.0-next.20: @@ -4918,48 +4934,6 @@ packages: resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} dev: false - /@twind/core/1.1.3_typescript@5.5.4: - resolution: {integrity: sha512-/B/aNFerMb2IeyjSJy3SJxqVxhrT77gBDknLMiZqXIRr4vNJqiuhx7KqUSRzDCwUmyGuogkamz+aOLzN6MeSLw==} - engines: {node: '>=14.15.0'} - peerDependencies: - typescript: ^4.8.4 - peerDependenciesMeta: - typescript: - optional: true - dependencies: - csstype: 3.1.2 - typescript: 5.5.4 - dev: false - - /@twind/preset-autoprefix/1.0.7_gmrgfak3invo7trrvnu65or3fu: - resolution: {integrity: sha512-3wmHO0pG/CVxYBNZUV0tWcL7CP0wD5KpyWAQE/KOalWmOVBj+nH6j3v6Y3I3pRuMFaG5DC78qbYbhA1O11uG3w==} - engines: {node: '>=14.15.0'} - peerDependencies: - '@twind/core': ^1.1.0 - typescript: ^4.8.4 - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@twind/core': 1.1.3_typescript@5.5.4 - style-vendorizer: 2.2.3 - typescript: 5.5.4 - dev: false - - /@twind/preset-tailwind/1.1.4_gmrgfak3invo7trrvnu65or3fu: - resolution: {integrity: sha512-zv85wrP/DW4AxgWrLfH7kyGn/KJF3K04FMLVl2AjoxZGYdCaoZDkL8ma3hzaKQ+WGgBFRubuB/Ku2Rtv/wjzVw==} - engines: {node: '>=14.15.0'} - peerDependencies: - '@twind/core': ^1.1.0 - typescript: ^4.8.4 - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@twind/core': 1.1.3_typescript@5.5.4 - typescript: 5.5.4 - dev: false - /@types/aria-query/5.0.1: resolution: {integrity: sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==} dev: true @@ -6342,7 +6316,6 @@ packages: /ansi-regex/6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} - dev: true /ansi-styles/1.1.0: resolution: {integrity: sha512-f2PKUkN5QngiSemowa6Mrk9MPCdtFiOSmibjZ+j1qhLGHHYsqZwmBMRF3IRMVXo8sybDqx2fJl2d/8OphBoWkA==} @@ -6374,7 +6347,6 @@ packages: /ansi-styles/6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} - dev: true /antd/4.3.5_ef5jwxihqo6n7gxfmzogljlgcm: resolution: {integrity: sha512-C1qILCKh+G7q06wtm4uqmyvqzAzAaDKKzuTO0BO+rtUbSATjzbUg2Bp5byDEw0ThUGTWfJEQ0J7HZHh0FgiaJA==} @@ -6445,8 +6417,7 @@ packages: dev: true /any-promise/1.3.0: - resolution: {integrity: sha1-q8av7tzqUugJzcA3au0845Y10X8=} - dev: true + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} /anymatch/3.1.2: resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} @@ -6454,7 +6425,10 @@ packages: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - dev: true + + /arg/5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + dev: false /argparse/1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -7047,7 +7021,6 @@ packages: /binary-extensions/2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} - dev: true /binaryextensions/4.18.0: resolution: {integrity: sha512-PQu3Kyv9dM4FnwB7XGj1+HucW+ShvJzJqjuw1JkKVs1mWdwOKVcRjOi+pV9X52A0tNvrPCsPkbFFQb+wE1EAXw==} @@ -7137,7 +7110,6 @@ packages: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} dependencies: balanced-match: 1.0.2 - dev: true /braces/2.3.2: resolution: {integrity: sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==} @@ -7484,6 +7456,11 @@ packages: tslib: 2.6.2 dev: true + /camelcase-css/2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + dev: false + /camelcase/5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} @@ -7662,7 +7639,6 @@ packages: readdirp: 3.6.0 optionalDependencies: fsevents: 2.3.3 - dev: true /chownr/2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} @@ -7973,7 +7949,6 @@ packages: /commander/4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} - dev: true /commander/6.2.1: resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} @@ -8402,7 +8377,6 @@ packages: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} hasBin: true - dev: true /cssnano/3.10.0: resolution: {integrity: sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=} @@ -8855,6 +8829,10 @@ packages: resolution: {integrity: sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==} dev: true + /didyoumean/1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dev: false + /diff-match-patch/1.0.5: resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} dev: false @@ -8907,6 +8885,10 @@ packages: resolution: {integrity: sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=} dev: true + /dlv/1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dev: false + /dnd-core/16.0.1: resolution: {integrity: sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==} dependencies: @@ -9081,7 +9063,6 @@ packages: /eastasianwidth/0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - dev: true /editions/4.2.0: resolution: {integrity: sha512-pmUkYpyeF6LY1tXXPaVr902upCeSAisp7vtY5udlSSj31emhRTNKBxEJk30sD6CJHbYSPAh+B3GDR4aJvXi/FQ==} @@ -10099,7 +10080,7 @@ packages: is-stream: 1.1.0 npm-run-path: 2.0.2 p-finally: 1.0.0 - signal-exit: 3.0.5 + signal-exit: 3.0.7 strip-eof: 1.0.0 dev: true @@ -10580,7 +10561,6 @@ packages: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 - dev: true /fork-ts-checker-async-overlay-webpack-plugin/0.4.0_sjzsq4ndqjvzfirlvtns5daumi: resolution: {integrity: sha512-PdmEymfvFsPsZ3o0b72rtcIOvl3QAUiVXIdV4uzuV/+AH0zjHYy9Mo8qN5wJSLYYzkHnDte+5rGf4mKhuONqJA==} @@ -10890,6 +10870,13 @@ packages: dependencies: is-glob: 4.0.3 + /glob-parent/6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: false + /glob-to-regexp/0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} @@ -10900,13 +10887,26 @@ packages: dependencies: foreground-child: 3.1.1 jackspeak: 2.3.6 - minimatch: 9.0.3 - minipass: 7.0.4 - path-scurry: 1.10.1 + minimatch: 9.0.5 + minipass: 7.1.2 + path-scurry: 1.11.1 dev: true + /glob/10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.0 + path-scurry: 1.11.1 + dev: false + /glob/3.1.21: resolution: {integrity: sha512-ANhy2V2+tFpRajE3wN4DhkNQ08KDr0Ir1qL12/cUe5+a7STEK8jkW4onUYuY8/06qAFuT5je7mjAqzx0eKI2tQ==} + deprecated: Glob versions prior to v9 are no longer supported dependencies: graceful-fs: 1.2.3 inherits: 1.0.2 @@ -11785,7 +11785,6 @@ packages: engines: {node: '>=8'} dependencies: binary-extensions: 2.2.0 - dev: true /is-boolean-object/1.1.2: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} @@ -12329,6 +12328,14 @@ packages: '@pkgjs/parseargs': 0.11.0 dev: true + /jackspeak/3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + dev: false + /jake/10.8.2: resolution: {integrity: sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==} hasBin: true @@ -12962,6 +12969,11 @@ packages: - utf-8-validate dev: true + /jiti/1.21.6: + resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} + hasBin: true + dev: false + /jotai-devtools/0.6.2_hgjqizhc26gc665cnflpub44vy: resolution: {integrity: sha512-iHKYt8V2T2Gh2DtGRpvpv2daVoFoHRJXqk5/LHnhFkJy9rMQuIGo4XgVu/v1ZMSvMzwDXdU3hDOQkfQWlDErUQ==} engines: {node: '>=14.0.0'} @@ -13462,6 +13474,16 @@ packages: immediate: 3.0.6 dev: false + /lilconfig/2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + dev: false + + /lilconfig/3.1.2: + resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} + engines: {node: '>=14'} + dev: false + /lines-and-columns/1.1.6: resolution: {integrity: sha512-8ZmlJFVK9iCmtLz19HpSsR8HaAMWBT284VMNednLwlIMDP2hJDCIhUp0IZ2xUcZ+Ob6BM0VvCSJwzASDM45NLQ==} @@ -13793,7 +13815,6 @@ packages: /lru-cache/10.2.0: resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} engines: {node: 14 || >=16.14} - dev: true /lru-cache/2.7.3: resolution: {integrity: sha512-WpibWJ60c3AgAz8a2iYErDrcT2C7OmKnsWhIcHOjkUHFjkXncJhtLxNSqUmxRxRunpb5I8Vprd7aNSd2NtksJQ==} @@ -14077,12 +14098,11 @@ packages: dependencies: brace-expansion: 1.1.11 - /minimatch/9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + /minimatch/9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} dependencies: brace-expansion: 2.0.1 - dev: true /minimist/1.2.0: resolution: {integrity: sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=} @@ -14102,10 +14122,9 @@ packages: yallist: 4.0.0 dev: true - /minipass/7.0.4: - resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + /minipass/7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - dev: true /minizlib/2.1.2: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} @@ -14230,6 +14249,14 @@ packages: engines: {node: '>=0.10.0'} dev: false + /mz/2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + dev: false + /nanoid/3.1.20: resolution: {integrity: sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -14402,7 +14429,6 @@ packages: /normalize-path/3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - dev: true /normalize-range/0.1.2: resolution: {integrity: sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=} @@ -14508,6 +14534,11 @@ packages: kind-of: 3.2.2 dev: true + /object-hash/3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + dev: false + /object-inspect/1.11.0: resolution: {integrity: sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==} dev: true @@ -14818,6 +14849,10 @@ packages: netmask: 2.0.2 dev: false + /package-json-from-dist/1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + dev: false + /pako/0.2.9: resolution: {integrity: sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=} dev: true @@ -14971,13 +15006,12 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /path-scurry/1.10.1: - resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} - engines: {node: '>=16 || 14 >=14.17'} + /path-scurry/1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} dependencies: lru-cache: 10.2.0 - minipass: 7.0.4 - dev: true + minipass: 7.1.2 /path-to-regexp/0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} @@ -15043,7 +15077,6 @@ packages: /pify/2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} - dev: true /pify/3.0.0: resolution: {integrity: sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=} @@ -15070,7 +15103,6 @@ packages: /pirates/4.0.6: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} - dev: true /pkg-dir/4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} @@ -15165,6 +15197,45 @@ packages: postcss: 5.2.18 dev: true + /postcss-import/15.1.0_postcss@8.4.27: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.27 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.2 + dev: false + + /postcss-js/4.0.1_postcss@8.4.27: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.27 + dev: false + + /postcss-load-config/4.0.2_postcss@8.4.27: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 3.1.2 + postcss: 8.4.27 + yaml: 2.5.1 + dev: false + /postcss-merge-idents/2.1.7: resolution: {integrity: sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=} dependencies: @@ -15253,6 +15324,26 @@ packages: postcss: 6.0.23 dev: true + /postcss-nested/5.0.6_postcss@8.4.27: + resolution: {integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.27 + postcss-selector-parser: 6.1.2 + dev: false + + /postcss-nested/6.2.0_postcss@8.4.27: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.27 + postcss-selector-parser: 6.1.2 + dev: false + /postcss-normalize-charset/1.1.1: resolution: {integrity: sha1-757nEhLX/nWceO0WL2HtYrXLk/E=} dependencies: @@ -15297,13 +15388,21 @@ packages: dev: true /postcss-selector-parser/2.2.3: - resolution: {integrity: sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=} + resolution: {integrity: sha512-3pqyakeGhrO0BQ5+/tGTfvi5IAUAhHRayGK8WFSu06aEv2BmHoXw/Mhb+w7VY5HERIuC+QoUI7wgrCcq2hqCVA==} dependencies: flatten: 1.0.3 indexes-of: 1.0.1 uniq: 1.0.1 dev: true + /postcss-selector-parser/6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: false + /postcss-svgo/2.1.6: resolution: {integrity: sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=} dependencies: @@ -15325,6 +15424,10 @@ packages: resolution: {integrity: sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==} dev: true + /postcss-value-parser/4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: false + /postcss-zindex/2.2.0: resolution: {integrity: sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=} dependencies: @@ -15688,7 +15791,6 @@ packages: /quick-lru/5.1.1: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} - dev: true /raf/3.4.1: resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} @@ -16891,6 +16993,12 @@ packages: loose-envify: 1.4.0 patched: true + /read-cache/1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + dependencies: + pify: 2.3.0 + dev: false + /read-only-stream/2.0.0: resolution: {integrity: sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=} dependencies: @@ -16939,7 +17047,6 @@ packages: engines: {node: '>=8.10.0'} dependencies: picomatch: 2.3.1 - dev: true /rechoir/0.8.0: resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} @@ -17678,10 +17785,6 @@ packages: resolution: {integrity: sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=} dev: true - /signal-exit/3.0.5: - resolution: {integrity: sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==} - dev: true - /signal-exit/3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true @@ -17689,7 +17792,6 @@ packages: /signal-exit/4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - dev: true /simple-concat/1.0.1: resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} @@ -18188,7 +18290,6 @@ packages: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.1.0 - dev: true /string.prototype.matchall/4.0.5: resolution: {integrity: sha512-Z5ZaXO0svs0M2xd/6By3qpeKpLKd9mO4v4q3oMEQrk8Ck4xOD5d5XeBOOjGrmVZZ/AHB1S0CgG4N5r1G9N3E2Q==} @@ -18302,7 +18403,6 @@ packages: engines: {node: '>=12'} dependencies: ansi-regex: 6.0.1 - dev: true /strip-bom/2.0.0: resolution: {integrity: sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==} @@ -18353,10 +18453,6 @@ packages: inline-style-parser: 0.1.1 dev: false - /style-vendorizer/2.2.3: - resolution: {integrity: sha512-/VDRsWvQAgspVy9eATN3z6itKTuyg+jW1q6UoTCQCFRqPDw8bi3E1hXIKnGw5LvXS2AQPuJ7Af4auTLYeBOLEg==} - dev: false - /stylis/4.0.10: resolution: {integrity: sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==} dev: false @@ -18367,6 +18463,20 @@ packages: minimist: 1.2.8 dev: true + /sucrase/3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + commander: 4.1.1 + glob: 10.4.5 + lines-and-columns: 1.1.6 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + dev: false + /superstruct/0.8.4: resolution: {integrity: sha512-48Ors8IVWZm/tMr8r0Si6+mJiB7mkD7jqvIzktjJ4+EnP5tBp0qOpiM1J8sCUorKx+TXWrfb3i1UcjdD1YK/wA==} dependencies: @@ -18482,6 +18592,37 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 + /tailwindcss/3.4.13: + resolution: {integrity: sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.5.3 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.1 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.6 + lilconfig: 2.1.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.27 + postcss-import: 15.1.0_postcss@8.4.27 + postcss-js: 4.0.1_postcss@8.4.27 + postcss-load-config: 4.0.2_postcss@8.4.27 + postcss-nested: 6.2.0_postcss@8.4.27 + postcss-selector-parser: 6.1.2 + resolve: 1.22.2 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + dev: false + /tapable/1.1.3: resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==} engines: {node: '>=6'} @@ -18591,6 +18732,19 @@ packages: engines: {node: '>=0.8'} dev: false + /thenify-all/1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + dependencies: + thenify: 3.3.1 + dev: false + + /thenify/3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + dependencies: + any-promise: 1.3.0 + dev: false + /three/0.139.2: resolution: {integrity: sha512-gV7q7QY8rogu7HLFZR9cWnOQAUedUhu2WXAnpr2kdXZP9YDKsG/0ychwQvWkZN5PlNw9mv5MoCTin6zNTXoONg==} dev: false @@ -18782,6 +18936,10 @@ packages: dependencies: typescript: 5.5.4 + /ts-interface-checker/0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + dev: false + /ts-loader/5.3.3_typescript@5.5.4: resolution: {integrity: sha512-KwF1SplmOJepnoZ4eRIloH/zXL195F51skt7reEsS6jvDqzgc/YSbz9b8E07GxIUwLXdcD4ssrJu6v8CwaTafA==} engines: {node: '>=6.11.5'} @@ -19839,7 +19997,6 @@ packages: ansi-styles: 6.2.1 string-width: 5.1.2 strip-ansi: 7.1.0 - dev: true /wrappy/1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -19978,6 +20135,12 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} + /yaml/2.5.1: + resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} + engines: {node: '>= 14'} + hasBin: true + dev: false + /yargs-parser/20.2.4: resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} engines: {node: '>=10'} diff --git a/editor/src/components/canvas/ui-jsx-canvas.tsx b/editor/src/components/canvas/ui-jsx-canvas.tsx index 03b2215fd10e..85239c3dce47 100644 --- a/editor/src/components/canvas/ui-jsx-canvas.tsx +++ b/editor/src/components/canvas/ui-jsx-canvas.tsx @@ -77,7 +77,6 @@ import { applyUIDMonkeyPatch } from '../../utils/canvas-react-utils' import type { RemixValidPathsGenerationContext } from './canvas-utils' import { getParseSuccessForFilePath, getValidElementPaths } from './canvas-utils' import { arrayEqualsByValue, fastForEach, NO_OP } from '../../core/shared/utils' -import { useTwind } from '../../core/tailwind/tailwind' import { AlwaysFalse, atomWithPubSub, @@ -98,6 +97,7 @@ import { IS_TEST_ENVIRONMENT } from '../../common/env-vars' import { listenForReactRouterErrors } from '../../core/shared/runtime-report-logs' import { getFilePathMappings } from '../../core/model/project-file-utils' import { useInvalidatedCanvasRemount } from './canvas-component-entry' +import { useTailwindCompilation } from '../../core/tailwind/tailwind-compilation' applyUIDMonkeyPatch() @@ -498,7 +498,7 @@ export const UiJsxCanvas = React.memo((props) const executionScope = scope - useTwind(projectContentsForRequireFn, customRequire, '#canvas-container') + useTailwindCompilation(customRequire) const topLevelElementsMap = useKeepReferenceEqualityIfPossible(new Map(topLevelJsxComponents)) diff --git a/editor/src/components/inspector/inspector.tsx b/editor/src/components/inspector/inspector.tsx index e4ea5d8d688c..2b6686ec0e4a 100644 --- a/editor/src/components/inspector/inspector.tsx +++ b/editor/src/components/inspector/inspector.tsx @@ -48,7 +48,6 @@ import { import { getElementsToTarget } from './common/inspector-utils' import type { ElementPath, PropertyPath } from '../../core/shared/project-file-types' import { unless, when } from '../../utils/react-conditionals' -import { isTwindEnabled } from '../../core/tailwind/tailwind' import { isKeyboardAbsoluteStrategy, isKeyboardReorderStrategy, @@ -90,6 +89,7 @@ import { import { InspectorSectionHeader } from './section-header' import { GridPlacementSubsection } from './sections/style-section/container-subsection/grid-cell-subsection' import { ContainerSubsection } from './sections/style-section/container-subsection/container-subsection' +import { isTailwindEnabled } from '../../core/tailwind/tailwind-compilation' export interface ElementPathElement { name?: string @@ -286,7 +286,8 @@ export const Inspector = React.memo((props: InspectorProps) => { const shouldShowStyleSectionContents = styleSectionOpen && !shouldHideInspectorSections const shouldShowAdvancedSectionContents = advancedSectionOpen && !shouldHideInspectorSections - const shouldShowClassNameSubsection = isTwindEnabled() && inspectorPreferences.includes('visual') + const shouldShowClassNameSubsection = + isTailwindEnabled() && inspectorPreferences.includes('visual') const shouldShowTargetSelectorSection = canEdit && inspectorPreferences.includes('visual') const shouldShowFlexSection = multiselectedContract === 'frame' && diff --git a/editor/src/core/tailwind/tailwind-compilation.ts b/editor/src/core/tailwind/tailwind-compilation.ts new file mode 100644 index 000000000000..9e030e1b83ad --- /dev/null +++ b/editor/src/core/tailwind/tailwind-compilation.ts @@ -0,0 +1,106 @@ +import React from 'react' +import type { TailwindConfig, Tailwindcss } from '@mhsdesign/jit-browser-tailwindcss' +import { createTailwindcss } from '@mhsdesign/jit-browser-tailwindcss' +import type { ProjectContentTreeRoot } from 'utopia-shared/src/types' +import { getProjectFileByFilePath, walkContentsTree } from '../../components/assets' +import { interactionSessionIsActive } from '../../components/canvas/canvas-strategies/interaction-state' +import { CanvasContainerID } from '../../components/canvas/canvas-types' +import { + Substores, + useEditorState, + useRefEditorState, +} from '../../components/editor/store/store-hook' +import { importDefault } from '../es-modules/commonjs-interop' +import { rescopeCSSToTargetCanvasOnly } from '../shared/css-utils' +import type { RequireFn } from '../shared/npm-dependency-types' +import { TailwindConfigPath } from './tailwind-config' +import { ElementsToRerenderGLOBAL } from '../../components/canvas/ui-jsx-canvas' +import { isFeatureEnabled } from '../../utils/feature-switches' + +const TAILWIND_INSTANCE: { current: Tailwindcss | null } = { current: null } + +export function isTailwindEnabled(): boolean { + return TAILWIND_INSTANCE.current != null +} + +function ensureElementExists({ type, id }: { type: string; id: string }) { + let tag = document.getElementById(id) + if (tag == null) { + tag = document.createElement(type) + tag.id = id + document.head.appendChild(tag) + } + return tag +} + +async function generateTailwindStyles(tailwindCss: Tailwindcss, allCSSFiles: string) { + const contentElement = document.getElementById(CanvasContainerID) + if (contentElement == null) { + return + } + const content = contentElement.outerHTML + const styleString = await tailwindCss.generateStylesFromContent(allCSSFiles, [content]) + const style = ensureElementExists({ type: 'style', id: 'utopia-tailwind-jit-styles' }) + style.textContent = rescopeCSSToTargetCanvasOnly(styleString) +} + +function getCssFilesFromProjectContents(projectContents: ProjectContentTreeRoot) { + let files: string[] = [] + walkContentsTree(projectContents, (path, file) => { + if (file.type === 'TEXT_FILE' && path.endsWith('.css')) { + files.push(file.fileContents.code) + } + }) + return files +} + +function generateTailwindClasses(projectContents: ProjectContentTreeRoot, requireFn: RequireFn) { + const allCSSFiles = getCssFilesFromProjectContents(projectContents).join('\n') + const rawConfig = importDefault(requireFn('/', TailwindConfigPath)) + const tailwindCss = createTailwindcss({ tailwindConfig: rawConfig as TailwindConfig }) + TAILWIND_INSTANCE.current = tailwindCss + void generateTailwindStyles(tailwindCss, allCSSFiles) +} + +export const useTailwindCompilation = (requireFn: RequireFn) => { + const projectContents = useEditorState( + Substores.projectContents, + (store) => store.editor.projectContents, + 'useTailwindCompilation projectContents', + ) + + const isInteractionActiveRef = useRefEditorState((store) => + interactionSessionIsActive(store.editor.canvas.interactionSession), + ) + + const observerCallback = React.useCallback(() => { + if ( + isInteractionActiveRef.current || + ElementsToRerenderGLOBAL.current !== 'rerender-all-elements' || // implies that an interaction is in progress + !isFeatureEnabled('Tailwind') + ) { + return + } + generateTailwindClasses(projectContents, requireFn) + }, [isInteractionActiveRef, projectContents, requireFn]) + + React.useEffect(() => { + const tailwindConfigFile = getProjectFileByFilePath(projectContents, TailwindConfigPath) + if (tailwindConfigFile == null || tailwindConfigFile.type !== 'TEXT_FILE') { + return // we consider tailwind to be enabled if there's a tailwind config file in the project + } + const observer = new MutationObserver(observerCallback) + + observer.observe(document.getElementById(CanvasContainerID)!, { + attributes: true, + childList: true, + subtree: true, + }) + + observerCallback() + + return () => { + observer.disconnect() + } + }, [isInteractionActiveRef, observerCallback, projectContents, requireFn]) +} diff --git a/editor/src/core/tailwind/tailwind-options.tsx b/editor/src/core/tailwind/tailwind-options.tsx index e0a0dc6140d2..93710dce45f0 100644 --- a/editor/src/core/tailwind/tailwind-options.tsx +++ b/editor/src/core/tailwind/tailwind-options.tsx @@ -28,9 +28,9 @@ import { } from '../shared/jsx-attribute-utils' import * as PP from '../shared/property-path' import * as EP from '../shared/element-path' -import { isTwindEnabled } from './tailwind' import type { AttributeCategory } from './attribute-categories' import { AttributeCategories } from './attribute-categories' +import { isTailwindEnabled } from './tailwind-compilation' export interface TailWindOption { label: string @@ -165,7 +165,7 @@ export function useFilteredOptions( onEmptyResults: () => void = NO_OP, ): Array { return React.useMemo(() => { - if (isTwindEnabled()) { + if (isTailwindEnabled()) { const sanitisedFilter = filter.trim().toLowerCase() const searchTerms = searchStringToIndividualTerms(sanitisedFilter) let results: Array diff --git a/editor/src/core/tailwind/tailwind.spec.browser2.tsx b/editor/src/core/tailwind/tailwind.spec.browser2.tsx new file mode 100644 index 000000000000..8040db905fdc --- /dev/null +++ b/editor/src/core/tailwind/tailwind.spec.browser2.tsx @@ -0,0 +1,218 @@ +import { renderTestEditorWithModel } from '../../components/canvas/ui-jsx.test-utils' +import { createModifiedProject } from '../../sample-projects/sample-project-utils.test-utils' +import { setFeatureForBrowserTestsUseInDescribeBlockOnly } from '../../utils/utils.test-utils' +import { wait } from '../model/performance-scripts' + +const Project = createModifiedProject({ + '/utopia/storyboard.js': `import { Scene, Storyboard } from 'utopia-api' + +export var storyboard = ( + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Text Shadow + This is a medium text shadow example + This is a large text shadow example + This has no text shadow +
+ + +) +`, + '/src/app.css': ` +@tailwind base; +@tailwind components; +@tailwind utilities; +`, + 'tailwind.config.js': ` +const Tailwind = { + theme: { + colors: { + transparent: 'transparent', + current: 'currentColor', + white: '#ffffff', + purple: '#3f3cbb', + midnight: '#121063', + metal: '#565584', + tahiti: '#3ab7bf', + silver: '#ecebff', + 'bubble-gum': '#ff77e9', + bermuda: '#78dcca', + }, + }, + plugins: [ + function ({ addUtilities }) { + const newUtilities = { + '.text-shadow': { + textShadow: '2px 2px 4px rgba(0, 0, 0, 0.1)', + }, + '.text-shadow-md': { + textShadow: '3px 3px 6px rgba(0, 0, 0, 0.2)', + }, + '.text-shadow-lg': { + textShadow: '4px 4px 8px rgba(0, 0, 0, 0.3)', + }, + '.text-shadow-none': { + textShadow: 'none', + }, + } + + addUtilities(newUtilities, ['responsive', 'hover']) + }, + ], +} +export default Tailwind +`, +}) + +describe('rendering tailwind projects in the editor', () => { + setFeatureForBrowserTestsUseInDescribeBlockOnly('Tailwind', true) + it('can render absolute positioning classes', async () => { + const editor = await renderTestEditorWithModel(Project, 'await-first-dom-report') + + { + const absolute = editor.renderedDOM.getByTestId('absolute') + const { position, top, left, width, height, backgroundColor } = getComputedStyle(absolute) + expect({ position, top, left, width, height, backgroundColor }).toEqual({ + backgroundColor: 'rgb(58, 183, 191)', + height: '177px', + left: '40px', + position: 'absolute', + top: '33px', + width: '620px', + }) + } + { + const absoluteChild = editor.renderedDOM.getByTestId('absolute-child') + const { position, top, left, width, height, backgroundColor } = + getComputedStyle(absoluteChild) + expect({ position, top, left, width, height, backgroundColor }).toEqual({ + backgroundColor: 'rgb(18, 16, 99)', + height: '40px', + left: '16px', + position: 'absolute', + top: '16px', + width: '40px', + }) + } + }) + + it('can render flex positioning classes', async () => { + const editor = await renderTestEditorWithModel(Project, 'await-first-dom-report') + + const flex = editor.renderedDOM.getByTestId('flex') + const { display, flexDirection, justifyContent, alignItems, gap, padding } = + getComputedStyle(flex) + expect({ display, flexDirection, justifyContent, alignItems, gap, padding }).toEqual({ + alignItems: 'center', + display: 'flex', + flexDirection: 'row', + gap: '40px', + justifyContent: 'flex-start', + padding: '30px 20px', + }) + }) + + it('can render grid positioning classes', async () => { + const editor = await renderTestEditorWithModel(Project, 'await-first-dom-report') + + const grid = editor.renderedDOM.getByTestId('grid') + const { display, gridTemplateColumns, gridTemplateRows, gap } = getComputedStyle(grid) + expect({ display, gridTemplateColumns, gridTemplateRows, gap }).toEqual({ + display: 'grid', + gap: '16px', + gridTemplateColumns: '138px 138px 138px 138px', + gridTemplateRows: '50.5px 50.5px 50.5px 50.5px', + }) + + { + const gridChild2 = editor.renderedDOM.getByTestId('grid-child-2') + const { gridColumnStart, gridColumnEnd, gridRowStart, gridRowEnd, backgroundColor } = + getComputedStyle(gridChild2) + expect({ gridColumnStart, gridColumnEnd, gridRowStart, gridRowEnd, backgroundColor }).toEqual( + { + backgroundColor: 'rgb(255, 119, 233)', + gridColumnEnd: 'span 3', + gridColumnStart: 'span 3', + gridRowEnd: 'auto', + gridRowStart: '2', + }, + ) + } + + { + const gridChild3 = editor.renderedDOM.getByTestId('grid-child-3') + const { gridColumnStart, gridColumnEnd, gridRowStart, gridRowEnd, backgroundColor } = + getComputedStyle(gridChild3) + expect({ gridColumnStart, gridColumnEnd, gridRowStart, gridRowEnd, backgroundColor }).toEqual( + { + backgroundColor: 'rgb(255, 119, 233)', + gridColumnEnd: 'auto', + gridColumnStart: '4', + gridRowEnd: 'auto', + gridRowStart: '3', + }, + ) + } + }) + + it('can render classes added from custom plugins', async () => { + const editor = await renderTestEditorWithModel(Project, 'await-first-dom-report') + + { + const textShadow1 = editor.renderedDOM.getByTestId('text-shadow-1') + const { textShadow, fontSize, lineHeight } = getComputedStyle(textShadow1) + expect({ textShadow, fontSize, lineHeight }).toEqual({ + fontSize: '36px', + lineHeight: '40px', + textShadow: 'rgba(0, 0, 0, 0.1) 2px 2px 4px', + }) + } + { + const textShadow2 = editor.renderedDOM.getByTestId('text-shadow-2') + const { textShadow, fontSize, lineHeight } = getComputedStyle(textShadow2) + expect({ textShadow, fontSize, lineHeight }).toEqual({ + fontSize: '30px', + lineHeight: '36px', + textShadow: 'rgba(0, 0, 0, 0.2) 3px 3px 6px', + }) + } + { + const textShadow3 = editor.renderedDOM.getByTestId('text-shadow-3') + const { textShadow, fontSize, lineHeight } = getComputedStyle(textShadow3) + expect({ textShadow, fontSize, lineHeight }).toEqual({ + fontSize: '24px', + lineHeight: '32px', + textShadow: 'rgba(0, 0, 0, 0.3) 4px 4px 8px', + }) + } + { + const textShadow4 = editor.renderedDOM.getByTestId('text-shadow-4') + const { textShadow, fontSize, lineHeight } = getComputedStyle(textShadow4) + expect({ textShadow, fontSize, lineHeight }).toEqual({ + fontSize: '20px', + lineHeight: '28px', + textShadow: 'none', + }) + } + }) +}) diff --git a/editor/src/core/tailwind/tailwind.spec.ts b/editor/src/core/tailwind/tailwind.spec.ts deleted file mode 100644 index ce4e4b35ed3e..000000000000 --- a/editor/src/core/tailwind/tailwind.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { adjustRuleScopeImpl } from './tailwind' - -describe('adjustRuleScope', () => { - it('Returns a rule unchanged if no prefix is provided', () => { - const input = '.container{width:100%}' - const output = adjustRuleScopeImpl(input, null) - const expected = input - expect(output).toEqual(expected) - }) - - it('Returns a rule unchanged if it is an at-rule but not a media query', () => { - const input = '@keyframes spin{to{transform:rotate(360deg)}}' - const output = adjustRuleScopeImpl(input, '#canvas-container') - const expected = input - expect(output).toEqual(expected) - }) - - it('Prefixes a regular selector', () => { - const input = '.container{width:100%}' - const output = adjustRuleScopeImpl(input, '#canvas-container') - const expected = '#canvas-container .container{width:100%}' - expect(output).toEqual(expected) - }) - - it('Handles comma separated selectors', () => { - const input = '.container,.other-container{width:100%}' - const output = adjustRuleScopeImpl(input, '#canvas-container') - const expected = '#canvas-container .container,#canvas-container .other-container{width:100%}' - expect(output).toEqual(expected) - }) - - it('Replaces :root, html, or head with the provided prefix', () => { - const rootOutput = adjustRuleScopeImpl(':root{width:100%}', '#canvas-container') - const htmlOutput = adjustRuleScopeImpl('html{width:100%}', '#canvas-container') - const headOutput = adjustRuleScopeImpl('head{width:100%}', '#canvas-container') - const expected = '#canvas-container{width:100%}' - expect(rootOutput).toEqual(expected) - expect(htmlOutput).toEqual(expected) - expect(headOutput).toEqual(expected) - }) - - it('Replaces body with a selector for children of the provided prefix', () => { - const input = 'body{width:100%}' - const output = adjustRuleScopeImpl(input, '#canvas-container') - const expected = '#canvas-container > *{width:100%}' - expect(output).toEqual(expected) - }) - - it('Handles a media query', () => { - const input = '@media (min-width:640px){.container{max-width:640px}}' - const output = adjustRuleScopeImpl(input, '#canvas-container') - const expected = '@media (min-width:640px){#canvas-container .container{max-width:640px}}' - expect(output).toEqual(expected) - }) -}) diff --git a/editor/src/core/tailwind/tailwind.ts b/editor/src/core/tailwind/tailwind.ts deleted file mode 100644 index e6f348483c79..000000000000 --- a/editor/src/core/tailwind/tailwind.ts +++ /dev/null @@ -1,291 +0,0 @@ -import type { ProjectContentTreeRoot } from '../../components/assets' -import { packageJsonFileFromProjectContents } from '../../components/assets' -import { getProjectFileByFilePath } from '../../components/assets' -import type { Either } from '../shared/either' -import { isRight, left, right } from '../shared/either' -import type { RequireFn } from '../shared/npm-dependency-types' -import type { ProjectFile } from '../shared/project-file-types' -import { isTextFile } from '../shared/project-file-types' -import type { Sheet, Twind } from '@twind/core' -import { cssom, observe, defineConfig, tw } from '@twind/core' -import presetAutoprefix from '@twind/preset-autoprefix' -import presetTailwind from '@twind/preset-tailwind' -import React from 'react' -import { includesDependency } from '../../components/editor/npm-dependency/npm-dependency' -import { propOrNull } from '../shared/object-utils' -import { memoize } from '../shared/memoize' -import { importDefault } from '../es-modules/commonjs-interop' -import { PostCSSPath, TailwindConfigPath } from './tailwind-config' -import { useKeepReferenceEqualityIfPossible } from '../../utils/react-performance' -import { twind } from '@twind/core' - -type TwindConfigType = ReturnType - -function hasRequiredDependenciesForTailwind(packageJsonFile: ProjectFile): boolean { - const hasTailwindDependency = includesDependency(packageJsonFile, 'tailwindcss') - const hasPostCSSDependency = includesDependency(packageJsonFile, 'postcss') - - return hasTailwindDependency && hasPostCSSDependency -} - -function useGetPackageJson(projectContents: ProjectContentTreeRoot): ProjectFile | null { - return React.useMemo(() => packageJsonFileFromProjectContents(projectContents), [projectContents]) -} - -function useHasRequiredDependenciesForTailwind(projectContents: ProjectContentTreeRoot): boolean { - const packageJsonFile = useGetPackageJson(projectContents) - return React.useMemo(() => { - return packageJsonFile != null && hasRequiredDependenciesForTailwind(packageJsonFile) - }, [packageJsonFile]) -} - -function postCSSIncludesTailwindPlugin(postCSSFile: ProjectFile, requireFn: RequireFn): boolean { - if (isTextFile(postCSSFile)) { - try { - const requireResult = requireFn('/', PostCSSPath) - const rawConfig = importDefault(requireResult) - const plugins = (rawConfig as any)?.plugins - return plugins?.tailwindcss != null - } catch (e) { - /* Do nothing */ - } - } - - return false -} - -function useGetPostCSSConfigFile(projectContents: ProjectContentTreeRoot): ProjectFile | null { - return React.useMemo( - () => getProjectFileByFilePath(projectContents, PostCSSPath), - [projectContents], - ) -} - -function usePostCSSIncludesTailwindPlugin( - projectContents: ProjectContentTreeRoot, - requireFn: RequireFn, -): boolean { - const postCSSFile = useGetPostCSSConfigFile(projectContents) - return React.useMemo(() => { - return postCSSFile != null && postCSSIncludesTailwindPlugin(postCSSFile, requireFn) - }, [postCSSFile, requireFn]) -} - -const PreflightKey = 'preflight' - -function enablesPreflight(tailwindConfig: any): boolean { - const corePlugins = propOrNull('corePlugins', tailwindConfig) - if (corePlugins != null && typeof corePlugins === 'object') { - if (Array.isArray(corePlugins)) { - // This means we have an explicit list of all allowed core plugins - return corePlugins.includes(PreflightKey) - } else { - // We have an object that enables or disables specific plugins - all unlisted plugins are enabled - const explicitDisabled = corePlugins[PreflightKey] === false // Tailwind doesn't use truthiness here - return !explicitDisabled - } - } - - return true -} - -function convertTailwindToTwindConfig(tailwindConfig: any): TwindConfigType { - const preflightEnabled = enablesPreflight(tailwindConfig) - - const twindConfig = defineConfig({ - presets: [ - presetTailwind({ - disablePreflight: !preflightEnabled, - }), - presetAutoprefix(), - ], - // force pushing tailwind's config to twind - theme: tailwindConfig.theme, - darkMode: tailwindConfig.darkMode, - variants: tailwindConfig.variants, - preflight: tailwindConfig.preflight, - }) - - return twindConfig -} - -function getTailwindConfig( - tailwindFile: ProjectFile | null, - requireFn: RequireFn, -): Either { - if (tailwindFile != null && isTextFile(tailwindFile)) { - try { - const requireResult = requireFn('/', TailwindConfigPath) - const rawConfig = importDefault(requireResult) - if (rawConfig != null) { - const twindConfig = convertTailwindToTwindConfig(rawConfig) - return right(twindConfig) - } else { - return left('Tailwind config contains no default export') - } - } catch (error) { - console.error('Error loading tailwind config', error) - return left(error) - } - } - - return left('Invalid or missing tailwind config file type') -} - -function useGetTailwindConfigFile(projectContents: ProjectContentTreeRoot): ProjectFile | null { - return React.useMemo( - () => getProjectFileByFilePath(projectContents, TailwindConfigPath), - [projectContents], - ) -} - -function useGetTailwindConfig( - projectContents: ProjectContentTreeRoot, - requireFn: RequireFn, -): TwindConfigType { - const tailwindConfigFile = useGetTailwindConfigFile(projectContents) - const tailwindConfig = React.useMemo(() => { - const maybeConfig = getTailwindConfig(tailwindConfigFile, requireFn) - if (isRight(maybeConfig)) { - return maybeConfig.value - } else { - return defineConfig({}) - } - }, [tailwindConfigFile, requireFn]) - return useKeepReferenceEqualityIfPossible(tailwindConfig) -} - -interface TwindInstance { - element: HTMLStyleElement - instance: Twind -} - -let twindInstance: TwindInstance | null = null - -export function isTwindEnabled(): boolean { - return twindInstance != null -} - -function clearTwind() { - if (twindInstance != null) { - twindInstance.instance.clear() - twindInstance.instance.destroy() - twindInstance.element.parentNode?.removeChild(twindInstance.element) - } -} - -export function adjustRuleScopeImpl(rule: string, prefixSelector: string | null): string { - // TODO Use css-tree to handle more complex cases. That doesn't seem necessary right now since Tailwind - // as at 2.2.4 only uses @media and @keyframes - const isMediaQuery = rule.startsWith('@media') - const isOtherAtRule = rule.startsWith('@') && !isMediaQuery - if (prefixSelector == null || isOtherAtRule) { - return rule - } else { - const splitOnBrace = rule.split('{') - const selectorIndex = isMediaQuery ? 1 : 0 - const splitSelectors = splitOnBrace[selectorIndex].split(',') - const scopedSelectors = splitSelectors.map((s) => { - const lowerCaseSelector = s.toLowerCase().trim() - - if ( - lowerCaseSelector === ':root' || - lowerCaseSelector === 'html' || - lowerCaseSelector === 'head' - ) { - return prefixSelector - } else if (lowerCaseSelector === 'body') { - return `${prefixSelector} > *` - } else { - return `${prefixSelector} ${s}` - } - }) - const joinedSelectors = scopedSelectors.join(',') - const theRest = splitOnBrace.slice(selectorIndex + 1) - const front = splitOnBrace.slice(0, selectorIndex) - const finalRule = [...front, joinedSelectors, ...theRest].join('{') - return finalRule - } -} - -const adjustRuleScope = memoize(adjustRuleScopeImpl, { - maxSize: 100, - matchesArg: (a, b) => a === b, -}) - -function updateTwind(config: TwindConfigType, prefixSelector: string | null) { - const element = document.head.appendChild(document.createElement('style')) - element.appendChild(document.createTextNode('')) // Avoid Edge bug where empty style elements doesn't create sheets - element.setAttribute('id', `twind-styles-${Math.random().toString(36).slice(2)}`) - - const sheet = cssom(element) - const customSheet: Sheet = { - ...sheet, - target: sheet.target, - insert: (rule, index, sheetRule) => { - const scopedRule = adjustRuleScope(rule, prefixSelector) - sheet.insert(scopedRule, index, sheetRule) - }, - } - - clearTwind() - - if (twindInstance == null) { - const prefixes = ['TWIND_', 'TAILWIND_'] - window.addEventListener('warning', (event: any | { detail: { code: string } }) => { - const isTwindWarning: boolean = prefixes.some((prefix) => - event?.detail?.code?.startsWith?.(prefix), - ) - if (isTwindWarning) { - event.preventDefault() - } - }) - } - - const instance = observe(twind(config, customSheet), document.documentElement) - - twindInstance = { - element: element, - instance: instance, - } -} - -export function useTwind( - projectContents: ProjectContentTreeRoot, - requireFn: RequireFn, - prefixSelector: string | null = null, -): void { - const hasDependencies = useHasRequiredDependenciesForTailwind(projectContents) - const hasPostCSSPlugin = usePostCSSIncludesTailwindPlugin(projectContents, requireFn) - const shouldUseTwind = hasDependencies && hasPostCSSPlugin - const tailwindConfig = useGetTailwindConfig(projectContents, requireFn) - React.useEffect(() => { - if (shouldUseTwind) { - updateTwind(tailwindConfig, prefixSelector) - } else { - clearTwind() - } - }, [prefixSelector, shouldUseTwind, tailwindConfig]) -} - -export function injectTwind( - projectContents: ProjectContentTreeRoot, - requireFn: RequireFn, - prefixSelector: string | null = null, -): void { - const packageJsonFile = packageJsonFileFromProjectContents(projectContents) - const hasDependencies = - packageJsonFile != null && hasRequiredDependenciesForTailwind(packageJsonFile) - const postCSSFile = getProjectFileByFilePath(projectContents, PostCSSPath) - const hasPostCSSPlugin = - postCSSFile != null && postCSSIncludesTailwindPlugin(postCSSFile, requireFn) - const shouldUseTwind = hasDependencies && hasPostCSSPlugin - const tailwindConfigFile = getProjectFileByFilePath(projectContents, TailwindConfigPath) - const maybeTailwindConfig = getTailwindConfig(tailwindConfigFile, requireFn) - const tailwindConfig = isRight(maybeTailwindConfig) ? maybeTailwindConfig.value : defineConfig({}) - if (shouldUseTwind) { - updateTwind(tailwindConfig, prefixSelector) - } else { - clearTwind() - } -} diff --git a/editor/src/templates/preview/preview.tsx b/editor/src/templates/preview/preview.tsx index 3379822ff2ea..c2db43f38fb9 100644 --- a/editor/src/templates/preview/preview.tsx +++ b/editor/src/templates/preview/preview.tsx @@ -21,7 +21,6 @@ import { pluck } from '../../core/shared/array-utils' import { getMainHTMLFilename, getMainJSFilename } from '../../core/shared/project-contents-utils' import type { NodeModules } from '../../core/shared/project-file-types' import { isTextFile } from '../../core/shared/project-file-types' -import { injectTwind } from '../../core/tailwind/tailwind' import { NewBundlerWorker, RealBundlerWorker } from '../../core/workers/bundler-bridge' import { createBundle } from '../../core/workers/bundler-promise' import Utils from '../../utils/utils' @@ -280,8 +279,6 @@ const initPreview = () => { } } - injectTwind(projectContents, requireFn) - const previewJSFileName = getMainJSFilename(projectContents) const previewJSFilePath = `/${previewJSFileName}` const previewJSFile = getProjectFileByFilePath(projectContents, previewJSFilePath) diff --git a/editor/src/utils/__mocks__/jit-browser-tailwindcss.js b/editor/src/utils/__mocks__/jit-browser-tailwindcss.js new file mode 100644 index 000000000000..54cc6d0eb188 --- /dev/null +++ b/editor/src/utils/__mocks__/jit-browser-tailwindcss.js @@ -0,0 +1,5 @@ +module.exports = { + createTailwindcss: () => ({ + generateStylesFromContent: () => '', + }), +} diff --git a/editor/src/utils/feature-switches.ts b/editor/src/utils/feature-switches.ts index cce2962ef6c7..9fa1a0064189 100644 --- a/editor/src/utils/feature-switches.ts +++ b/editor/src/utils/feature-switches.ts @@ -17,6 +17,7 @@ export type FeatureName = | 'Condensed Navigator Entries' | 'Canvas Fast Selection Hack' | 'Roll Your Own' + | 'Tailwind' export const AllFeatureNames: FeatureName[] = [ // 'Dragging Reparents By Default', // Removing this option so that we can experiment on this later @@ -34,6 +35,7 @@ export const AllFeatureNames: FeatureName[] = [ 'Condensed Navigator Entries', 'Canvas Fast Selection Hack', 'Roll Your Own', + 'Tailwind', ] let FeatureSwitches: { [feature in FeatureName]: boolean } = { @@ -48,6 +50,7 @@ let FeatureSwitches: { [feature in FeatureName]: boolean } = { 'Project Thumbnail Generation': false, 'Debug - Print UIDs': false, 'Debug – Connections': false, + Tailwind: false, 'Condensed Navigator Entries': !IS_TEST_ENVIRONMENT, 'Canvas Fast Selection Hack': true, 'Roll Your Own': false,