Skip to content

Commit 96e6958

Browse files
committed
feat: switch to parser & traverse
1 parent 846505a commit 96e6958

File tree

12 files changed

+245
-115
lines changed

12 files changed

+245
-115
lines changed

packages/core/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,13 @@
5656
"@babel/helper-plugin-utils": "^7.24.7",
5757
"@babel/parser": "^7.24.7",
5858
"@babel/preset-typescript": "^7.24.7",
59+
"@babel/traverse": "^7.24.7",
5960
"@babel/types": "^7.24.7",
6061
"@tailwindcss-mangle/config": "workspace:^",
6162
"@tailwindcss-mangle/shared": "workspace:^",
6263
"@vue/compiler-sfc": "^3.4.29",
6364
"fast-sort": "^3.4.0",
65+
"htmlparser2": "^9.1.0",
6466
"magic-string": "^0.30.10",
6567
"micromatch": "^4.0.7",
6668
"parse5": "^7.1.2",
@@ -70,6 +72,7 @@
7072
"devDependencies": {
7173
"@parse5/tools": "^0.5.0",
7274
"@types/babel__core": "^7.20.5",
75+
"@types/babel__traverse": "^7.20.6",
7376
"@types/micromatch": "^4.0.7",
7477
"@vue/compiler-core": "^3.4.29",
7578
"@vue/compiler-sfc": "^3.4.27"

packages/core/src/babel/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import _babelTraverse from '@babel/traverse'
2+
3+
export { parse, parseExpression } from '@babel/parser'
4+
5+
function _interopDefaultCompat(e: any) {
6+
return e && typeof e === 'object' && 'default' in e ? e.default : e
7+
}
8+
9+
export const traverse = _interopDefaultCompat(_babelTraverse) as typeof _babelTraverse

packages/core/src/ctx/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export class Context {
1818
options: MangleUserConfig
1919
private includeMatcher: (file: string) => boolean
2020
private excludeMatcher: (file: string) => boolean
21-
public replaceMap: Map<string, string>
21+
public replaceMap: Map<string, string | boolean>
2222
classSet: Set<string>
2323

2424
classGenerator: ClassGenerator

packages/core/src/js/pre.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { splitCode } from '@tailwindcss-mangle/shared'
55
import { sort } from 'fast-sort'
66
import { jsStringEscape } from '@ast-core/escape'
77
import type { ParseResult } from '@babel/parser'
8+
// @ts-ignore
9+
import babelTypescript from '@babel/preset-typescript'
810
import { escapeStringRegexp } from '@/utils'
911
import type { Context } from '@/ctx'
1012
import { between } from '@/math'
@@ -44,7 +46,7 @@ export function handleValue(options: HandleValueOptions) {
4446

4547
// replace
4648
const v = replaceMap.get(str)
47-
if (v) {
49+
if (v && typeof v === 'string') {
4850
value = value.replaceAll(str, v)
4951
}
5052
}
@@ -107,7 +109,7 @@ function transformSync(ast: babel.types.Node, code: string, plugins: babel.Plugi
107109
export function loadPresets() {
108110
return [
109111
[
110-
require('@babel/preset-typescript'),
112+
babelTypescript,
111113
{
112114
allExtensions: true,
113115
isTSX: true,
@@ -208,6 +210,7 @@ export function preProcessRawCode(options: IPreProcessJsOptions): string {
208210
for (const regex of ctx.preserveFunctionRegexs) {
209211
const allArr: RegExpExecArray[] = []
210212
let arr: RegExpExecArray | null = null
213+
// eslint-disable-next-line no-cond-assign
211214
while ((arr = regex.exec(magicString.original)) !== null) {
212215
allArr.push(arr)
213216
markArr.push([arr.index, arr.index + arr[0].length])
@@ -254,6 +257,7 @@ export function preProcessRawCode(options: IPreProcessJsOptions): string {
254257
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec
255258
const regex = new RegExp(escapeStringRegexp(key), 'g')
256259
let arr: RegExpExecArray | null = null
260+
// eslint-disable-next-line no-cond-assign
257261
while ((arr = regex.exec(magicString.original)) !== null) {
258262
const start = arr.index
259263
const end = arr.index + arr[0].length
@@ -264,7 +268,7 @@ export function preProcessRawCode(options: IPreProcessJsOptions): string {
264268
break
265269
}
266270
}
267-
if (shouldUpdate) {
271+
if (shouldUpdate && typeof value === 'string') {
268272
magicString.update(start, end, value)
269273
markArr.push([start, end])
270274
}

packages/core/src/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ export interface IJsHandlerOptions extends IHandlerOptions {
2727
minified?: boolean
2828
}
2929

30+
export interface IVueHandlerOptions extends IHandlerOptions {
31+
32+
}
33+
3034
export interface ICssHandlerOptions extends IHandlerOptions {
3135
// scene?: 'loader' | 'process'
3236
ignoreVueScoped?: boolean
@@ -36,5 +40,4 @@ export interface ICssHandlerOptions extends IHandlerOptions {
3640
export interface IPreProcessJsOptions extends IHandlerOptions {
3741
code: string | MagicString
3842
id: string
39-
4043
}

packages/core/src/vue/index.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,34 @@
1-
import { parse } from '@vue/compiler-sfc'
1+
import { parse as parseVue } from '@vue/compiler-sfc'
2+
import MagicString from 'magic-string'
3+
import type { IVueHandlerOptions } from '@/types'
4+
import { parse as parseJs, traverse } from '@/babel'
25

3-
export function vueHandler(raw: string) {
4-
parse(raw)
6+
export function vueHandler(raw: string | MagicString, options: IVueHandlerOptions) {
7+
const { ctx } = options
8+
const ms: MagicString = typeof raw === 'string' ? new MagicString(raw) : raw
9+
const { descriptor } = parseVue(ms.original)
10+
const { template, scriptSetup, script } = descriptor
11+
// console.log(template, scriptSetup, script)
12+
if (scriptSetup) {
13+
const ast = parseJs(scriptSetup.content, {
14+
sourceType: 'module',
15+
plugins: ['typescript'],
16+
})
17+
18+
traverse(ast, {
19+
StringLiteral: {
20+
enter(p) {
21+
22+
},
23+
},
24+
})
25+
}
26+
if (script) {
27+
parseJs(script.content, {
28+
sourceType: 'module',
29+
plugins: ['typescript'],
30+
})
31+
}
32+
33+
return ms.toString()
534
}

packages/core/test/__snapshots__/js.test.ts.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ const aaa = twMerge('px-2 py-1 bg-red hover:bg-dark-red', 'p-3 bg-[#B91C1C]')
259259
</script>
260260
261261
<template>
262-
<main class="flex tw-d tw-f tw-e tw-b tw-j ">
262+
<main class="flex tw-d tw-f tw-e tw-b tw-j " :class="['before:text-[#123456]']">
263263
<nav :class="aaa">aaa</nav>
264264
</main>
265265
</template>
@@ -341,7 +341,7 @@ const aaa = twMerge('px-2 py-1 bg-red-100 hover:bg-red-800', 'p-3 bg-[#B91C1C]')
341341
</script>
342342
343343
<template>
344-
<main class="flex tw-kb tw-hc tw-hb tw-sa tw-vc ">
344+
<main class="flex tw-kb tw-hc tw-hb tw-sa tw-vc " :class="['before:text-[#123456]']">
345345
<nav :class="aaa">{{ aaa }}</nav>
346346
<div class="tw-cd tw-pc tw-dc tw-hb tw-sa tw-xb tw-lc tw-jc">
347347
<p
Lines changed: 131 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,135 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3-
exports[`vue > module mode preamble 1`] = `
4-
"import { createVNode as _createVNode, resolveDirective as _resolveDirective } from "vue"
3+
exports[`vue > test for vue 1`] = `
4+
"<script setup lang="ts">
5+
import { twMerge } from 'tailwind-merge'
6+
// 'hover:bg-dark-red p-3 bg-[#B91C1C]'
7+
const aaa = twMerge('px-2 py-1 bg-red-100 hover:bg-red-800', 'p-3 bg-[#B91C1C]')
58
6-
export function render(_ctx, _cache) {
7-
return null
8-
}"
9+
</script>
10+
11+
<template>
12+
<main class="flex min-h-screen flex-col items-center justify-between p-24 " :class="['before:text-[#123456]']">
13+
<nav :class="aaa">{{ aaa }}</nav>
14+
<div class="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex">
15+
<p
16+
class="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
17+
Get started by editing&nbsp;
18+
<code class="font-mono font-bold">pages/index.tsx</code>
19+
</p>
20+
<div
21+
class="fixed bottom-0 left-0 flex h-48 w-full items-end justify-center bg-gradient-to-t from-white via-white dark:from-black dark:via-black lg:static lg:h-auto lg:w-auto lg:bg-none">
22+
<a class="pointer-events-none flex place-items-center gap-2 p-8 lg:pointer-events-auto lg:p-0"
23+
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
24+
target="_blank" rel="noopener noreferrer">
25+
By
26+
<img src="/vite.svg" alt="Vercel Logo" class="dark:invert" priority />
27+
</a>
28+
</div>
29+
</div>
30+
31+
<div
32+
class="relative flex place-items-center before:absolute before:h-[300px] before:w-[480px] before:-translate-x-1/2 before:rounded-full before:bg-gradient-radial before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-[240px] after:translate-x-1/3 after:bg-gradient-conic after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700/10 after:dark:from-sky-900 after:dark:via-[#0141ff]/40 before:lg:h-[360px]">
33+
<img class="relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert" src="./assets/vue.svg" alt="Next.js Logo"
34+
priority />
35+
</div>
36+
37+
<div class="mb-32 grid text-center lg:mb-0 lg:grid-cols-4 lg:text-left ">
38+
<a href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
39+
class="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
40+
target="_blank" rel="noopener noreferrer">
41+
<h2 class="bg-red-200 mb-3 text-2xl font-semibold">
42+
Docs <span
43+
class="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">-&gt;</span>
44+
</h2>
45+
<p class="m-0 max-w-[30ch] text-sm opacity-50">Find in-depth information about Next.js
46+
features and API.</p>
47+
</a>
48+
49+
<a href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
50+
class="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
51+
target="_blank" rel="noopener noreferrer">
52+
<h2 class="bg-red-200/40 mb-3 text-2xl font-semibold">
53+
Learn <span
54+
class="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">-&gt;</span>
55+
</h2>
56+
<p class="m-0 max-w-[30ch] text-sm opacity-50">Learn about Next.js in an interactive
57+
course with&nbsp;quizzes!</p>
58+
</a>
59+
60+
<a href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
61+
class="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
62+
target="_blank" rel="noopener noreferrer">
63+
<h2 class="bg-red-200/70 mb-3 text-2xl font-semibold">
64+
Templates <span
65+
class="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">-&gt;</span>
66+
</h2>
67+
<p class="m-0 max-w-[30ch] text-sm opacity-50">Discover and deploy boilerplate example
68+
Next.js&nbsp;projects.</p>
69+
</a>
70+
71+
<a href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
72+
class="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
73+
target="_blank" rel="noopener noreferrer">
74+
<h2 class="bg-red-200/90 mb-3 text-2xl font-semibold">
75+
Deploy <span
76+
class="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">-&gt;</span>
77+
</h2>
78+
<p class="m-0 max-w-[30ch] text-sm opacity-50">Instantly deploy your Next.js site to a
79+
shareable URL with Vercel.</p>
80+
</a>
81+
</div>
82+
</main>
83+
</template>
84+
85+
<script lang="ts">
86+
import { onMounted, defineComponent } from 'vue'
87+
88+
export default defineComponent({
89+
setup() {
90+
onMounted(() => {
91+
const clipPath = '';
92+
document.documentElement.animate(
93+
{
94+
clipPath
95+
},
96+
{
97+
duration: 500,
98+
easing: /* tw-mangle ignore */ 'ease-out',
99+
pseudoElement: '::view-transition-new(root)'
100+
})
101+
document.documentElement.animate(
102+
{
103+
clipPath
104+
},
105+
{
106+
duration: 500,
107+
easing: 'ease-out',
108+
pseudoElement: '::view-transition-new(root)'
109+
}
110+
)
111+
})
112+
return {}
113+
}
114+
})
115+
116+
</script>
117+
118+
<style scoped>
119+
.logo {
120+
height: 6em;
121+
padding: 1.5em;
122+
will-change: filter;
123+
transition: filter 300ms;
124+
}
125+
126+
.logo:hover {
127+
filter: drop-shadow(0 0 2em #646cffaa);
128+
}
129+
130+
.logo.vue:hover {
131+
filter: drop-shadow(0 0 2em #42b883aa);
132+
}
133+
</style>
134+
"
9135
`;

packages/core/test/fixtures/preserve-fn-case0.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const aaa = twMerge('px-2 py-1 bg-red hover:bg-dark-red', 'p-3 bg-[#B91C1C]')
66
</script>
77

88
<template>
9-
<main class="flex min-h-screen flex-col items-center justify-between p-24 ">
9+
<main class="flex min-h-screen flex-col items-center justify-between p-24 " :class="['before:text-[#123456]']">
1010
<nav :class="aaa">aaa</nav>
1111
</main>
1212
</template>

packages/core/test/fixtures/preserve-fn-case1.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const aaa = twMerge('px-2 py-1 bg-red-100 hover:bg-red-800', 'p-3 bg-[#B91C1C]')
66
</script>
77

88
<template>
9-
<main class="flex min-h-screen flex-col items-center justify-between p-24 ">
9+
<main class="flex min-h-screen flex-col items-center justify-between p-24 " :class="['before:text-[#123456]']">
1010
<nav :class="aaa">{{ aaa }}</nav>
1111
<div class="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex">
1212
<p

0 commit comments

Comments
 (0)