From eefe1b75226b8b94d951561dd7e06061a4619a3c Mon Sep 17 00:00:00 2001 From: snowingfox <1503401882@qq.com> Date: Mon, 25 Jul 2022 15:48:57 +0800 Subject: [PATCH 01/19] feat: transformer-attributify-jsx --- .../transformer-attributify-jsx/README.md | 59 +++++++++++++++++++ .../build.config.ts | 15 +++++ .../transformer-attributify-jsx/package.json | 44 ++++++++++++++ .../transformer-attributify-jsx/src/index.ts | 23 ++++++++ 4 files changed, 141 insertions(+) create mode 100644 packages/transformer-attributify-jsx/README.md create mode 100644 packages/transformer-attributify-jsx/build.config.ts create mode 100644 packages/transformer-attributify-jsx/package.json create mode 100644 packages/transformer-attributify-jsx/src/index.ts diff --git a/packages/transformer-attributify-jsx/README.md b/packages/transformer-attributify-jsx/README.md new file mode 100644 index 0000000000..26b2454fc0 --- /dev/null +++ b/packages/transformer-attributify-jsx/README.md @@ -0,0 +1,59 @@ +# @unocss/transformer-attributify-jsx + + + +happy play with preset-attributify in jsx/tsx + +## Install + +```bash +npm i -D @unocss/transformer-attributify-jsx +``` + +```ts +// uno.config.js +import {defineConfig} from 'unocss' +import transformerAttributifyJsx from "@unocss/transformer-attributify-jsx" +import {presetAttributify} from "unocss/src"; + +export default defineConfig({ + // ... + transformers: [ + transformerAttributifyJsx(), + ], + presets: [ + // ... + presetAttributify() + ] +}) +``` + +## Notice + +> ⚠️ The rules are almost the same as those of `preset-attributify`, but there are several precautions + +```vue +
// cannot end with `%` +
// cannot contain `:` +
// cannot contain `[` or `]` +``` + +# Usage + +```jsx +
+ unocss +
+``` + +Will be compiled to: + +```html +
+ unocss +
+``` + +## License + +MIT License © 2021-PRESENT [Anthony Fu](https://github.com/antfu) diff --git a/packages/transformer-attributify-jsx/build.config.ts b/packages/transformer-attributify-jsx/build.config.ts new file mode 100644 index 0000000000..d20739e914 --- /dev/null +++ b/packages/transformer-attributify-jsx/build.config.ts @@ -0,0 +1,15 @@ +import { defineBuildConfig } from 'unbuild' + +export default defineBuildConfig({ + entries: [ + 'src/index', + ], + clean: true, + declaration: true, + externals: [ + 'magic-string', + ], + rollup: { + emitCJS: true, + }, +}) diff --git a/packages/transformer-attributify-jsx/package.json b/packages/transformer-attributify-jsx/package.json new file mode 100644 index 0000000000..b8e1f85b8d --- /dev/null +++ b/packages/transformer-attributify-jsx/package.json @@ -0,0 +1,44 @@ +{ + "name": "@unocss/transformer-attributify-jsx", + "version": "0.0.1", + "description": "Compile group of classes into one class", + "author": "Anthony Fu ", + "license": "MIT", + "funding": "https://github.com/sponsors/antfu", + "homepage": "https://github.com/unocss/unocss/tree/main/packages/transformer-attributify-jsx#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/unocss/unocss.git", + "directory": "packages/transformer-attributify-jsx" + }, + "bugs": { + "url": "https://github.com/unocss/unocss/issues" + }, + "keywords": [ + "unocss", + "unocss-transformer" + ], + "sideEffects": false, + "exports": { + ".": { + "require": "./dist/index.cjs", + "import": "./dist/index.mjs" + } + }, + "main": "./dist/index.cjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "unbuild", + "stub": "unbuild --stub" + }, + "dependencies": { + "@unocss/core": "workspace:*" + }, + "devDependencies": { + "magic-string": "^0.26.2" + } +} diff --git a/packages/transformer-attributify-jsx/src/index.ts b/packages/transformer-attributify-jsx/src/index.ts new file mode 100644 index 0000000000..e28ce34d19 --- /dev/null +++ b/packages/transformer-attributify-jsx/src/index.ts @@ -0,0 +1,23 @@ +import type { SourceCodeTransformer } from '@unocss/core' + +const elementRE = /|<(\/?)([a-zA-Z][-.:0-9_a-zA-Z]*)((?:\s+[^>]*?(?:(?:'[^']*')|(?:"[^"]*"))?)*)\s*(\/?)>/gs + +export default function transformerAttributifyJsx(): SourceCodeTransformer { + return { + name: 'attributify-jsx', + enforce: 'pre', + transform(code) { + Array.from(code.toString().matchAll(elementRE)).forEach((item) => { + for (const attr of item[3].matchAll(/([a-zA-Z()#][a-zA-Z0-9-_:()#]*)(?:\s*=\s*((?:'[^']*')|(?:"[^"]*")|\S+))?/g)) { + if (!attr[0].includes('=')) { + const matchedRule = attr[0] + const tag = item[2] + const startIdx = (item.index || 0) + (attr.index || 0) + tag.length + 1 + const endIdx = startIdx + matchedRule.length + code.overwrite(startIdx, endIdx, `${matchedRule}=""`) + } + } + }) + }, + } +} From cf59d94be3fe6c1717a93863cb47f2668ca36747 Mon Sep 17 00:00:00 2001 From: snowingfox <1503401882@qq.com> Date: Mon, 25 Jul 2022 15:49:05 +0800 Subject: [PATCH 02/19] test: transformer-attributify-jsx --- test/transformer-attributify-jsx.test.ts | 63 ++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 test/transformer-attributify-jsx.test.ts diff --git a/test/transformer-attributify-jsx.test.ts b/test/transformer-attributify-jsx.test.ts new file mode 100644 index 0000000000..ec7e6c0b61 --- /dev/null +++ b/test/transformer-attributify-jsx.test.ts @@ -0,0 +1,63 @@ +import MagicString from 'magic-string' +import { createGenerator } from '@unocss/core' +import { describe, expect, test } from 'vitest' +import transformerAttributifyJsx from '../packages/transformer-attributify-jsx' + +describe('transformerAttributifyJs', () => { + const originalCode = ` +
+
+
+ unocss +
+
+ The instant on-demand Atomic CSS engine. +
+
+ +
+
+
+
+ on-demand · instant · fully customizable +
+ ` + const code = new MagicString(originalCode) + + const uno = createGenerator({}) + + test('transform', () => { + transformerAttributifyJsx().transform(code, 'foo.js', { uno, tokens: new Set() } as any) + + expect(code.toString()).toMatchInlineSnapshot(` + " +
+
+
+ unocss +
+
+ The instant on-demand Atomic CSS engine. +
+
+ +
+
+
+
+ on-demand · instant · fully customizable +
+ " + `) + }) +}) From 3275b8e390b2e1935c09f70c1337f94f592883e6 Mon Sep 17 00:00:00 2001 From: snowingfox <1503401882@qq.com> Date: Mon, 25 Jul 2022 15:49:54 +0800 Subject: [PATCH 03/19] feat(attributify): better support for jsx --- packages/preset-attributify/src/extractor.ts | 3 +- .../preset-attributify.test.ts.snap | 332 ------------------ test/preset-attributify.test.ts | 104 ++++-- 3 files changed, 75 insertions(+), 364 deletions(-) delete mode 100644 test/__snapshots__/preset-attributify.test.ts.snap diff --git a/packages/preset-attributify/src/extractor.ts b/packages/preset-attributify/src/extractor.ts index f2b1c59d63..7dbd6a27bf 100644 --- a/packages/preset-attributify/src/extractor.ts +++ b/packages/preset-attributify/src/extractor.ts @@ -9,7 +9,8 @@ const strippedPrefixes = [ const splitterRE = /[\s'"`;]+/g const elementRE = /<\w(?=.*>)[\w:\.$-]*\s((?:['"`\{].*?['"`\}]|.*?)*?)>/gs -const valuedAttributeRE = /([?]|(?!\d|-{2}|-\d)[a-zA-Z0-9\u00A0-\uFFFF-_:!%-]+)(?:=(["'])([^\2]*?)\2)?/g +const valuedAttributeRE = /([?]|(?!\d|-{2}|-\d)[a-zA-Z0-9\u00A0-\uFFFF-_:!%-]+)(?:={?(["'])([^\2]*?)\2}?)?/g + export const defaultIgnoreAttributes = ['placeholder'] export const extractorAttributify = (options?: AttributifyOptions): Extractor => { diff --git a/test/__snapshots__/preset-attributify.test.ts.snap b/test/__snapshots__/preset-attributify.test.ts.snap deleted file mode 100644 index 9c1c1c0c8d..0000000000 --- a/test/__snapshots__/preset-attributify.test.ts.snap +++ /dev/null @@ -1,332 +0,0 @@ -// Vitest Snapshot v1 - -exports[`attributify > compatible with full controlled rules 1`] = ` -"/* layer: default */ - - .custom-2 { - font-size: 2px; - } - /* you can have multiple rules */ - .custom-2::after { - content: 'after'; - } - .foo > .custom-2 { - color: red; - } - /* or media queries */ - @media (min-width: 680px) { - .custom-2 { - font-size: 16px; - } - } - - - [custom~=\\"\\\\31 \\"] { - font-size: 1px; - } - /* you can have multiple rules */ - [custom~=\\"\\\\31 \\"]::after { - content: 'after'; - } - .foo > [custom~=\\"\\\\31 \\"] { - color: red; - } - /* or media queries */ - @media (min-width: 680px) { - [custom~=\\"\\\\31 \\"] { - font-size: 16px; - } - } - " -`; - -exports[`attributify > extractor1 1`] = ` -Set { - "[uno-layer-base~=\\"c-white/10\\"]", - "[uno-layer-base~=\\"hover:c-black/20\\"]", - "[sm~=\\"[color:red]\\"]", - "[md~=\\"[--var:var(--another)]\\"]", - "[lg~=\\"bg-blue-600\\"]", - "absolute", - "fixed", - "[important~=\\"text-red\\"]", - "[important~=\\"bg-red\\"]", - "[bg~=\\"blue-400\\"]", - "[bg~=\\"hover:blue-500\\"]", - "[bg~=\\"dark:!blue-500\\"]", - "[bg~=\\"dark:hover:blue-600\\"]", - "[text~=\\"sm\\"]", - "[text~=\\"white\\"]", - "[!leading-4=\\"\\"]", - "[flex~=\\"!~\\"]", - "[flex~=\\"col\\"]", - "[p~=\\"t-2\\"]", - "[pt~=\\"2\\"]", - "[border~=\\"rounded-xl\\"]", - "[border~=\\"x-1\\"]", - "[border~=\\"x-style-dashed\\"]", - "[font~=\\"foo\\"]", - "[font~=\\">\\"]", - "[font~=\\"bar\\"]", - "[font~=\\"?\\"]", - "[font~=\\"mono\\"]", - "[font~=\\":\\"]", - "[font~=\\"sans\\"]", - "[p~=\\"y-2\\"]", - "[p~=\\"x-4\\"]", - "[border~=\\"2\\"]", - "[border~=\\"rounded\\"]", - "[border~=\\"blue-200\\"]", - "[un-children~=\\"m-auto\\"]", - "[pt2=\\"\\"]", - "[rounded-sm=\\"\\"]", - "[inline-block=\\"\\"]", - "[transform=\\"\\"]", - "[translate-x-100%=\\"\\"]", - "[translate-y-=\\"\\"]", - "[%=\\"\\"]", - "[rotate-30=\\"\\"]", - "[after~=\\"content-[unocss]\\"]", - "[rotate-60=\\"\\"]", - "[ma=\\"\\"]", - "[m~=\\"1\\"]", - "[m~=\\"2\\"]", - "[m~=\\"3\\"]", -} -`; - -exports[`attributify > extractor2 1`] = ` -Set { - "[h-80=\\"\\"]", - "[text-center=\\"\\"]", - "[flex=\\"\\"]", - "[flex-col=\\"\\"]", - "[align-center=\\"\\"]", - "[select-none=\\"\\"]", - "[all:transition-400=\\"\\"]", - "[type~=\\"checkbox\\"]", - "[peer=\\"\\"]", - "[mt-a=\\"\\"]", - "[mb-a=\\"\\"]", - "[group=\\"\\"]", - "[peer-checked~=\\"text-4xl\\"]", - "[font-100=\\"\\"]", - "[text-4xl=\\"\\"]", - "[mb--3=\\"\\"]", - "[p-10=\\"\\"]", - "[bg-gradient~=\\"to-r\\"]", - "[bg-gradient~=\\"from-yellow-400\\"]", - "[bg-gradient~=\\"via-red-500\\"]", - "[bg-gradient~=\\"to-pink-500\\"]", - "[text-5xl=\\"\\"]", - "[sm~=\\"bg-blue-600\\"]", - "[op-20=\\"\\"]", - "[font-200=\\"\\"]", - "[mt-1=\\"\\"]", - "[tracking-wider=\\"\\"]", - "[group-hover~=\\"text-teal-400\\"]", - "[group-hover~=\\"op-50\\"]", - "[ma=\\"\\"]", - "[inline-flex=\\"\\"]", - "[relative=\\"\\"]", - "[h-14=\\"\\"]", - "[type~=\\"text\\"]", - "[m-0=\\"\\"]", - "[pt-4=\\"\\"]", - "[px-4=\\"\\"]", - "[text-true-gray-800=\\"\\"]", - "[un-placeholder~=\\"text-red\\"]", - "[absolute=\\"\\"]", - "[leading-1rem=\\"\\"]", - "[left-4=\\"\\"]", - "[pointer-events-none=\\"\\"]", - "[text-gray-7=\\"\\"]", - "[top~=\\"1/3\\"]", - "[transition~=\\"200\\"]", - "[transition~=\\"linear\\"]", - "[peer-not-placeholder-shown~=\\"-translate-y-4\\"]", - "[peer-not-placeholder-shown~=\\"scale-75\\"]", - "[peer-not-placeholder-shown~=\\"origin-top-left\\"]", - "[peer-not-placeholder-shown~=\\"text-green-500\\"]", - "[peer-focus~=\\"-translate-y-4\\"]", - "[peer-focus~=\\"scale-75\\"]", - "[peer-focus~=\\"origin-top-left\\"]", - "[peer-focus~=\\"text-green-500\\"]", - "[before~=\\"content-!\\"]", - "[after~=\\"content-[!]\\"]", -} -`; - -exports[`attributify > fixture1 1`] = ` -"/* layer: base */ -[uno-layer-base~=\\"c-white\\\\/10\\"]{color:rgba(255,255,255,0.1);} -[uno-layer-base~=\\"hover\\\\:c-black\\\\/20\\"]:hover{color:rgba(0,0,0,0.2);} -/* layer: default */ -.absolute{position:absolute;} -.fixed{position:fixed;} -[m~=\\"\\\\31 \\"]{margin:0.25rem;} -[m~=\\"\\\\32 \\"]{margin:0.5rem;} -[m~=\\"\\\\33 \\"]{margin:0.75rem;} -[ma=\\"\\"], -[un-children~=\\"m-auto\\"]>*{margin:auto;} -[inline-block=\\"\\"]{display:inline-block;} -[flex~=\\"\\\\!\\\\~\\"]{display:flex !important;} -[flex~=\\"col\\"]{flex-direction:column;} -[translate-x-100\\\\%=\\"\\"]{--un-translate-x:100%;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} -[rotate-30=\\"\\"]{--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-rotate:30deg;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} -[rotate-60=\\"\\"]{--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-rotate:60deg;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} -[transform=\\"\\"]{transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} -[border~=\\"\\\\32 \\"]{border-width:2px;border-style:solid;} -[border~=\\"x-1\\"]{border-left-width:1px;border-right-width:1px;border-left-style:solid;border-right-style:solid;} -[border~=\\"blue-200\\"]{--un-border-opacity:1;border-color:rgba(191,219,254,var(--un-border-opacity));} -[border~=\\"rounded-xl\\"]{border-radius:0.75rem;} -[border~=\\"rounded\\"]{border-radius:0.25rem;} -[rounded-sm=\\"\\"]{border-radius:0.125rem;} -[border~=\\"x-style-dashed\\"]{border-left-style:dashed;border-right-style:dashed;} -.dark [bg~=\\"dark\\\\:\\\\!blue-500\\"]{--un-bg-opacity:1 !important;background-color:rgba(59,130,246,var(--un-bg-opacity)) !important;} -.dark [bg~=\\"dark\\\\:hover\\\\:blue-600\\"]:hover{--un-bg-opacity:1;background-color:rgba(37,99,235,var(--un-bg-opacity));} -[bg~=\\"blue-400\\"]{--un-bg-opacity:1;background-color:rgba(96,165,250,var(--un-bg-opacity));} -[bg~=\\"hover\\\\:blue-500\\"]:hover{--un-bg-opacity:1;background-color:rgba(59,130,246,var(--un-bg-opacity));} -[important~=\\"bg-red\\"]{--un-bg-opacity:1 !important;background-color:rgba(248,113,113,var(--un-bg-opacity)) !important;} -[p~=\\"x-4\\"]{padding-left:1rem;padding-right:1rem;} -[p~=\\"y-2\\"]{padding-top:0.5rem;padding-bottom:0.5rem;} -[p~=\\"t-2\\"], -[pt~=\\"\\\\32 \\"], -[pt2=\\"\\"]{padding-top:0.5rem;} -[font~=\\"mono\\"]{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\\"Liberation Mono\\",\\"Courier New\\",monospace;} -[font~=\\"sans\\"]{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,\\"Segoe UI\\",Roboto,\\"Helvetica Neue\\",Arial,\\"Noto Sans\\",sans-serif,\\"Apple Color Emoji\\",\\"Segoe UI Emoji\\",\\"Segoe UI Symbol\\",\\"Noto Color Emoji\\";} -[text~=\\"sm\\"]{font-size:0.875rem;line-height:1.25rem;} -[\\\\!leading-4=\\"\\"]{line-height:1rem !important;} -[important~=\\"text-red\\"]{--un-text-opacity:1 !important;color:rgba(248,113,113,var(--un-text-opacity)) !important;} -[text~=\\"white\\"]{--un-text-opacity:1;color:rgba(255,255,255,var(--un-text-opacity));} -[after~=\\"content-\\\\[unocss\\\\]\\"]::after{content:\\"unocss\\";} -@media (min-width: 640px){ -[sm~=\\"\\\\[color\\\\:red\\\\]\\"]{color:red;} -} -@media (min-width: 768px){ -[md~=\\"\\\\[--var\\\\:var\\\\(--another\\\\)\\\\]\\"]{--var:var(--another);} -} -@media (min-width: 1024px){ -[lg~=\\"bg-blue-600\\"]{--un-bg-opacity:1;background-color:rgba(37,99,235,var(--un-bg-opacity));} -}" -`; - -exports[`attributify > fixture2 1`] = ` -"/* layer: default */ -[pointer-events-none=\\"\\"]{pointer-events:none;} -[absolute=\\"\\"]{position:absolute;} -[relative=\\"\\"]{position:relative;} -[left-4=\\"\\"]{left:1rem;} -[top~=\\"\\\\31 \\\\/3\\"]{top:33.3333333333%;} -[m-0=\\"\\"]{margin:0rem;} -[ma=\\"\\"]{margin:auto;} -[mb--3=\\"\\"]{margin-bottom:-0.75rem;} -[mb-a=\\"\\"]{margin-bottom:auto;} -[mt-1=\\"\\"]{margin-top:0.25rem;} -[mt-a=\\"\\"]{margin-top:auto;} -[h-14=\\"\\"]{height:3.5rem;} -[h-80=\\"\\"]{height:20rem;} -[flex=\\"\\"]{display:flex;} -[inline-flex=\\"\\"]{display:inline-flex;} -[flex-col=\\"\\"]{flex-direction:column;} -[peer=\\"\\"]:focus~[peer-focus~=\\"origin-top-left\\"], -[peer=\\"\\"]:not(:placeholder-shown)~[peer-not-placeholder-shown~=\\"origin-top-left\\"]{transform-origin:top left;} -[peer=\\"\\"]:focus~[peer-focus~=\\"-translate-y-4\\"], -[peer=\\"\\"]:not(:placeholder-shown)~[peer-not-placeholder-shown~=\\"-translate-y-4\\"]{--un-translate-y:-1rem;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} -[peer=\\"\\"]:focus~[peer-focus~=\\"scale-75\\"], -[peer=\\"\\"]:not(:placeholder-shown)~[peer-not-placeholder-shown~=\\"scale-75\\"]{--un-scale-x:0.75;--un-scale-y:0.75;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} -[select-none=\\"\\"]{user-select:none;} -[bg-gradient~=\\"from-yellow-400\\"]{--un-gradient-from:rgba(250,204,21,var(--un-from-opacity, 1));--un-gradient-to:rgba(250,204,21,0);--un-gradient-stops:var(--un-gradient-from), var(--un-gradient-to);} -[bg-gradient~=\\"via-red-500\\"]{--un-gradient-to:rgba(239,68,68,0);--un-gradient-stops:var(--un-gradient-from), rgba(239,68,68,var(--un-via-opacity, 1)), var(--un-gradient-to);} -[bg-gradient~=\\"to-pink-500\\"]{--un-gradient-to:rgba(236,72,153,var(--un-to-opacity, 1));} -[bg-gradient~=\\"to-r\\"]{--un-gradient-shape:to right;--un-gradient:var(--un-gradient-shape), var(--un-gradient-stops);background-image:linear-gradient(var(--un-gradient));} -[p-10=\\"\\"]{padding:2.5rem;} -[px-4=\\"\\"]{padding-left:1rem;padding-right:1rem;} -[pt-4=\\"\\"]{padding-top:1rem;} -[text-center=\\"\\"]{text-align:center;} -[peer=\\"\\"]:checked~[peer-checked~=\\"text-4xl\\"], -[text-4xl=\\"\\"]{font-size:2.25rem;line-height:2.5rem;} -[text-5xl=\\"\\"]{font-size:3rem;line-height:1;} -[font-100=\\"\\"]{font-weight:100;} -[font-200=\\"\\"]{font-weight:200;} -[leading-1rem=\\"\\"]{line-height:1rem;} -[tracking-wider=\\"\\"]{letter-spacing:0.05em;} -[group=\\"\\"]:hover [group-hover~=\\"text-teal-400\\"]{--un-text-opacity:1;color:rgba(45,212,191,var(--un-text-opacity));} -[peer=\\"\\"]:focus~[peer-focus~=\\"text-green-500\\"], -[peer=\\"\\"]:not(:placeholder-shown)~[peer-not-placeholder-shown~=\\"text-green-500\\"]{--un-text-opacity:1;color:rgba(34,197,94,var(--un-text-opacity));} -[text-gray-7=\\"\\"]{--un-text-opacity:1;color:rgba(55,65,81,var(--un-text-opacity));} -[text-true-gray-800=\\"\\"]{--un-text-opacity:1;color:rgba(38,38,38,var(--un-text-opacity));} -[un-placeholder~=\\"text-red\\"]::placeholder{--un-text-opacity:1;color:rgba(248,113,113,var(--un-text-opacity));} -[group=\\"\\"]:hover [group-hover~=\\"op-50\\"]{opacity:0.5;} -[op-20=\\"\\"]{opacity:0.2;} -[all\\\\:transition-400=\\"\\"] *{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:400ms;} -[transition~=\\"\\\\32 00\\"]{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:200ms;} -[after~=\\"content-\\\\[\\\\!\\\\]\\"]::after{content:\\"!\\";} -@media (min-width: 640px){ -[sm~=\\"bg-blue-600\\"]{--un-bg-opacity:1;background-color:rgba(37,99,235,var(--un-bg-opacity));} -}" -`; - -exports[`attributify > variant 1`] = ` -[ - "uno-layer-base-c-white/10", - "hover:uno-layer-base-c-black/20", - "sm-[color:red]", - "md-[--var:var(--another)]", - "lg-bg-blue-600", - undefined, - undefined, - "important-text-red", - "important-bg-red", - "bg-blue-400", - "hover:bg-blue-500", - "dark:!bg-blue-500", - "dark:hover:bg-blue-600", - "text-sm", - "text-white", - "!leading-4", - "!flex", - "flex-col", - "p-t-2", - "pt-2", - "border-rounded-xl", - "border-x-1", - "border-x-style-dashed", - "font-foo", - "font->", - "font-bar", - "font-?", - "font-mono", - "font-:", - "font-sans", - "p-y-2", - "p-x-4", - "border-2", - "border-rounded", - "border-blue-200", - "children-m-auto", - "pt2", - "rounded-sm", - "inline-block", - "transform", - "translate-x-100%", - "translate-y-", - "%", - "rotate-30", - "after-content-[unocss]", - "rotate-60", - "ma", - "m-1", - "m-2", - "m-3", -] -`; - -exports[`attributify > with trueToNonValued 1`] = ` -"/* layer: default */ -.grid, -[grid~=\\"\\\\~\\"]{display:grid;} -[grid~=\\"cols-2\\"]{grid-template-columns:repeat(2,minmax(0,1fr));} -.flex, -[flex~=\\"true\\"]{display:flex;}" -`; diff --git a/test/preset-attributify.test.ts b/test/preset-attributify.test.ts index 82da67b84f..8c3f121562 100644 --- a/test/preset-attributify.test.ts +++ b/test/preset-attributify.test.ts @@ -78,6 +78,39 @@ describe('attributify', () => {
` + // jsx + const fixture4 = ` + + ` + const uno = createGenerator({ presets: [ presetAttributify({ strict: true }), @@ -117,6 +150,10 @@ describe('attributify', () => { expect(await uno.applyExtractors(fixture2)).toMatchSnapshot() }) + test('extractor4', async () => { + expect(await uno.applyExtractors(fixture4)).toMatchSnapshot() + }) + test('variant', async () => { const variant = variantAttributify({ prefix: 'un-', @@ -142,25 +179,29 @@ describe('attributify', () => { expect(css).toMatchSnapshot() }) - test('autocomplete extractor', async () => { - const res = await autocompleteExtractorAttributify.extract({ - content: fixture1, - cursor: 187, - }) + test('fixture4', async () => { + const { css } = await uno.generate(fixture4, { preflights: false }) + expect(css).toMatchSnapshot() + + test('autocomplete extractor', async () => { + const res = await autocompleteExtractorAttributify.extract({ + content: fixture1, + cursor: 187, + }) - expect(res).not.toBeNull() + expect(res).not.toBeNull() - expect(res!.extracted).toMatchInlineSnapshot('"bg-blue-400"') - expect(res!.transformSuggestions!([`${res!.extracted}1`, `${res!.extracted}2`])) - .toMatchInlineSnapshot(` + expect(res!.extracted).toMatchInlineSnapshot('"bg-blue-400"') + expect(res!.transformSuggestions!([`${res!.extracted}1`, `${res!.extracted}2`])) + .toMatchInlineSnapshot(` [ "blue-4001", "blue-4002", ] `) - const reversed = res!.resolveReplacement(`${res!.extracted}1`) - expect(reversed).toMatchInlineSnapshot(` + const reversed = res!.resolveReplacement(`${res!.extracted}1`) + expect(reversed).toMatchInlineSnapshot(` { "end": 193, "replacement": "blue-4001", @@ -168,30 +209,31 @@ describe('attributify', () => { } `) - expect(fixture1.slice(reversed.start, reversed.end)) - .toMatchInlineSnapshot('"blue-400"') - }) - - test('compatible with full controlled rules', async () => { - const { css } = await uno.generate(fixture3, { preflights: false }) - expect(css).toMatchSnapshot() - }) + expect(fixture1.slice(reversed.start, reversed.end)) + .toMatchInlineSnapshot('"blue-400"') + }) - test('with trueToNonValued', async () => { - const uno = createGenerator({ - presets: [ - presetAttributify({ trueToNonValued: true }), - presetUno(), - ], + test('compatible with full controlled rules', async () => { + const { css } = await uno.generate(fixture3, { preflights: false }) + expect(css).toMatchSnapshot() }) - const { css } = await uno.generate(` + + test('with trueToNonValued', async () => { + const uno = createGenerator({ + presets: [ + presetAttributify({ trueToNonValued: true }), + presetUno(), + ], + }) + const { css } = await uno.generate(`
`, { preflights: false }) - expect(css).toMatchSnapshot() - }) + expect(css).toMatchSnapshot() + }) - test('with incomplete element', async () => { - await uno.generate('
{ + await uno.generate('
Date: Mon, 25 Jul 2022 15:55:47 +0800 Subject: [PATCH 04/19] chore: update desc --- packages/transformer-attributify-jsx/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/transformer-attributify-jsx/package.json b/packages/transformer-attributify-jsx/package.json index b8e1f85b8d..c27c1ec68b 100644 --- a/packages/transformer-attributify-jsx/package.json +++ b/packages/transformer-attributify-jsx/package.json @@ -1,7 +1,7 @@ { "name": "@unocss/transformer-attributify-jsx", "version": "0.0.1", - "description": "Compile group of classes into one class", + "description": "Better JSX attribute handling for preset-attributify", "author": "Anthony Fu ", "license": "MIT", "funding": "https://github.com/sponsors/antfu", From 20f825f5146e1790a93b50e615d140508107af80 Mon Sep 17 00:00:00 2001 From: snowingfox <1503401882@qq.com> Date: Mon, 25 Jul 2022 15:59:53 +0800 Subject: [PATCH 05/19] chore: commit snapshot --- .../preset-attributify.test.ts.snap | 375 ++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 test/__snapshots__/preset-attributify.test.ts.snap diff --git a/test/__snapshots__/preset-attributify.test.ts.snap b/test/__snapshots__/preset-attributify.test.ts.snap new file mode 100644 index 0000000000..0cb2b5c502 --- /dev/null +++ b/test/__snapshots__/preset-attributify.test.ts.snap @@ -0,0 +1,375 @@ +// Vitest Snapshot v1 + +exports[`attributify > extractor1 1`] = ` +Set { + "[uno-layer-base~=\\"c-white/10\\"]", + "[uno-layer-base~=\\"hover:c-black/20\\"]", + "[sm~=\\"[color:red]\\"]", + "[md~=\\"[--var:var(--another)]\\"]", + "[lg~=\\"bg-blue-600\\"]", + "absolute", + "fixed", + "[important~=\\"text-red\\"]", + "[important~=\\"bg-red\\"]", + "[bg~=\\"blue-400\\"]", + "[bg~=\\"hover:blue-500\\"]", + "[bg~=\\"dark:!blue-500\\"]", + "[bg~=\\"dark:hover:blue-600\\"]", + "[text~=\\"sm\\"]", + "[text~=\\"white\\"]", + "[!leading-4=\\"\\"]", + "[flex~=\\"!~\\"]", + "[flex~=\\"col\\"]", + "[p~=\\"t-2\\"]", + "[pt~=\\"2\\"]", + "[border~=\\"rounded-xl\\"]", + "[border~=\\"x-1\\"]", + "[border~=\\"x-style-dashed\\"]", + "[font~=\\"foo\\"]", + "[font~=\\">\\"]", + "[font~=\\"bar\\"]", + "[font~=\\"?\\"]", + "[font~=\\"mono\\"]", + "[font~=\\":\\"]", + "[font~=\\"sans\\"]", + "[p~=\\"y-2\\"]", + "[p~=\\"x-4\\"]", + "[border~=\\"2\\"]", + "[border~=\\"rounded\\"]", + "[border~=\\"blue-200\\"]", + "[un-children~=\\"m-auto\\"]", + "[pt2=\\"\\"]", + "[rounded-sm=\\"\\"]", + "[inline-block=\\"\\"]", + "[transform=\\"\\"]", + "[translate-x-100%=\\"\\"]", + "[translate-y-=\\"\\"]", + "[%=\\"\\"]", + "[rotate-30=\\"\\"]", + "[after~=\\"content-[unocss]\\"]", + "[rotate-60=\\"\\"]", + "[ma=\\"\\"]", + "[m~=\\"1\\"]", + "[m~=\\"2\\"]", + "[m~=\\"3\\"]", +} +`; + +exports[`attributify > extractor2 1`] = ` +Set { + "[h-80=\\"\\"]", + "[text-center=\\"\\"]", + "[flex=\\"\\"]", + "[flex-col=\\"\\"]", + "[align-center=\\"\\"]", + "[select-none=\\"\\"]", + "[all:transition-400=\\"\\"]", + "[type~=\\"checkbox\\"]", + "[peer=\\"\\"]", + "[mt-a=\\"\\"]", + "[mb-a=\\"\\"]", + "[group=\\"\\"]", + "[peer-checked~=\\"text-4xl\\"]", + "[font-100=\\"\\"]", + "[text-4xl=\\"\\"]", + "[mb--3=\\"\\"]", + "[p-10=\\"\\"]", + "[bg-gradient~=\\"to-r\\"]", + "[bg-gradient~=\\"from-yellow-400\\"]", + "[bg-gradient~=\\"via-red-500\\"]", + "[bg-gradient~=\\"to-pink-500\\"]", + "[text-5xl=\\"\\"]", + "[sm~=\\"bg-blue-600\\"]", + "[op-20=\\"\\"]", + "[font-200=\\"\\"]", + "[mt-1=\\"\\"]", + "[tracking-wider=\\"\\"]", + "[group-hover~=\\"text-teal-400\\"]", + "[group-hover~=\\"op-50\\"]", + "[ma=\\"\\"]", + "[inline-flex=\\"\\"]", + "[relative=\\"\\"]", + "[h-14=\\"\\"]", + "[type~=\\"text\\"]", + "[m-0=\\"\\"]", + "[pt-4=\\"\\"]", + "[px-4=\\"\\"]", + "[text-true-gray-800=\\"\\"]", + "[un-placeholder~=\\"text-red\\"]", + "[absolute=\\"\\"]", + "[leading-1rem=\\"\\"]", + "[left-4=\\"\\"]", + "[pointer-events-none=\\"\\"]", + "[text-gray-7=\\"\\"]", + "[top~=\\"1/3\\"]", + "[transition~=\\"200\\"]", + "[transition~=\\"linear\\"]", + "[peer-not-placeholder-shown~=\\"-translate-y-4\\"]", + "[peer-not-placeholder-shown~=\\"scale-75\\"]", + "[peer-not-placeholder-shown~=\\"origin-top-left\\"]", + "[peer-not-placeholder-shown~=\\"text-green-500\\"]", + "[peer-focus~=\\"-translate-y-4\\"]", + "[peer-focus~=\\"scale-75\\"]", + "[peer-focus~=\\"origin-top-left\\"]", + "[peer-focus~=\\"text-green-500\\"]", + "[before~=\\"content-!\\"]", + "[after~=\\"content-[!]\\"]", +} +`; + +exports[`attributify > extractor4 1`] = ` +Set { + "[uno-layer-base~=\\"c-white/10\\"]", + "[uno-layer-base~=\\"hover:c-black/20\\"]", + "[sm~=\\"[color:red]\\"]", + "[md~=\\"[--var:var(--another)]\\"]", + "[lg~=\\"bg-blue-600\\"]", + "absolute", + "fixed", + "[important~=\\"text-red\\"]", + "[important~=\\"bg-red\\"]", + "[bg~=\\"blue-400\\"]", + "[bg~=\\"hover:blue-500\\"]", + "[bg~=\\"dark:!blue-500\\"]", + "[bg~=\\"dark:hover:blue-600\\"]", + "[text~=\\"sm\\"]", + "[text~=\\"white\\"]", + "[flex~=\\"!~\\"]", + "[flex~=\\"col\\"]", + "[p~=\\"t-2\\"]", + "[pt~=\\"2\\"]", + "[border~=\\"rounded-xl\\"]", + "[border~=\\"x-1\\"]", + "[border~=\\"x-style-dashed\\"]", + "[border~=\\"2\\"]", + "[border~=\\"rounded\\"]", + "[border~=\\"blue-200\\"]", + "[un-children~=\\"m-auto\\"]", + "[pt2=\\"\\"]", + "[rounded-sm=\\"\\"]", + "[inline-block=\\"\\"]", + "[transform=\\"\\"]", + "[translate-x-100=\\"\\"]", + "[rotate-30=\\"\\"]", + "[after~=\\"content-[unocss]\\"]", + "[rotate-60=\\"\\"]", + "[ma=\\"\\"]", + "[m~=\\"1\\"]", + "[m~=\\"2\\"]", + "[m~=\\"3\\"]", +} +`; + +exports[`attributify > fixture1 1`] = ` +"/* layer: base */ +[uno-layer-base~=\\"c-white\\\\/10\\"]{color:rgba(255,255,255,0.1);} +[uno-layer-base~=\\"hover\\\\:c-black\\\\/20\\"]:hover{color:rgba(0,0,0,0.2);} +/* layer: default */ +.absolute{position:absolute;} +.fixed{position:fixed;} +[m~=\\"\\\\31 \\"]{margin:0.25rem;} +[m~=\\"\\\\32 \\"]{margin:0.5rem;} +[m~=\\"\\\\33 \\"]{margin:0.75rem;} +[ma=\\"\\"], +[un-children~=\\"m-auto\\"]>*{margin:auto;} +[inline-block=\\"\\"]{display:inline-block;} +[flex~=\\"\\\\!\\\\~\\"]{display:flex !important;} +[flex~=\\"col\\"]{flex-direction:column;} +[translate-x-100\\\\%=\\"\\"]{--un-translate-x:100%;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} +[rotate-30=\\"\\"]{--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-rotate:30deg;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} +[rotate-60=\\"\\"]{--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-rotate:60deg;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} +[transform=\\"\\"]{transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} +[border~=\\"\\\\32 \\"]{border-width:2px;border-style:solid;} +[border~=\\"x-1\\"]{border-left-width:1px;border-right-width:1px;border-left-style:solid;border-right-style:solid;} +[border~=\\"blue-200\\"]{--un-border-opacity:1;border-color:rgba(191,219,254,var(--un-border-opacity));} +[border~=\\"rounded-xl\\"]{border-radius:0.75rem;} +[border~=\\"rounded\\"]{border-radius:0.25rem;} +[rounded-sm=\\"\\"]{border-radius:0.125rem;} +[border~=\\"x-style-dashed\\"]{border-left-style:dashed;border-right-style:dashed;} +.dark [bg~=\\"dark\\\\:\\\\!blue-500\\"]{--un-bg-opacity:1 !important;background-color:rgba(59,130,246,var(--un-bg-opacity)) !important;} +.dark [bg~=\\"dark\\\\:hover\\\\:blue-600\\"]:hover{--un-bg-opacity:1;background-color:rgba(37,99,235,var(--un-bg-opacity));} +[bg~=\\"blue-400\\"]{--un-bg-opacity:1;background-color:rgba(96,165,250,var(--un-bg-opacity));} +[bg~=\\"hover\\\\:blue-500\\"]:hover{--un-bg-opacity:1;background-color:rgba(59,130,246,var(--un-bg-opacity));} +[important~=\\"bg-red\\"]{--un-bg-opacity:1 !important;background-color:rgba(248,113,113,var(--un-bg-opacity)) !important;} +[p~=\\"x-4\\"]{padding-left:1rem;padding-right:1rem;} +[p~=\\"y-2\\"]{padding-top:0.5rem;padding-bottom:0.5rem;} +[p~=\\"t-2\\"], +[pt~=\\"\\\\32 \\"], +[pt2=\\"\\"]{padding-top:0.5rem;} +[font~=\\"mono\\"]{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\\"Liberation Mono\\",\\"Courier New\\",monospace;} +[font~=\\"sans\\"]{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,\\"Segoe UI\\",Roboto,\\"Helvetica Neue\\",Arial,\\"Noto Sans\\",sans-serif,\\"Apple Color Emoji\\",\\"Segoe UI Emoji\\",\\"Segoe UI Symbol\\",\\"Noto Color Emoji\\";} +[text~=\\"sm\\"]{font-size:0.875rem;line-height:1.25rem;} +[\\\\!leading-4=\\"\\"]{line-height:1rem !important;} +[important~=\\"text-red\\"]{--un-text-opacity:1 !important;color:rgba(248,113,113,var(--un-text-opacity)) !important;} +[text~=\\"white\\"]{--un-text-opacity:1;color:rgba(255,255,255,var(--un-text-opacity));} +[after~=\\"content-\\\\[unocss\\\\]\\"]::after{content:\\"unocss\\";} +@media (min-width: 640px){ +[sm~=\\"\\\\[color\\\\:red\\\\]\\"]{color:red;} +} +@media (min-width: 768px){ +[md~=\\"\\\\[--var\\\\:var\\\\(--another\\\\)\\\\]\\"]{--var:var(--another);} +} +@media (min-width: 1024px){ +[lg~=\\"bg-blue-600\\"]{--un-bg-opacity:1;background-color:rgba(37,99,235,var(--un-bg-opacity));} +}" +`; + +exports[`attributify > fixture2 1`] = ` +"/* layer: default */ +[pointer-events-none=\\"\\"]{pointer-events:none;} +[absolute=\\"\\"]{position:absolute;} +[relative=\\"\\"]{position:relative;} +[left-4=\\"\\"]{left:1rem;} +[top~=\\"\\\\31 \\\\/3\\"]{top:33.3333333333%;} +[m-0=\\"\\"]{margin:0rem;} +[ma=\\"\\"]{margin:auto;} +[mb--3=\\"\\"]{margin-bottom:-0.75rem;} +[mb-a=\\"\\"]{margin-bottom:auto;} +[mt-1=\\"\\"]{margin-top:0.25rem;} +[mt-a=\\"\\"]{margin-top:auto;} +[h-14=\\"\\"]{height:3.5rem;} +[h-80=\\"\\"]{height:20rem;} +[flex=\\"\\"]{display:flex;} +[inline-flex=\\"\\"]{display:inline-flex;} +[flex-col=\\"\\"]{flex-direction:column;} +[peer=\\"\\"]:focus~[peer-focus~=\\"origin-top-left\\"], +[peer=\\"\\"]:not(:placeholder-shown)~[peer-not-placeholder-shown~=\\"origin-top-left\\"]{transform-origin:top left;} +[peer=\\"\\"]:focus~[peer-focus~=\\"-translate-y-4\\"], +[peer=\\"\\"]:not(:placeholder-shown)~[peer-not-placeholder-shown~=\\"-translate-y-4\\"]{--un-translate-y:-1rem;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} +[peer=\\"\\"]:focus~[peer-focus~=\\"scale-75\\"], +[peer=\\"\\"]:not(:placeholder-shown)~[peer-not-placeholder-shown~=\\"scale-75\\"]{--un-scale-x:0.75;--un-scale-y:0.75;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} +[select-none=\\"\\"]{user-select:none;} +[bg-gradient~=\\"from-yellow-400\\"]{--un-gradient-from:rgba(250,204,21,var(--un-from-opacity, 1));--un-gradient-to:rgba(250,204,21,0);--un-gradient-stops:var(--un-gradient-from), var(--un-gradient-to);} +[bg-gradient~=\\"via-red-500\\"]{--un-gradient-to:rgba(239,68,68,0);--un-gradient-stops:var(--un-gradient-from), rgba(239,68,68,var(--un-via-opacity, 1)), var(--un-gradient-to);} +[bg-gradient~=\\"to-pink-500\\"]{--un-gradient-to:rgba(236,72,153,var(--un-to-opacity, 1));} +[bg-gradient~=\\"to-r\\"]{--un-gradient-shape:to right;--un-gradient:var(--un-gradient-shape), var(--un-gradient-stops);background-image:linear-gradient(var(--un-gradient));} +[p-10=\\"\\"]{padding:2.5rem;} +[px-4=\\"\\"]{padding-left:1rem;padding-right:1rem;} +[pt-4=\\"\\"]{padding-top:1rem;} +[text-center=\\"\\"]{text-align:center;} +[peer=\\"\\"]:checked~[peer-checked~=\\"text-4xl\\"], +[text-4xl=\\"\\"]{font-size:2.25rem;line-height:2.5rem;} +[text-5xl=\\"\\"]{font-size:3rem;line-height:1;} +[font-100=\\"\\"]{font-weight:100;} +[font-200=\\"\\"]{font-weight:200;} +[leading-1rem=\\"\\"]{line-height:1rem;} +[tracking-wider=\\"\\"]{letter-spacing:0.05em;} +[group=\\"\\"]:hover [group-hover~=\\"text-teal-400\\"]{--un-text-opacity:1;color:rgba(45,212,191,var(--un-text-opacity));} +[peer=\\"\\"]:focus~[peer-focus~=\\"text-green-500\\"], +[peer=\\"\\"]:not(:placeholder-shown)~[peer-not-placeholder-shown~=\\"text-green-500\\"]{--un-text-opacity:1;color:rgba(34,197,94,var(--un-text-opacity));} +[text-gray-7=\\"\\"]{--un-text-opacity:1;color:rgba(55,65,81,var(--un-text-opacity));} +[text-true-gray-800=\\"\\"]{--un-text-opacity:1;color:rgba(38,38,38,var(--un-text-opacity));} +[un-placeholder~=\\"text-red\\"]::placeholder{--un-text-opacity:1;color:rgba(248,113,113,var(--un-text-opacity));} +[group=\\"\\"]:hover [group-hover~=\\"op-50\\"]{opacity:0.5;} +[op-20=\\"\\"]{opacity:0.2;} +[all\\\\:transition-400=\\"\\"] *{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:400ms;} +[transition~=\\"\\\\32 00\\"]{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:200ms;} +[after~=\\"content-\\\\[\\\\!\\\\]\\"]::after{content:\\"!\\";} +@media (min-width: 640px){ +[sm~=\\"bg-blue-600\\"]{--un-bg-opacity:1;background-color:rgba(37,99,235,var(--un-bg-opacity));} +}" +`; + +exports[`attributify > fixture4 1`] = ` +"/* layer: base */ +[uno-layer-base~=\\"c-white\\\\/10\\"]{color:rgba(255,255,255,0.1);} +[uno-layer-base~=\\"hover\\\\:c-black\\\\/20\\"]:hover{color:rgba(0,0,0,0.2);} +/* layer: default */ +.absolute{position:absolute;} +.fixed{position:fixed;} +[m~=\\"\\\\31 \\"]{margin:0.25rem;} +[m~=\\"\\\\32 \\"]{margin:0.5rem;} +[m~=\\"\\\\33 \\"]{margin:0.75rem;} +[ma=\\"\\"], +[un-children~=\\"m-auto\\"]>*{margin:auto;} +[inline-block=\\"\\"]{display:inline-block;} +[flex~=\\"\\\\!\\\\~\\"]{display:flex !important;} +[flex~=\\"col\\"]{flex-direction:column;} +[translate-x-100=\\"\\"]{--un-translate-x:25rem;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} +[rotate-30=\\"\\"]{--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-rotate:30deg;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} +[rotate-60=\\"\\"]{--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-rotate:60deg;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} +[transform=\\"\\"]{transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));} +[border~=\\"\\\\32 \\"]{border-width:2px;border-style:solid;} +[border~=\\"x-1\\"]{border-left-width:1px;border-right-width:1px;border-left-style:solid;border-right-style:solid;} +[border~=\\"blue-200\\"]{--un-border-opacity:1;border-color:rgba(191,219,254,var(--un-border-opacity));} +[border~=\\"rounded-xl\\"]{border-radius:0.75rem;} +[border~=\\"rounded\\"]{border-radius:0.25rem;} +[rounded-sm=\\"\\"]{border-radius:0.125rem;} +[border~=\\"x-style-dashed\\"]{border-left-style:dashed;border-right-style:dashed;} +.dark [bg~=\\"dark\\\\:\\\\!blue-500\\"]{--un-bg-opacity:1 !important;background-color:rgba(59,130,246,var(--un-bg-opacity)) !important;} +.dark [bg~=\\"dark\\\\:hover\\\\:blue-600\\"]:hover{--un-bg-opacity:1;background-color:rgba(37,99,235,var(--un-bg-opacity));} +[bg~=\\"blue-400\\"]{--un-bg-opacity:1;background-color:rgba(96,165,250,var(--un-bg-opacity));} +[bg~=\\"hover\\\\:blue-500\\"]:hover{--un-bg-opacity:1;background-color:rgba(59,130,246,var(--un-bg-opacity));} +[important~=\\"bg-red\\"]{--un-bg-opacity:1 !important;background-color:rgba(248,113,113,var(--un-bg-opacity)) !important;} +[p~=\\"t-2\\"], +[pt~=\\"\\\\32 \\"], +[pt2=\\"\\"]{padding-top:0.5rem;} +[text~=\\"sm\\"]{font-size:0.875rem;line-height:1.25rem;} +[important~=\\"text-red\\"]{--un-text-opacity:1 !important;color:rgba(248,113,113,var(--un-text-opacity)) !important;} +[text~=\\"white\\"]{--un-text-opacity:1;color:rgba(255,255,255,var(--un-text-opacity));} +[after~=\\"content-\\\\[unocss\\\\]\\"]::after{content:\\"unocss\\";} +@media (min-width: 640px){ +[sm~=\\"\\\\[color\\\\:red\\\\]\\"]{color:red;} +} +@media (min-width: 768px){ +[md~=\\"\\\\[--var\\\\:var\\\\(--another\\\\)\\\\]\\"]{--var:var(--another);} +} +@media (min-width: 1024px){ +[lg~=\\"bg-blue-600\\"]{--un-bg-opacity:1;background-color:rgba(37,99,235,var(--un-bg-opacity));} +}" +`; + +exports[`attributify > variant 1`] = ` +[ + "uno-layer-base-c-white/10", + "hover:uno-layer-base-c-black/20", + "sm-[color:red]", + "md-[--var:var(--another)]", + "lg-bg-blue-600", + undefined, + undefined, + "important-text-red", + "important-bg-red", + "bg-blue-400", + "hover:bg-blue-500", + "dark:!bg-blue-500", + "dark:hover:bg-blue-600", + "text-sm", + "text-white", + "!leading-4", + "!flex", + "flex-col", + "p-t-2", + "pt-2", + "border-rounded-xl", + "border-x-1", + "border-x-style-dashed", + "font-foo", + "font->", + "font-bar", + "font-?", + "font-mono", + "font-:", + "font-sans", + "p-y-2", + "p-x-4", + "border-2", + "border-rounded", + "border-blue-200", + "children-m-auto", + "pt2", + "rounded-sm", + "inline-block", + "transform", + "translate-x-100%", + "translate-y-", + "%", + "rotate-30", + "after-content-[unocss]", + "rotate-60", + "ma", + "m-1", + "m-2", + "m-3", +] +`; From 0d1ceabc03c8dbc1575ef1ea4882cdc871dbd126 Mon Sep 17 00:00:00 2001 From: snowingfox <1503401882@qq.com> Date: Tue, 26 Jul 2022 14:09:03 +0800 Subject: [PATCH 06/19] feat: ensure usage in jsx|tsx|js file & blocklist --- .../transformer-attributify-jsx/src/index.ts | 46 +++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/packages/transformer-attributify-jsx/src/index.ts b/packages/transformer-attributify-jsx/src/index.ts index e28ce34d19..9d9bc2e263 100644 --- a/packages/transformer-attributify-jsx/src/index.ts +++ b/packages/transformer-attributify-jsx/src/index.ts @@ -1,23 +1,53 @@ +import { isRegExp } from 'util/types' import type { SourceCodeTransformer } from '@unocss/core' +export interface TransformerAttributifyJsxOptions { + /** + * the list of attributes to ignore + * @default [] + */ + blocklist?: (string | RegExp)[] +} + const elementRE = /|<(\/?)([a-zA-Z][-.:0-9_a-zA-Z]*)((?:\s+[^>]*?(?:(?:'[^']*')|(?:"[^"]*"))?)*)\s*(\/?)>/gs -export default function transformerAttributifyJsx(): SourceCodeTransformer { +export default function transformerAttributifyJsx(options: TransformerAttributifyJsxOptions = {}): SourceCodeTransformer { + if (!options.blocklist) + options.blocklist = [] + + const isRuleContainedInBlocklist = (matchedRule: string) => { + if (options.blocklist!.includes(matchedRule)) { + return true + } + else { + for (const blockedRule of options.blocklist!) { + if (isRegExp(blockedRule) && blockedRule.test(matchedRule)) + return true + } + } + + return false + } + return { - name: 'attributify-jsx', + name: 'transformer-jsx', enforce: 'pre', - transform(code) { - Array.from(code.toString().matchAll(elementRE)).forEach((item) => { - for (const attr of item[3].matchAll(/([a-zA-Z()#][a-zA-Z0-9-_:()#]*)(?:\s*=\s*((?:'[^']*')|(?:"[^"]*")|\S+))?/g)) { - if (!attr[0].includes('=')) { - const matchedRule = attr[0] + idFilter: id => id.endsWith('.jsx') || id.endsWith('.js') || id.endsWith('.tsx'), + async transform(code, _, { uno }) { + for (const item of Array.from(code.toString().matchAll(elementRE))) { + for (const attr of item[3].matchAll(/([a-zA-Z()#][\[?a-zA-Z0-9-_:()#%\]?]*)(?:\s*=\s*((?:'[^']*')|(?:"[^"]*")|\S+))?/g)) { + const matchedRule = attr[0] + if (!matchedRule.includes('=') + && await uno.parseToken(matchedRule) + && !isRuleContainedInBlocklist(matchedRule) + ) { const tag = item[2] const startIdx = (item.index || 0) + (attr.index || 0) + tag.length + 1 const endIdx = startIdx + matchedRule.length code.overwrite(startIdx, endIdx, `${matchedRule}=""`) } } - }) + } }, } } From 8e3750685dec1dd3de4f94b505ef2b95ecb7c661 Mon Sep 17 00:00:00 2001 From: snowingfox <1503401882@qq.com> Date: Tue, 26 Jul 2022 14:10:40 +0800 Subject: [PATCH 07/19] docs: add type declaration & blocklist usage --- .../transformer-attributify-jsx/README.md | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/transformer-attributify-jsx/README.md b/packages/transformer-attributify-jsx/README.md index 26b2454fc0..9b6cd3e7c5 100644 --- a/packages/transformer-attributify-jsx/README.md +++ b/packages/transformer-attributify-jsx/README.md @@ -19,7 +19,9 @@ import {presetAttributify} from "unocss/src"; export default defineConfig({ // ... transformers: [ - transformerAttributifyJsx(), + transformerAttributifyJsx({ + blocklist: ['text-red'] + }), ], presets: [ // ... @@ -27,6 +29,15 @@ export default defineConfig({ ] }) ``` +```ts +export interface TransformerAttributifyJsxOptions { + /** + * the list of attributes to ignore + * @default [] + */ + blocklist?: (string | RegExp)[] +} +``` ## Notice @@ -40,8 +51,13 @@ export default defineConfig({ # Usage +```js +transformerAttributifyJsx({ + blocklist: ['text-red', 'text-center'] +}) +``` ```jsx -
+
unocss
``` @@ -49,7 +65,7 @@ export default defineConfig({ Will be compiled to: ```html -
+
unocss
``` From d8d282affa8bfaf63318875b38e0230540509c87 Mon Sep 17 00:00:00 2001 From: snowingfox <1503401882@qq.com> Date: Tue, 26 Jul 2022 14:11:40 +0800 Subject: [PATCH 08/19] test: blocklist --- test/transformer-attributify-jsx.test.ts | 64 +++++++++++++++++++++--- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/test/transformer-attributify-jsx.test.ts b/test/transformer-attributify-jsx.test.ts index ec7e6c0b61..af1c8c1a2d 100644 --- a/test/transformer-attributify-jsx.test.ts +++ b/test/transformer-attributify-jsx.test.ts @@ -1,7 +1,10 @@ +import { isRegExp } from 'util/types' import MagicString from 'magic-string' +import presetAttributify from '@unocss/preset-attributify' +import presetUno from '@unocss/preset-uno' import { createGenerator } from '@unocss/core' import { describe, expect, test } from 'vitest' -import transformerAttributifyJsx from '../packages/transformer-attributify-jsx' +import transformerAttributifyJsx from '../packages/transformer-attributify-jsx/src' describe('transformerAttributifyJs', () => { const originalCode = ` @@ -27,12 +30,16 @@ describe('transformerAttributifyJs', () => { on-demand · instant · fully customizable
` - const code = new MagicString(originalCode) - - const uno = createGenerator({}) + const uno = createGenerator({ + presets: [ + presetUno(), + presetAttributify(), + ], + }) - test('transform', () => { - transformerAttributifyJsx().transform(code, 'foo.js', { uno, tokens: new Set() } as any) + test('transform', async () => { + const code = new MagicString(originalCode) + await transformerAttributifyJsx().transform(code, 'app.tsx', { uno, tokens: new Set() } as any) expect(code.toString()).toMatchInlineSnapshot(` " @@ -46,7 +53,7 @@ describe('transformerAttributifyJs', () => {
{ " `) }) + + test('blocklist', async () => { + const code = new MagicString(originalCode) + const blocklist = ['flex', 'absolute', /op[0-9]+/] + + await transformerAttributifyJsx({ + blocklist, + }).transform(code, 'app.jsx', { uno, tokens: new Set() } as any) + + expect(code.toString()).toMatchInlineSnapshot(` + " + +
+ on-demand · instant · fully customizable +
+ " + `) + + const codeToString = code.toString() + blocklist.forEach((rule) => { + if (isRegExp(rule)) + expect(new RegExp(`${rule.source}=""`).test(codeToString)).not.true + else + expect(codeToString).not.toMatch(`${rule}=""`) + }) + }) }) From 92a113c41422c89c1745970b0953eadcbcb633bb Mon Sep 17 00:00:00 2001 From: snowingfox <1503401882@qq.com> Date: Tue, 26 Jul 2022 14:18:58 +0800 Subject: [PATCH 09/19] chore: fix typo --- packages/transformer-attributify-jsx/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/transformer-attributify-jsx/README.md b/packages/transformer-attributify-jsx/README.md index 9b6cd3e7c5..3911c63621 100644 --- a/packages/transformer-attributify-jsx/README.md +++ b/packages/transformer-attributify-jsx/README.md @@ -14,7 +14,7 @@ npm i -D @unocss/transformer-attributify-jsx // uno.config.js import {defineConfig} from 'unocss' import transformerAttributifyJsx from "@unocss/transformer-attributify-jsx" -import {presetAttributify} from "unocss/src"; +import {presetAttributify} from "unocss"; export default defineConfig({ // ... From 782431e69aa15a4d41a6cf2791f37487bccca7ee Mon Sep 17 00:00:00 2001 From: snowingfox <1503401882@qq.com> Date: Tue, 26 Jul 2022 17:26:34 +0800 Subject: [PATCH 10/19] docs: update usage example --- packages/transformer-attributify-jsx/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/transformer-attributify-jsx/README.md b/packages/transformer-attributify-jsx/README.md index 3911c63621..7cf2f2f572 100644 --- a/packages/transformer-attributify-jsx/README.md +++ b/packages/transformer-attributify-jsx/README.md @@ -53,11 +53,11 @@ export interface TransformerAttributifyJsxOptions { ```js transformerAttributifyJsx({ - blocklist: ['text-red', 'text-center'] + blocklist: [/text-[a-zA-Z]*/, 'text-5xl'] }) ``` ```jsx -
+
unocss
``` @@ -65,7 +65,7 @@ transformerAttributifyJsx({ Will be compiled to: ```html -
+
unocss
``` From 7799e2bfc11dc45a632daf4a0b0a69964269d73e Mon Sep 17 00:00:00 2001 From: snowingfox <1503401882@qq.com> Date: Fri, 29 Jul 2022 16:26:39 +0800 Subject: [PATCH 11/19] fix: insert await before runAsyncChildProcess --- test/cli.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cli.test.ts b/test/cli.test.ts index 1f97db2de5..ebc1680b8c 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -44,7 +44,7 @@ export default defineConfig({ const testDir = getTestDir() const absolutePathOfFile = resolve(testDir, fileName) await fs.outputFile(absolutePathOfFile, initializedContent) - runAsyncChildProcess(testDir, './views/**/*', '-w') + await runAsyncChildProcess(testDir, './views/**/*', '-w') const outputPath = resolve(testDir, 'uno.css') for (let i = 50; i >= 0; i--) { await sleep(50) From a2842a82602d6d5463026154e53a96c953be7073 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 29 Jul 2022 19:35:08 +0800 Subject: [PATCH 12/19] chore: update --- .../transformer-attributify-jsx/README.md | 97 +++++++++++++------ .../transformer-attributify-jsx/src/index.ts | 50 +++++++--- 2 files changed, 109 insertions(+), 38 deletions(-) diff --git a/packages/transformer-attributify-jsx/README.md b/packages/transformer-attributify-jsx/README.md index 7cf2f2f572..ad5111b09d 100644 --- a/packages/transformer-attributify-jsx/README.md +++ b/packages/transformer-attributify-jsx/README.md @@ -2,7 +2,46 @@ -happy play with preset-attributify in jsx/tsx +Support [valueless attributify](https://github.com/unocss/unocss/tree/main/packages/preset-attributify#valueless-attributify) in JSX/TSX. + +```jsx +export function Component() { + return ( +
+ unocss +
+ ) +} +``` + +Will be transformed to: + +```jsx +export function Component() { + return ( +
+ unocss +
+ ) +} +``` + +
+Without this transformer + +JSX by default will treat valueless attributes as boolean attributes. + +```jsx +export function Component() { + return ( +
+ unocss +
+ ) +} +``` + +
## Install @@ -12,50 +51,54 @@ npm i -D @unocss/transformer-attributify-jsx ```ts // uno.config.js -import {defineConfig} from 'unocss' -import transformerAttributifyJsx from "@unocss/transformer-attributify-jsx" -import {presetAttributify} from "unocss"; +import { defineConfig, presetAttributify } from 'unocss' +import transformerAttributifyJsx from '@unocss/transformer-attributify-jsx' export default defineConfig({ // ... - transformers: [ - transformerAttributifyJsx({ - blocklist: ['text-red'] - }), - ], presets: [ // ... presetAttributify() - ] + ], + transformers: [ + transformerAttributifyJsx(), // <-- + ], }) ``` -```ts -export interface TransformerAttributifyJsxOptions { - /** - * the list of attributes to ignore - * @default [] - */ - blocklist?: (string | RegExp)[] -} -``` -## Notice +## Caveats > ⚠️ The rules are almost the same as those of `preset-attributify`, but there are several precautions -```vue -
// cannot end with `%` -
// cannot contain `:` -
// cannot contain `[` or `]` +```html +
+ +
+ +
``` -# Usage +Instead, you may want to use valued attributes instead: + +```html +
+ +
+ +
+``` + +## Blocklist + +This transformer will only transform attrubutes that are valid UnoCSS utilities. +You can also `blocklist` bypass some attributes from been transformed. ```js transformerAttributifyJsx({ blocklist: [/text-[a-zA-Z]*/, 'text-5xl'] }) ``` + ```jsx
unocss @@ -66,10 +109,10 @@ Will be compiled to: ```html
- unocss + unocss
``` ## License -MIT License © 2021-PRESENT [Anthony Fu](https://github.com/antfu) +MIT License © 2022-PRESENT [Anthony Fu](https://github.com/antfu) diff --git a/packages/transformer-attributify-jsx/src/index.ts b/packages/transformer-attributify-jsx/src/index.ts index 9d9bc2e263..c30c4dab37 100644 --- a/packages/transformer-attributify-jsx/src/index.ts +++ b/packages/transformer-attributify-jsx/src/index.ts @@ -1,5 +1,7 @@ import { isRegExp } from 'util/types' import type { SourceCodeTransformer } from '@unocss/core' +import type { FilterPattern } from '@rollup/pluginutils' +import { createFilter } from '@rollup/pluginutils' export interface TransformerAttributifyJsxOptions { /** @@ -7,9 +9,23 @@ export interface TransformerAttributifyJsxOptions { * @default [] */ blocklist?: (string | RegExp)[] + + /** + * Regex of modules to be included from processing + * @default [/\.[jt]sx$/, /\.mdx$/] + */ + include?: FilterPattern + + /** + * Regex of modules to exclude from processing + * + * @default [] + */ + exclude?: FilterPattern } const elementRE = /|<(\/?)([a-zA-Z][-.:0-9_a-zA-Z]*)((?:\s+[^>]*?(?:(?:'[^']*')|(?:"[^"]*"))?)*)\s*(\/?)>/gs +const attributeRE = /([a-zA-Z()#][\[?a-zA-Z0-9-_:()#%\]?]*)(?:\s*=\s*((?:'[^']*')|(?:"[^"]*")|\S+))?/g export default function transformerAttributifyJsx(options: TransformerAttributifyJsxOptions = {}): SourceCodeTransformer { if (!options.blocklist) @@ -29,25 +45,37 @@ export default function transformerAttributifyJsx(options: TransformerAttributif return false } + const idFilter = createFilter( + options.include || [/\.[jt]sx$/, /\.mdx$/], + options.exclude || [], + ) + return { name: 'transformer-jsx', enforce: 'pre', - idFilter: id => id.endsWith('.jsx') || id.endsWith('.js') || id.endsWith('.tsx'), + idFilter, async transform(code, _, { uno }) { + const tasks: Promise[] = [] + for (const item of Array.from(code.toString().matchAll(elementRE))) { - for (const attr of item[3].matchAll(/([a-zA-Z()#][\[?a-zA-Z0-9-_:()#%\]?]*)(?:\s*=\s*((?:'[^']*')|(?:"[^"]*")|\S+))?/g)) { + for (const attr of item[3].matchAll(attributeRE)) { const matchedRule = attr[0] - if (!matchedRule.includes('=') - && await uno.parseToken(matchedRule) - && !isRuleContainedInBlocklist(matchedRule) - ) { - const tag = item[2] - const startIdx = (item.index || 0) + (attr.index || 0) + tag.length + 1 - const endIdx = startIdx + matchedRule.length - code.overwrite(startIdx, endIdx, `${matchedRule}=""`) - } + if (matchedRule.includes('=') + || isRuleContainedInBlocklist(matchedRule)) + return + + tasks.push((async () => { + if (await uno.parseToken(matchedRule)) { + const tag = item[2] + const startIdx = (item.index || 0) + (attr.index || 0) + tag.length + 1 + const endIdx = startIdx + matchedRule.length + code.overwrite(startIdx, endIdx, `${matchedRule}=""`) + } + })()) } } + + await Promise.all(tasks) }, } } From 046aac1ee8ef1a16a0b60f535a242d3a26f1be08 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 29 Jul 2022 20:00:41 +0800 Subject: [PATCH 13/19] chore: update --- packages/preset-attributify/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/preset-attributify/README.md b/packages/preset-attributify/README.md index 8871c57aed..9bd7c39c07 100644 --- a/packages/preset-attributify/README.md +++ b/packages/preset-attributify/README.md @@ -71,7 +71,7 @@ Can be written as In addition to Windi CSS's Attributify Mode, this presets also supports valueless attributes. -For example, +For example, ```html
@@ -83,6 +83,8 @@ now can be
``` +> Note: If you are using JSX, `
` might be transformed to `
` which will make the generate CSS from UnoCSS failed to match the attributes. To solve this, you might want to try [`transformer-attributify-jsx`](https://github.com/unocss/unocss/tree/main/packages/transformer-attributify-jsx) along with this preset. + ### Properties Conflicts If the name of the attributes mode ever conflicts with the elements' or components' properties, you can add `un-` prefix to be specific to UnoCSS's attributify mode. From 954ab692091adcf0da183b3bbb01e5784651d623 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 29 Jul 2022 20:15:33 +0800 Subject: [PATCH 14/19] chore: update --- .../transformer-attributify-jsx/src/index.ts | 32 +++++++++---------- test/transformer-attributify-jsx.test.ts | 17 ++++------ 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/packages/transformer-attributify-jsx/src/index.ts b/packages/transformer-attributify-jsx/src/index.ts index c30c4dab37..5505fa8cbb 100644 --- a/packages/transformer-attributify-jsx/src/index.ts +++ b/packages/transformer-attributify-jsx/src/index.ts @@ -28,18 +28,19 @@ const elementRE = /|<(\/?)([a-zA-Z][-.:0-9_a-zA-Z]*)((?:\s+[^>]*? const attributeRE = /([a-zA-Z()#][\[?a-zA-Z0-9-_:()#%\]?]*)(?:\s*=\s*((?:'[^']*')|(?:"[^"]*")|\S+))?/g export default function transformerAttributifyJsx(options: TransformerAttributifyJsxOptions = {}): SourceCodeTransformer { - if (!options.blocklist) - options.blocklist = [] + const { + blocklist = [], + } = options - const isRuleContainedInBlocklist = (matchedRule: string) => { - if (options.blocklist!.includes(matchedRule)) { - return true - } - else { - for (const blockedRule of options.blocklist!) { - if (isRegExp(blockedRule) && blockedRule.test(matchedRule)) + const isBlocked = (matchedRule: string) => { + for (const blockedRule of blocklist) { + if (isRegExp(blockedRule)) { + if (blockedRule.test(matchedRule)) return true } + else if (matchedRule === blockedRule) { + return true + } } return false @@ -57,21 +58,20 @@ export default function transformerAttributifyJsx(options: TransformerAttributif async transform(code, _, { uno }) { const tasks: Promise[] = [] - for (const item of Array.from(code.toString().matchAll(elementRE))) { + for (const item of Array.from(code.original.matchAll(elementRE))) { for (const attr of item[3].matchAll(attributeRE)) { const matchedRule = attr[0] - if (matchedRule.includes('=') - || isRuleContainedInBlocklist(matchedRule)) - return + if (matchedRule.includes('=') || isBlocked(matchedRule)) + continue - tasks.push((async () => { - if (await uno.parseToken(matchedRule)) { + tasks.push(uno.parseToken(matchedRule).then((matched) => { + if (matched) { const tag = item[2] const startIdx = (item.index || 0) + (attr.index || 0) + tag.length + 1 const endIdx = startIdx + matchedRule.length code.overwrite(startIdx, endIdx, `${matchedRule}=""`) } - })()) + })) } } diff --git a/test/transformer-attributify-jsx.test.ts b/test/transformer-attributify-jsx.test.ts index af1c8c1a2d..ba027e1503 100644 --- a/test/transformer-attributify-jsx.test.ts +++ b/test/transformer-attributify-jsx.test.ts @@ -8,7 +8,7 @@ import transformerAttributifyJsx from '../packages/transformer-attributify-jsx/s describe('transformerAttributifyJs', () => { const originalCode = ` -
+
unocss @@ -29,7 +29,8 @@ describe('transformerAttributifyJs', () => {
on-demand · instant · fully customizable
- ` + `.trim() + const uno = createGenerator({ presets: [ presetUno(), @@ -42,8 +43,7 @@ describe('transformerAttributifyJs', () => { await transformerAttributifyJsx().transform(code, 'app.tsx', { uno, tokens: new Set() } as any) expect(code.toString()).toMatchInlineSnapshot(` - " -
+ "
unocss @@ -63,8 +63,7 @@ describe('transformerAttributifyJs', () => {
on-demand · instant · fully customizable -
- " +
" `) }) @@ -77,8 +76,7 @@ describe('transformerAttributifyJs', () => { }).transform(code, 'app.jsx', { uno, tokens: new Set() } as any) expect(code.toString()).toMatchInlineSnapshot(` - " -
+ "
unocss @@ -98,8 +96,7 @@ describe('transformerAttributifyJs', () => {
on-demand · instant · fully customizable -
- " +
" `) const codeToString = code.toString() From b1ba40f56b27c216b35ee8150f78c49824caa8a4 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 29 Jul 2022 20:22:36 +0800 Subject: [PATCH 15/19] chore: setup package --- alias.ts | 1 + packages/README.md | 1 + packages/transformer-attributify-jsx/package.json | 3 ++- packages/transformer-attributify-jsx/src/index.ts | 3 ++- packages/unocss/package.json | 1 + packages/unocss/src/index.ts | 1 + pnpm-lock.yaml | 13 +++++++++++++ tsconfig.json | 1 + 8 files changed, 22 insertions(+), 2 deletions(-) diff --git a/alias.ts b/alias.ts index 5ea00bddab..4350a7e22a 100644 --- a/alias.ts +++ b/alias.ts @@ -23,6 +23,7 @@ export const alias: Record = { '@unocss/transformer-directives': r('./packages/transformer-directives/src/'), '@unocss/transformer-variant-group': r('./packages/transformer-variant-group/src/'), '@unocss/transformer-compile-class': r('./packages/transformer-compile-class/src/'), + '@unocss/transformer-attributify-jsx': r('./packages/transformer-attributify-jsx/src/'), '@unocss/vite': r('./packages/vite/src/'), 'unocss': r('./packages/unocss/src/'), } diff --git a/packages/README.md b/packages/README.md index 5b06eb9bef..c6599381c8 100644 --- a/packages/README.md +++ b/packages/README.md @@ -17,6 +17,7 @@ | [@unocss/transformer-variant-group](./transformer-variant-group) | Transformer for Windi CSS's variant group feature | ✅ | No | | [@unocss/transformer-directives](./transformer-directives) | Transformer for CSS directives like `@apply` | ✅ | No | | [@unocss/transformer-compile-class](./transformer-compile-class) | Compile group of classes into one class | ✅ | No | +| [@unocss/transformer-attributify-jsx](./transformer-attributify-jsx) | Support valueless attributify in JSX/TSX | ✅ | No | | [@unocss/extractor-pug](./extractor-pug) | Extractor for Pug | No | - | | [@unocss/autocomplete](./autocomplete) | Utils for autocomplete | No | - | | [@unocss/config](./config) | Configuration file loader | ✅ | - | diff --git a/packages/transformer-attributify-jsx/package.json b/packages/transformer-attributify-jsx/package.json index c27c1ec68b..04ee0212c2 100644 --- a/packages/transformer-attributify-jsx/package.json +++ b/packages/transformer-attributify-jsx/package.json @@ -1,7 +1,7 @@ { "name": "@unocss/transformer-attributify-jsx", "version": "0.0.1", - "description": "Better JSX attribute handling for preset-attributify", + "description": "Support valueless attributify in JSX/TSX.", "author": "Anthony Fu ", "license": "MIT", "funding": "https://github.com/sponsors/antfu", @@ -39,6 +39,7 @@ "@unocss/core": "workspace:*" }, "devDependencies": { + "@rollup/pluginutils": "^4.2.1", "magic-string": "^0.26.2" } } diff --git a/packages/transformer-attributify-jsx/src/index.ts b/packages/transformer-attributify-jsx/src/index.ts index 5505fa8cbb..63f9cdbb10 100644 --- a/packages/transformer-attributify-jsx/src/index.ts +++ b/packages/transformer-attributify-jsx/src/index.ts @@ -1,8 +1,9 @@ import { isRegExp } from 'util/types' import type { SourceCodeTransformer } from '@unocss/core' -import type { FilterPattern } from '@rollup/pluginutils' import { createFilter } from '@rollup/pluginutils' +export type FilterPattern = ReadonlyArray | string | RegExp | null + export interface TransformerAttributifyJsxOptions { /** * the list of attributes to ignore diff --git a/packages/unocss/package.json b/packages/unocss/package.json index 12414e1c5b..42010c18ef 100644 --- a/packages/unocss/package.json +++ b/packages/unocss/package.json @@ -119,6 +119,7 @@ "@unocss/transformer-compile-class": "workspace:*", "@unocss/transformer-directives": "workspace:*", "@unocss/transformer-variant-group": "workspace:*", + "@unocss/transformer-attributify-jsx": "workspace:*", "@unocss/vite": "workspace:*" }, "devDependencies": { diff --git a/packages/unocss/src/index.ts b/packages/unocss/src/index.ts index f154573d11..c2bccfabc7 100644 --- a/packages/unocss/src/index.ts +++ b/packages/unocss/src/index.ts @@ -13,6 +13,7 @@ export { default as presetWind } from '@unocss/preset-wind' export { default as transformerDirectives } from '@unocss/transformer-directives' export { default as transformerVariantGroup } from '@unocss/transformer-variant-group' export { default as transformerCompileClass } from '@unocss/transformer-compile-class' +export { default as transformerAttributifyJsx } from '@unocss/transformer-attributify-jsx' export function defineConfig(config: UserConfig) { return config diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c3e037a2e0..c99e1db2d7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -458,6 +458,17 @@ importers: '@unocss/core': link:../core '@unocss/scope': link:../scope + packages/transformer-attributify-jsx: + specifiers: + '@rollup/pluginutils': ^4.2.1 + '@unocss/core': workspace:* + magic-string: ^0.26.2 + dependencies: + '@unocss/core': link:../core + devDependencies: + '@rollup/pluginutils': 4.2.1 + magic-string: 0.26.2 + packages/transformer-compile-class: specifiers: '@unocss/core': workspace:* @@ -500,6 +511,7 @@ importers: '@unocss/preset-web-fonts': workspace:* '@unocss/preset-wind': workspace:* '@unocss/reset': workspace:* + '@unocss/transformer-attributify-jsx': workspace:* '@unocss/transformer-compile-class': workspace:* '@unocss/transformer-directives': workspace:* '@unocss/transformer-variant-group': workspace:* @@ -517,6 +529,7 @@ importers: '@unocss/preset-web-fonts': link:../preset-web-fonts '@unocss/preset-wind': link:../preset-wind '@unocss/reset': link:../reset + '@unocss/transformer-attributify-jsx': link:../transformer-attributify-jsx '@unocss/transformer-compile-class': link:../transformer-compile-class '@unocss/transformer-directives': link:../transformer-directives '@unocss/transformer-variant-group': link:../transformer-variant-group diff --git a/tsconfig.json b/tsconfig.json index e7a0d7e08d..529ca480d0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -38,6 +38,7 @@ "@unocss/transformer-directives": ["./packages/transformer-directives/src/index.ts"], "@unocss/transformer-variant-group": ["./packages/transformer-variant-group/src/index.ts"], "@unocss/transformer-compile-class": ["./packages/transformer-compile-class/src/index.ts"], + "@unocss/transformer-attributify-jsx": ["./packages/transformer-attributify-jsx/src/index.ts"], "@unocss/vite": ["./packages/vite/src/index.ts"], "unocss": ["./packages/unocss/src/index.ts"], "unocss/vite": ["./packages/unocss/src/vite.ts"] From 94dcc153ed1974625c011a9da41b64a5160fee2e Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 29 Jul 2022 20:28:44 +0800 Subject: [PATCH 16/19] chore: fix build --- packages/transformer-attributify-jsx/src/index.ts | 3 +-- packages/unocss/package.json | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/transformer-attributify-jsx/src/index.ts b/packages/transformer-attributify-jsx/src/index.ts index 63f9cdbb10..525fe821e7 100644 --- a/packages/transformer-attributify-jsx/src/index.ts +++ b/packages/transformer-attributify-jsx/src/index.ts @@ -1,4 +1,3 @@ -import { isRegExp } from 'util/types' import type { SourceCodeTransformer } from '@unocss/core' import { createFilter } from '@rollup/pluginutils' @@ -35,7 +34,7 @@ export default function transformerAttributifyJsx(options: TransformerAttributif const isBlocked = (matchedRule: string) => { for (const blockedRule of blocklist) { - if (isRegExp(blockedRule)) { + if (blockedRule instanceof RegExp) { if (blockedRule.test(matchedRule)) return true } diff --git a/packages/unocss/package.json b/packages/unocss/package.json index 42010c18ef..bbc780042e 100644 --- a/packages/unocss/package.json +++ b/packages/unocss/package.json @@ -116,10 +116,10 @@ "@unocss/preset-web-fonts": "workspace:*", "@unocss/preset-wind": "workspace:*", "@unocss/reset": "workspace:*", + "@unocss/transformer-attributify-jsx": "workspace:*", "@unocss/transformer-compile-class": "workspace:*", "@unocss/transformer-directives": "workspace:*", "@unocss/transformer-variant-group": "workspace:*", - "@unocss/transformer-attributify-jsx": "workspace:*", "@unocss/vite": "workspace:*" }, "devDependencies": { From ff1039f82415d81a61f2e218157e24b8a6746eff Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 29 Jul 2022 20:45:46 +0800 Subject: [PATCH 17/19] chore: update --- test/transformer-attributify-jsx.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/transformer-attributify-jsx.test.ts b/test/transformer-attributify-jsx.test.ts index ba027e1503..9a28678226 100644 --- a/test/transformer-attributify-jsx.test.ts +++ b/test/transformer-attributify-jsx.test.ts @@ -1,4 +1,3 @@ -import { isRegExp } from 'util/types' import MagicString from 'magic-string' import presetAttributify from '@unocss/preset-attributify' import presetUno from '@unocss/preset-uno' @@ -101,7 +100,7 @@ describe('transformerAttributifyJs', () => { const codeToString = code.toString() blocklist.forEach((rule) => { - if (isRegExp(rule)) + if (rule instanceof RegExp) expect(new RegExp(`${rule.source}=""`).test(codeToString)).not.true else expect(codeToString).not.toMatch(`${rule}=""`) From a2bedf4773d261fddaadada808fade8049b85f32 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 29 Jul 2022 21:41:53 +0800 Subject: [PATCH 18/19] chore: try fix ci --- interactive/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactive/package.json b/interactive/package.json index bfe2578ad6..dbadce666c 100644 --- a/interactive/package.json +++ b/interactive/package.json @@ -6,7 +6,7 @@ "build": "nuxi generate . && esno scripts/verify-build.ts", "dev": "nuxi dev .", "start": "node .output/server/index.mjs", - "postinstall": "esno scripts/prepare.ts", + "postinstall": "npx nuxt-tsconfig-stub && esno scripts/prepare.ts", "fetch": "esno scripts/fetch.ts" }, "devDependencies": { From 35c45fcde37b7ad7615392ac54b075632db961fe Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 29 Jul 2022 21:49:02 +0800 Subject: [PATCH 19/19] chore: update --- interactive/package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/interactive/package.json b/interactive/package.json index dbadce666c..ac0c6ad312 100644 --- a/interactive/package.json +++ b/interactive/package.json @@ -3,10 +3,9 @@ "type": "module", "private": true, "scripts": { - "build": "nuxi generate . && esno scripts/verify-build.ts", - "dev": "nuxi dev .", + "build": "nuxi prepare && esno scripts/prepare.ts && nuxi generate . && esno scripts/verify-build.ts", + "dev": "nuxi prepare && esno scripts/prepare.ts && nuxi dev .", "start": "node .output/server/index.mjs", - "postinstall": "npx nuxt-tsconfig-stub && esno scripts/prepare.ts", "fetch": "esno scripts/fetch.ts" }, "devDependencies": {