Skip to content

Commit

Permalink
feat: ignore string compilation
Browse files Browse the repository at this point in the history
  • Loading branch information
zguolee committed Sep 1, 2022
1 parent 34f5893 commit 4b86da8
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 67 deletions.
9 changes: 9 additions & 0 deletions packages/transformer-applet/README.md
Expand Up @@ -33,9 +33,18 @@ export interface TransformerAppletOptions {
/**
* Prefix for compile class name
* @default 'uno-'
* e.g. bg-[hsl(2.7,81.9%,69.6%)] to uno-98db2v
*/
classPrefix?: string

/**
* Prefix for ignore compile string
* If the string contains ignore prefix, it will be replaced with an empty string.
* e.g. 'applet-ignore: bg-[hsl(2.7,81.9%,69.6%)]' to 'bg-[hsl(2.7,81.9%,69.6%)]'
* @default 'applet-ignore:'
*/
ignorePrefix?: string

/**
* Hash function
*/
Expand Down
60 changes: 28 additions & 32 deletions packages/transformer-applet/src/index.ts
Expand Up @@ -3,16 +3,16 @@ import MagicString from 'magic-string'
import type { TransformerAppletOptions } from './types'
import { compileApplet } from './compile'

const elementRE = /<\w(?=.*>)[\w:\.$-]*\s(((".*?>?.*?")|.*?)*?)\/?>/gs
const valuedAttributeRE = /([?]|(?!\d|-{2}|-\d)[a-zA-Z0-9\u00A0-\uFFFF-_:!%-]+)(?:={?(["'])([^\2]*?)\2}?)?/g

// Regular expression of characters to be escaped
const charReg = /[.:%!#()[\/\],]/

const string1RE = /([']).*?(['])/g
const string2RE = /([\`]).*?([\`])/g
const elementRE = /<\w(?=.*>)[\w:\.$-]*\s(((".*?>?.*?")|.*?)*?)\/?>/gs
const valuedAttributeRE = /([?]|(?!\d|-{2}|-\d)[a-zA-Z0-9\u00A0-\uFFFF-_:!%-]+)(?:={?(["'])([^\2]*?)\2}?)?/g
const stringRE = /'(.*?)'|/g
// TODO: support (\`(.*?)\`)

export default function transformerApplet(options: TransformerAppletOptions = {}): SourceCodeTransformer {
const ignorePrefix = options.ignorePrefix || 'applet-ignore:'
return {
name: 'transformer-applet',
enforce: 'pre',
Expand Down Expand Up @@ -49,38 +49,34 @@ export default function transformerApplet(options: TransformerAppletOptions = {}
}
code = new MagicString(code.toString())

// process string1
const string1Matches = [...code.original.matchAll(string1RE)]
for (const match of string1Matches) {
// There may be no need
// https://tailwindcss.com/docs/background-image#arbitrary-values
// skip all the image formats in HTML for bg-[url('...')]
if (/\.(png|jpg|jpeg|gif|svg)/g.test(match[0]))
continue
// skip http(s)://
if (/http(s)?:\/\//g.test(match[0]))
// process string, only effective with ''
const stringMatches = code.original.matchAll(stringRE)
for (const match of stringMatches) {
// ignore empty string
if (!match[1])
continue

const start = match.index!
const body = match[0].slice(1, -1)
if (charReg.test(body)) {
const replacements = await compileApplet(body, ctx, options)
code.overwrite(start, start + match[0].length, `'${replacements.join(' ')}'`)
}
}
let content = match[1]

// process string2
const string2Matches = [...code.original.matchAll(string2RE)]
for (const match of string2Matches) {
// skip `${...}`
if (/\$\{.*\}/g.test(match[0]))
continue
if (content.startsWith(ignorePrefix)) {
content = content.substring(ignorePrefix.length).trim()
code.overwrite(start, start + match[0].length, match[0].replace(match[1], content))
}
else {
// There may be no need
// https://tailwindcss.com/docs/background-image#arbitrary-values
// skip all the image formats in HTML for bg-[url('...')]
if (/\.(png|jpg|jpeg|gif|svg)/g.test(content))
continue
// skip http(s)://
if (/http(s)?:\/\//g.test(content))
continue

const start = match.index!
const body = match[0].slice(1, -1)
if (charReg.test(body)) {
const replacements = await compileApplet(body, ctx, options)
code.overwrite(start, start + match[0].length, `'${replacements.join(' ')}'`)
if (charReg.test(content)) {
const replacements = await compileApplet(content, ctx, options)
code.overwrite(start, start + match[0].length, `'${replacements.join(' ')}'`)
}
}
}
s.overwrite(0, s.original.length, code.toString())
Expand Down
9 changes: 9 additions & 0 deletions packages/transformer-applet/src/types.ts
Expand Up @@ -2,9 +2,18 @@ export interface TransformerAppletOptions {
/**
* Prefix for compile class name
* @default 'uno-'
* e.g. bg-[hsl(2.7,81.9%,69.6%)] to uno-98db2v
*/
classPrefix?: string

/**
* Prefix for ignore compile string
* If the string contains ignore prefix, it will be replaced with an empty string.
* e.g. 'applet-ignore: bg-[hsl(2.7,81.9%,69.6%)]' to 'bg-[hsl(2.7,81.9%,69.6%)]'
* @default 'applet-ignore:'
*/
ignorePrefix?: string

/**
* Hash function
*/
Expand Down
13 changes: 7 additions & 6 deletions playground/src/pages/index/index.vue
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { ref } from 'vue'
const bg = 'bg-[hsl(2.7,81.9%,69.6%)]'
const bgIgnore = 'applet-ignore: bg-[hsl(2.7,81.9%,69.6%)]'
const index = 1
const customClass = 'text-red'
const bool = ref<boolean>()
Expand All @@ -10,27 +11,27 @@ const bool = ref<boolean>()
<div class="text-center aaa" p="4">
<div text="4xl" class="rotate-180 i-carbon-campsite" :class="bg" />
<div class="border bg-blue-200 font-(light mono) ">
<div class="hover:(!bg-gray-600 text-white font-medium)" text="#fff">
0123456789
<div class="hover:(!bg-gray-600 text-red font-bold)" text="#fff">
{{ 'applet-ignore: hover:(!bg-gray-600 text-red font-bold)' }}
</div>
</div>
<div :class="{ 'bg-blue': bool }" p-2 :hover-class="['!bg-green']">
<div :class="`p-2.5 ${bool ? 'p-0.5' : ''}`" m-2 :hover-class="['!bg-green']">
class="hover:bg-green"
</div>
<div flex="~ col gap-1" class="p-1" items-center :class="bool ? 'text-yellow-500 px-2.5' : ''">
<div i-carbon-campsite inline-block color="blue" text="xl" />
{{ `index${index + 1}` }}
{{ `index${index + 1}` }}{{ `index` }}
</div>
<div flex="~ col" border="2 blue">
<div text-right h-10 flex="1" text="red" :class="{ 'text-sm': index > 0 }">
0123456789
</div>
<div h-10 flex="1" text-blue :class="[index > 1 ? 'text' : '']" :style="[index > 1 ? '' : '']" :type="index > 1">
0123456789
{{ bgIgnore }}
</div>
</div>
<div class="bg-[url(https://static.runoob.com/images/demo/demo2.jpg)]" w-40 h-20 ma color="white" bg="center cover">
bg-img
{{ 'applet-ignore: bg-[url(https://static.runoob.com/images/demo/demo2.jpg)]' }}
</div>
<div class="m-0.5 p-1 text-2xl" :class="bool ? '' : 'text-yellow-500 p-2.5'">
abckefghijklmnopqrstuvwxyz
Expand Down
14 changes: 7 additions & 7 deletions test/fixtures/rules.vue
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { ref } from 'vue'
const bg = 'bg-[hsl(2.7,81.9%,69.6%)]'
const bgIgnore = 'applet-ignore: bg-[hsl(2.7,81.9%,69.6%)]'
const index = 1
const customClass = 'text-red'
const bool = ref<boolean>()
Expand All @@ -10,31 +11,30 @@ const bool = ref<boolean>()
<div class="text-center aaa" p="4">
<div text="4xl" class="rotate-180 i-carbon-campsite" :class="bg" />
<div class="border bg-blue-200 font-(light mono) ">
<div class="hover:(!bg-gray-600 text-white font-medium)" text="#fff">
0123456789
<div class="hover:(!bg-gray-600 text-red font-bold)" text="#fff">
{{ 'applet-ignore: hover:(!bg-gray-600 text-red font-bold)' }}
</div>
</div>
<div :class="{ 'bg-blue': bool }" p-2 :hover-class="['!bg-green']">
<div :class="`p-2.5 ${bool ? 'p-0.5' : ''}`" m-2 :hover-class="['!bg-green']">
class="hover:bg-green"
</div>
<div flex="~ col gap-1" class="p-1" items-center :class="bool ? 'text-yellow-500 px-2.5' : ''">
<div i-carbon-campsite inline-block color="blue" text="xl" />
{{ `index${index + 1}` }}
{{ `index${index + 1}` }}{{ `index` }}
</div>
<div flex="~ col" border="2 blue">
<div text-right h-10 flex="1" text="red" :class="{ 'text-sm': index > 0 }">
0123456789
</div>
<div h-10 flex="1" text-blue :class="[index > 1 ? 'text' : '']" :style="[index > 1 ? '' : '']" :type="index > 1">
0123456789
{{ bgIgnore }}
</div>
</div>
<div class="bg-[url(https://static.runoob.com/images/demo/demo2.jpg)]" w-40 h-20 ma color="white" bg="center cover">
bg-img
{{ 'applet-ignore: bg-[url(https://static.runoob.com/images/demo/demo2.jpg)]' }}
</div>
<div class="m-0.5 p-1 text-2xl" :class="bool ? '' : 'text-yellow-500 p-2.5'">
abckefghijklmnopqrstuvwxyz
</div>
</div>
</template>

23 changes: 13 additions & 10 deletions test/transformer-applet.test.ts
Expand Up @@ -36,6 +36,7 @@ describe('transformer-applet', () => {
"code": "<script setup lang=\\"ts\\">
import { ref } from 'vue'
const bg = 'uno-98db2v'
const bgIgnore = 'bg-[hsl(2.7,81.9%,69.6%)]'
const index = 1
const customClass = 'text-red'
const bool = ref<boolean>()
Expand All @@ -45,45 +46,46 @@ describe('transformer-applet', () => {
<div class=\\"text-center aaa\\" p=\\"4\\">
<div text=\\"4xl\\" class=\\"rotate-180 i-carbon-campsite\\" :class=\\"bg\\" />
<div class=\\"uno-1lreki font-(light mono)\\">
<div class=\\"uno-4oaq3e hover:(!bg-gray-600 font-medium)\\" text=\\"#fff\\">
0123456789
<div class=\\"uno-jw3na0 hover:(!bg-gray-600 font-bold)\\" text=\\"#fff\\">
{{ 'hover:(!bg-gray-600 text-red font-bold)' }}
</div>
</div>
<div :class=\\"{ 'bg-blue': bool }\\" p-2 :hover-class=\\"['uno-y5ng0p']\\">
<div :class=\\"\`p-2.5 \${bool ? 'uno-kl65hf' : ''}\`\\" m-2 :hover-class=\\"['uno-y5ng0p']\\">
class=\\"hover:bg-green\\"
</div>
<div flex=\\"~ col gap-1\\" class=\\"p-1\\" items-center :class=\\"bool ? 'uno-2z589z' : ''\\">
<div i-carbon-campsite inline-block color=\\"blue\\" text=\\"xl\\" />
{{ \`index\${index + 1}\` }}
{{ \`index\${index + 1}\` }}{{ \`index\` }}
</div>
<div flex=\\"~ col\\" border=\\"2 blue\\">
<div text-right h-10 flex=\\"1\\" text=\\"red\\" :class=\\"{ 'text-sm': index > 0 }\\">
0123456789
</div>
<div h-10 flex=\\"1\\" text-blue :class=\\"[index > 1 ? 'text' : '']\\" :style=\\"[index > 1 ? '' : '']\\" :type=\\"index > 1\\">
0123456789
{{ bgIgnore }}
</div>
</div>
<div class=\\"uno-w33epq\\" w-40 h-20 ma color=\\"white\\" bg=\\"center cover\\">
bg-img
{{ 'bg-[url(https://static.runoob.com/images/demo/demo2.jpg)]' }}
</div>
<div class=\\"uno-tw4biu\\" :class=\\"bool ? '' : 'uno-qju0i9'\\">
abckefghijklmnopqrstuvwxyz
</div>
</div>
</template>
",
"css": "/* layer: applet_shortcuts */
.uno-tw4biu{margin:0.125rem;padding:0.25rem;font-size:1.5rem;line-height:2rem;}
.uno-1lreki{border-width:1px;border-style:solid;--un-bg-opacity:1;background-color:rgba(191,219,254,var(--un-bg-opacity));}
.uno-98db2v{--un-bg-opacity:1;background-color:hsla(2.7,81.9%,69.6%,var(--un-bg-opacity));}
.uno-y5ng0p{--un-bg-opacity:1 !important;background-color:rgba(74,222,128,var(--un-bg-opacity)) !important;}
.uno-w33epq{--un-url:url(https://static.runoob.com/images/demo/demo2.jpg);background-image:var(--un-url);}
.uno-kl65hf{padding:0.125rem;}
.uno-qju0i9{padding:0.625rem;--un-text-opacity:1;color:rgba(234,179,8,var(--un-text-opacity));}
.uno-2z589z{padding-left:0.625rem;padding-right:0.625rem;--un-text-opacity:1;color:rgba(234,179,8,var(--un-text-opacity));}
.uno-4oaq3e{--un-text-opacity:1;color:rgba(255,255,255,var(--un-text-opacity));}
.uno-jw3na0{--un-text-opacity:1;color:rgba(248,113,113,var(--un-text-opacity));}
/* layer: default */
.m-2{margin:0.5rem;}
.ma{margin:auto;}
.inline-block{display:inline-block;}
.h-10{height:2.5rem;}
Expand All @@ -94,10 +96,11 @@ describe('transformer-applet', () => {
.items-center{align-items:center;}
.gap-1{grid-gap:0.25rem;gap:0.25rem;}
.border{border-width:1px;border-style:solid;}
.bg-blue{--un-bg-opacity:1;background-color:rgba(96,165,250,var(--un-bg-opacity));}
.bg-\\\\[hsl\\\\(2\\\\.7\\\\,81\\\\.9\\\\%\\\\,69\\\\.6\\\\%\\\\)\\\\]{--un-bg-opacity:1;background-color:hsla(2.7,81.9%,69.6%,var(--un-bg-opacity));}
.hover\\\\:bg-green:hover{--un-bg-opacity:1;background-color:rgba(74,222,128,var(--un-bg-opacity));}
.bg-\\\\[url\\\\(https\\\\:\\\\/\\\\/static\\\\.runoob\\\\.com\\\\/images\\\\/demo\\\\/demo2\\\\.jpg\\\\)\\\\]{--un-url:url(https://static.runoob.com/images/demo/demo2.jpg);background-image:var(--un-url);}
.p-1{padding:0.25rem;}
.p-2{padding:0.5rem;}
.p-2\\\\.5{padding:0.625rem;}
.text-center{text-align:center;}
.text-right{text-align:right;}
.text-sm{font-size:0.875rem;line-height:1.25rem;}
Expand Down
23 changes: 11 additions & 12 deletions test/transformer-attributify.test.ts
Expand Up @@ -50,6 +50,7 @@ describe('transformer-attributify', () => {
"code": "<script setup lang=\\"ts\\">
import { ref } from 'vue'
const bg = 'bg-[hsl(2.7,81.9%,69.6%)]'
const bgIgnore = 'applet-ignore: bg-[hsl(2.7,81.9%,69.6%)]'
const index = 1
const customClass = 'text-red'
const bool = ref<boolean>()
Expand All @@ -59,34 +60,33 @@ describe('transformer-attributify', () => {
<div class=\\"text-center aaa p-4\\" p=\\"4\\">
<div text=\\"4xl\\" class=\\"rotate-180 i-carbon-campsite text-4xl\\" :class=\\"bg\\" />
<div class=\\"border bg-blue-200 font-(light mono) \\">
<div class=\\"hover:(!bg-gray-600 text-white font-medium) text-#fff\\" text=\\"#fff\\">
0123456789
<div class=\\"hover:(!bg-gray-600 text-red font-bold) text-#fff\\" text=\\"#fff\\">
{{ 'applet-ignore: hover:(!bg-gray-600 text-red font-bold)' }}
</div>
</div>
<div :class=\\"{ 'bg-blue': bool }\\" p-2 :hover-class=\\"['!bg-green']\\" class=\\"p-2\\">
<div :class=\\"\`p-2.5 \${bool ? 'p-0.5' : ''}\`\\" m-2 :hover-class=\\"['!bg-green']\\" class=\\"m-2\\">
class=\\"hover:bg-green\\"
</div>
<div flex=\\"~ col gap-1\\" class=\\"p-1 flex flex-col flex-gap-1 items-center\\" items-center :class=\\"bool ? 'text-yellow-500 px-2.5' : ''\\">
<div i-carbon-campsite inline-block color=\\"blue\\" text=\\"xl\\" class=\\"inline-block color-blue text-xl\\"/>
{{ \`index\${index + 1}\` }}
{{ \`index\${index + 1}\` }}{{ \`index\` }}
</div>
<div flex=\\"~ col\\" border=\\"2 blue\\" class=\\"flex flex-col border-2 border-blue\\">
<div text-right h-10 flex=\\"1\\" text=\\"red\\" :class=\\"{ 'text-sm': index > 0 }\\" class=\\"text-right h-10 flex-1 text-red\\">
0123456789
</div>
<div h-10 flex=\\"1\\" text-blue :class=\\"[index > 1 ? 'text' : '']\\" :style=\\"[index > 1 ? '' : '']\\" :type=\\"index > 1\\" class=\\"h-10 flex-1 text-blue\\">
0123456789
{{ bgIgnore }}
</div>
</div>
<div class=\\"bg-[url(https://static.runoob.com/images/demo/demo2.jpg)] w-40 h-20 ma color-white bg-center bg-cover\\" w-40 h-20 ma color=\\"white\\" bg=\\"center cover\\">
bg-img
{{ 'applet-ignore: bg-[url(https://static.runoob.com/images/demo/demo2.jpg)]' }}
</div>
<div class=\\"m-0.5 p-1 text-2xl\\" :class=\\"bool ? '' : 'text-yellow-500 p-2.5'\\">
abckefghijklmnopqrstuvwxyz
</div>
</div>
</template>
",
"css": "/* layer: default */
.m-0\\\\.5{margin:0.125rem;}
Expand All @@ -107,14 +107,13 @@ describe('transformer-attributify', () => {
.border-blue{--un-border-opacity:1;border-color:rgba(96,165,250,var(--un-border-opacity));}
.\\\\!bg-green{--un-bg-opacity:1 !important;background-color:rgba(74,222,128,var(--un-bg-opacity)) !important;}
.bg-\\\\[hsl\\\\(2\\\\.7\\\\,81\\\\.9\\\\%\\\\,69\\\\.6\\\\%\\\\)\\\\]{--un-bg-opacity:1;background-color:hsla(2.7,81.9%,69.6%,var(--un-bg-opacity));}
.bg-blue{--un-bg-opacity:1;background-color:rgba(96,165,250,var(--un-bg-opacity));}
.bg-blue-200{--un-bg-opacity:1;background-color:rgba(191,219,254,var(--un-bg-opacity));}
.hover\\\\:bg-green:hover{--un-bg-opacity:1;background-color:rgba(74,222,128,var(--un-bg-opacity));}
.bg-\\\\[url\\\\(https\\\\:\\\\/\\\\/static\\\\.runoob\\\\.com\\\\/images\\\\/demo\\\\/demo2\\\\.jpg\\\\)\\\\]{--un-url:url(https://static.runoob.com/images/demo/demo2.jpg);background-image:var(--un-url);}
.bg-cover{background-size:cover;}
.bg-center{background-position:center;}
.p-0\\\\.5{padding:0.125rem;}
.p-1{padding:0.25rem;}
.p-2{padding:0.5rem;}
.p-2\\\\.5{padding:0.625rem;}
.p-4{padding:1rem;}
.px-2\\\\.5{padding-left:0.625rem;padding-right:0.625rem;}
Expand All @@ -127,10 +126,10 @@ describe('transformer-attributify', () => {
.color-blue,
.text-blue{--un-text-opacity:1;color:rgba(96,165,250,var(--un-text-opacity));}
.color-white,
.text-\\\\#fff,
.text-white{--un-text-opacity:1;color:rgba(255,255,255,var(--un-text-opacity));}
.text-\\\\#fff{--un-text-opacity:1;color:rgba(255,255,255,var(--un-text-opacity));}
.text-red{--un-text-opacity:1;color:rgba(248,113,113,var(--un-text-opacity));}
.text-yellow-500{--un-text-opacity:1;color:rgba(234,179,8,var(--un-text-opacity));}",
.text-yellow-500{--un-text-opacity:1;color:rgba(234,179,8,var(--un-text-opacity));}
.m-2{margin:0.5rem;}",
}
`)
})
Expand Down

0 comments on commit 4b86da8

Please sign in to comment.