Skip to content

Commit 03dedc1

Browse files
committed
feat: add getOptions method and include exclude option
1 parent 2787375 commit 03dedc1

File tree

7 files changed

+162
-111
lines changed

7 files changed

+162
-111
lines changed

packages/unplugin-tailwindcss-mangle/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@
6868
"@babel/parser": "^7.21.4",
6969
"@babel/traverse": "^7.21.4",
7070
"@babel/types": "^7.21.4",
71-
"acorn-walk": "^8.2.0",
72-
"escodegen": "^2.0.0",
71+
"micromatch": "^4.0.5",
7372
"parse5": "^7.1.2",
7473
"postcss": "^8.4.23",
7574
"postcss-selector-parser": "^6.0.11",
@@ -86,6 +85,7 @@
8685
"@types/babel__generator": "^7.6.4",
8786
"@types/babel__traverse": "^7.18.3",
8887
"@types/escodegen": "^0.0.7",
88+
"@types/micromatch": "^4.0.2",
8989
"@types/semver": "^7.3.13",
9090
"pkg-types": "^1.0.2",
9191
"tailwindcss": "^3.3.1",

packages/unplugin-tailwindcss-mangle/src/defaults.ts

Whitespace-only changes.

packages/unplugin-tailwindcss-mangle/src/index.ts

+83-88
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import { createUnplugin } from 'unplugin'
22
import type { Options } from './types'
33
import { pluginName } from './constants'
4-
import { getClassCacheSet } from 'tailwindcss-patch'
54
import { getGroupedEntries } from './utils'
6-
import { OutputAsset, OutputChunk } from 'rollup'
7-
import ClassGenerator from './classGenerator'
5+
import type { OutputAsset, OutputChunk } from 'rollup'
86
import { htmlHandler } from './html'
97
import { jsHandler } from './js'
108
import { cssHandler } from './css'
119
import type { sources } from 'webpack'
1210
import path from 'path'
1311
import fs from 'fs'
12+
import { getOptions } from './options'
1413

14+
// cache map
1515
const outputCachedMap = new Map<
1616
string,
1717
{
@@ -22,27 +22,7 @@ const outputCachedMap = new Map<
2222
>()
2323

2424
export const unplugin = createUnplugin((options: Options | undefined = {}, meta) => {
25-
const isMangleClass = (className: string) => {
26-
// ignore className like 'filter','container'
27-
// it may be dangerous to mangle/rename all StringLiteral , so use /-/ test for only those with /-/ like:
28-
// bg-[#123456] w-1 etc...
29-
return /[-:]/.test(className)
30-
}
31-
let classSet: Set<string>
32-
// let cached: boolean
33-
const classGenerator = new ClassGenerator(options.classGenerator)
34-
function getCachedClassSet() {
35-
const set = getClassCacheSet()
36-
set.forEach((c) => {
37-
if (!isMangleClass(c)) {
38-
set.delete(c)
39-
}
40-
})
41-
42-
classSet = set
43-
44-
return classSet
45-
}
25+
const { classGenerator, getCachedClassSet, isInclude } = getOptions(options)
4626

4727
return {
4828
name: pluginName,
@@ -58,30 +38,36 @@ export const unplugin = createUnplugin((options: Options | undefined = {}, meta)
5838

5939
if (Array.isArray(groupedEntries.html) && groupedEntries.html.length) {
6040
for (let i = 0; i < groupedEntries.html.length; i++) {
61-
const [, asset] = groupedEntries.html[i] as [string, OutputAsset]
62-
asset.source = htmlHandler(asset.source.toString(), {
63-
classGenerator,
64-
runtimeSet
65-
})
41+
const [file, asset] = groupedEntries.html[i] as [string, OutputAsset]
42+
if (isInclude(file)) {
43+
asset.source = htmlHandler(asset.source.toString(), {
44+
classGenerator,
45+
runtimeSet
46+
})
47+
}
6648
}
6749
}
6850
if (Array.isArray(groupedEntries.js) && groupedEntries.js.length) {
6951
for (let i = 0; i < groupedEntries.js.length; i++) {
70-
const [, chunk] = groupedEntries.js[i] as [string, OutputChunk]
71-
chunk.code = jsHandler(chunk.code, {
72-
runtimeSet,
73-
classGenerator
74-
}).code
52+
const [file, chunk] = groupedEntries.js[i] as [string, OutputChunk]
53+
if (isInclude(file)) {
54+
chunk.code = jsHandler(chunk.code, {
55+
runtimeSet,
56+
classGenerator
57+
}).code
58+
}
7559
}
7660
}
7761

7862
if (Array.isArray(groupedEntries.css) && groupedEntries.css.length) {
7963
for (let i = 0; i < groupedEntries.css.length; i++) {
80-
const [, css] = groupedEntries.css[i] as [string, OutputAsset]
81-
css.source = cssHandler(css.source.toString(), {
82-
classGenerator,
83-
runtimeSet
84-
})
64+
const [file, css] = groupedEntries.css[i] as [string, OutputAsset]
65+
if (isInclude(file)) {
66+
css.source = cssHandler(css.source.toString(), {
67+
classGenerator,
68+
runtimeSet
69+
})
70+
}
8571
}
8672
}
8773
}
@@ -120,7 +106,7 @@ export const unplugin = createUnplugin((options: Options | undefined = {}, meta)
120106
// console.log(compiler.outputPath)
121107
const runtimeSet = getCachedClassSet()
122108
const groupedEntries = getGroupedEntries(Object.entries(assets))
123-
109+
// cache result
124110
if (!runtimeSet.size) {
125111
const css = new Map()
126112
const html = new Map()
@@ -150,75 +136,84 @@ export const unplugin = createUnplugin((options: Options | undefined = {}, meta)
150136
if (groupedEntries.html.length) {
151137
for (let i = 0; i < groupedEntries.html.length; i++) {
152138
const [file, asset] = groupedEntries.html[i]
153-
const html = htmlHandler(asset.source().toString(), {
154-
classGenerator,
155-
runtimeSet
156-
})
157-
const source = new ConcatSource(html)
158-
compilation.updateAsset(file, source)
159-
}
160-
}
161-
outputCachedMap.forEach(({ html }, key) => {
162-
if (html.size) {
163-
html.forEach((asset, file) => {
164-
const html = htmlHandler((asset as sources.Source).source().toString(), {
139+
if (isInclude(file)) {
140+
const html = htmlHandler(asset.source().toString(), {
165141
classGenerator,
166142
runtimeSet
167143
})
168-
overwriteServerSideAsset(key, file, html)
169-
})
170-
html.clear()
144+
const source = new ConcatSource(html)
145+
compilation.updateAsset(file, source)
146+
}
171147
}
172-
})
148+
}
173149

174150
if (groupedEntries.js.length) {
175151
for (let i = 0; i < groupedEntries.js.length; i++) {
176152
const [file, chunk] = groupedEntries.js[i]
177-
const code = jsHandler(chunk.source().toString(), {
178-
runtimeSet,
179-
classGenerator
180-
}).code
181-
const source = new ConcatSource(code)
182-
compilation.updateAsset(file, source)
183-
}
184-
}
185-
186-
outputCachedMap.forEach(({ js }, key) => {
187-
if (js.size) {
188-
js.forEach((chunk, file) => {
189-
const rawCode = (chunk as sources.Source).source().toString()
190-
const code = jsHandler(rawCode, {
153+
if (isInclude(file)) {
154+
const code = jsHandler(chunk.source().toString(), {
191155
runtimeSet,
192156
classGenerator
193157
}).code
194-
195-
overwriteServerSideAsset(key, file, code)
196-
})
197-
js.clear()
158+
const source = new ConcatSource(code)
159+
compilation.updateAsset(file, source)
160+
}
198161
}
199-
})
162+
}
200163

201164
if (groupedEntries.css.length) {
202165
for (let i = 0; i < groupedEntries.css.length; i++) {
203166
const [file, css] = groupedEntries.css[i]
204-
const newCss = cssHandler(css.source().toString(), {
205-
classGenerator,
206-
runtimeSet
207-
})
208-
const source = new ConcatSource(newCss)
209-
compilation.updateAsset(file, source)
167+
if (isInclude(file)) {
168+
const newCss = cssHandler(css.source().toString(), {
169+
classGenerator,
170+
runtimeSet
171+
})
172+
const source = new ConcatSource(newCss)
173+
compilation.updateAsset(file, source)
174+
}
210175
}
211176
}
177+
// overwrite ssr server side chunk
178+
outputCachedMap.forEach(({ js, html, css }, key) => {
179+
if (html.size) {
180+
html.forEach((asset, file) => {
181+
if (isInclude(file)) {
182+
const html = htmlHandler((asset as sources.Source).source().toString(), {
183+
classGenerator,
184+
runtimeSet
185+
})
186+
overwriteServerSideAsset(key, file, html)
187+
}
188+
})
189+
html.clear()
190+
}
191+
192+
if (js.size) {
193+
js.forEach((chunk, file) => {
194+
if (isInclude(file)) {
195+
const rawCode = (chunk as sources.Source).source().toString()
196+
const code = jsHandler(rawCode, {
197+
runtimeSet,
198+
classGenerator
199+
}).code
200+
201+
overwriteServerSideAsset(key, file, code)
202+
}
203+
})
204+
js.clear()
205+
}
212206

213-
outputCachedMap.forEach(({ css }, key) => {
214207
if (css.size) {
215208
css.forEach((style, file) => {
216-
const newCss = cssHandler((style as sources.Source).source().toString(), {
217-
classGenerator,
218-
runtimeSet
219-
})
220-
221-
overwriteServerSideAsset(key, file, newCss)
209+
if (isInclude(file)) {
210+
const newCss = cssHandler((style as sources.Source).source().toString(), {
211+
classGenerator,
212+
runtimeSet
213+
})
214+
215+
overwriteServerSideAsset(key, file, newCss)
216+
}
222217
})
223218
css.clear()
224219
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import type { Options } from './types'
2+
import { getClassCacheSet } from 'tailwindcss-patch'
3+
import ClassGenerator from './classGenerator'
4+
import { createGlobMatcher } from './utils'
5+
6+
export function getOptions(options: Options | undefined = {}) {
7+
const includeMatcher = createGlobMatcher(options.include, true)
8+
const excludeMatcher = createGlobMatcher(options.exclude, false)
9+
10+
function isInclude(file: string) {
11+
return includeMatcher(file) && !excludeMatcher(file)
12+
}
13+
14+
const isMangleClass = (className: string) => {
15+
// ignore className like 'filter','container'
16+
// it may be dangerous to mangle/rename all StringLiteral , so use /-/ test for only those with /-/ like:
17+
// bg-[#123456] w-1 etc...
18+
return /[-:]/.test(className)
19+
}
20+
let classSet: Set<string>
21+
// let cached: boolean
22+
const classGenerator = new ClassGenerator(options.classGenerator)
23+
function getCachedClassSet() {
24+
const set = getClassCacheSet()
25+
set.forEach((c) => {
26+
if (!isMangleClass(c)) {
27+
set.delete(c)
28+
}
29+
})
30+
31+
classSet = set
32+
33+
return classSet
34+
}
35+
36+
return {
37+
getCachedClassSet,
38+
classGenerator,
39+
includeMatcher,
40+
excludeMatcher,
41+
isInclude
42+
}
43+
}

packages/unplugin-tailwindcss-mangle/src/types.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@ export interface IClassGeneratorContextItem {
66
}
77

88
export interface IClassGeneratorOptions {
9-
// classNameRegExp?: string
109
reserveClassName?: (string | RegExp)[]
11-
// ignorePrefix?: string[]
12-
// ignorePrefixRegExp?: string
1310
customGenerate?: (original: string, opts: IClassGeneratorOptions, context: Record<string, any>) => string | undefined
1411
log?: boolean
1512
exclude?: (string | RegExp)[]
@@ -33,4 +30,6 @@ export interface IHandlerOptions {
3330

3431
export interface Options {
3532
classGenerator?: IClassGeneratorOptions
33+
exclude?: string[]
34+
include?: string[]
3635
}

packages/unplugin-tailwindcss-mangle/src/utils.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { IClassGeneratorOptions, IClassGenerator } from './types'
2-
2+
import { isMatch } from 'micromatch'
33
export function groupBy<T>(arr: T[], cb: (arg: T) => string): Record<string, T[]> {
44
if (!Array.isArray(arr)) {
55
throw new Error('expected an array for first argument')
@@ -121,3 +121,14 @@ export function escapeStringRegexp(str: string) {
121121
}
122122
return str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d')
123123
}
124+
125+
export function createGlobMatcher(pattern: string | string[] | undefined, fallbackValue: boolean = false) {
126+
if (typeof pattern === 'undefined') {
127+
return function (file: string) {
128+
return fallbackValue
129+
}
130+
}
131+
return function (file: string) {
132+
return isMatch(file, pattern)
133+
}
134+
}

0 commit comments

Comments
 (0)