From 0f5ab7461eae5728cc88cfda35c97d046acdb591 Mon Sep 17 00:00:00 2001 From: ice breaker Date: Thu, 10 Aug 2023 01:04:18 +0800 Subject: [PATCH] Feature/add more test case (#40) * chore: commit js case * refact: type and plugin * feat: use config to control plugin * chore: allow test for tailwindcss patch * chore: add ci codecov --- .github/workflows/ci.yml | 6 + .github/workflows/release.yml | 2 + packages/config/package.json | 5 +- packages/config/src/defaults.ts | 21 +++- packages/config/src/types.ts | 19 +++ .../test/__snapshots__/defaults.test.ts.snap | 17 +++ packages/config/test/index.test.ts | 5 +- packages/core/package.json | 2 +- packages/core/src/types.ts | 20 +-- packages/core/test/fixtures/prod/0.js | 11 ++ packages/core/test/fixtures/prod/1.js | 11 ++ packages/shared/package.json | 4 +- packages/tailwindcss-patch/package.json | 6 +- .../tailwindcss-patch/test/config.test.ts | 5 +- packages/tailwindcss-patch/vitest.setup.ts | 4 - .../unplugin-tailwindcss-mangle/package.json | 9 +- .../src/constants.ts | 2 - .../src/core/context.ts | 85 +++++++++++++ .../src/core/index.ts | 2 +- .../src/core/options.ts | 114 ------------------ .../src/core/plugin.ts | 76 +++++++----- .../unplugin-tailwindcss-mangle/src/nuxt.ts | 8 +- .../unplugin-tailwindcss-mangle/src/types.ts | 17 +-- .../unplugin-tailwindcss-mangle/src/utils.ts | 23 +--- .../test/pre.test.ts | 18 +-- .../vitest.setup.ts | 4 - pnpm-lock.yaml | 3 + vitest.workspace.ts | 2 +- 28 files changed, 261 insertions(+), 240 deletions(-) create mode 100644 packages/core/test/fixtures/prod/0.js create mode 100644 packages/core/test/fixtures/prod/1.js create mode 100644 packages/unplugin-tailwindcss-mangle/src/core/context.ts delete mode 100644 packages/unplugin-tailwindcss-mangle/src/core/options.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 47bacfd..931dff4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,5 +42,11 @@ jobs: - name: Build run: pnpm build + - name: Patch + working-directory: packages/tailwindcss-patch + run: pnpm run patch + - name: Test run: pnpm test + + - uses: codecov/codecov-action@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0546269..64fb122 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,3 +39,5 @@ jobs: # NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + + - uses: codecov/codecov-action@v3 \ No newline at end of file diff --git a/packages/config/package.json b/packages/config/package.json index 7ba8321..cb85695 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,6 +1,6 @@ { "name": "@tailwindcss-mangle/config", - "version": "1.0.1", + "version": "2.0.4", "description": "The config and load function of tailwindcss-mangle", "exports": { ".": { @@ -25,7 +25,7 @@ ], "scripts": { "build": "unbuild", - "test": "vitest run", + "test": "vitest run --coverage.enabled", "test:dev": "vitest" }, "keywords": [ @@ -44,6 +44,7 @@ "registry": "https://registry.npmjs.org/" }, "dependencies": { + "@tailwindcss-mangle/shared": "workspace:^", "c12": "^1.4.2", "dedent": "^1.5.1" }, diff --git a/packages/config/src/defaults.ts b/packages/config/src/defaults.ts index 5bf262e..a2098cc 100644 --- a/packages/config/src/defaults.ts +++ b/packages/config/src/defaults.ts @@ -1,4 +1,5 @@ -import type { PatchUserConfig, UserConfig } from './types' +import { defaultMangleClassFilter } from '@tailwindcss-mangle/shared' +import type { PatchUserConfig, UserConfig, MangleUserConfig } from './types' export function getDefaultPatchConfig(): PatchUserConfig { return { @@ -11,8 +12,24 @@ export function getDefaultPatchConfig(): PatchUserConfig { } } +export function getDefaultMangleUserConfig(): MangleUserConfig { + return { + mangleClassFilter: defaultMangleClassFilter, + include: ['**/*.{js,jsx,ts,tsx,svelte,vue}'], + exclude: ['node_modules/**/*', '**/*.{css,scss,less,sass,postcss,html,htm}'], + disabled: process.env.NODE_ENV === 'development', + classListPath: '.tw-patch/tw-class-list.json', + classMapOutput: { + enable: false, + filename: '.tw-patch/tw-map-list.json', + loose: true + } + } +} + export function getDefaultUserConfig(): UserConfig { return { - patch: getDefaultPatchConfig() + patch: getDefaultPatchConfig(), + mangle: getDefaultMangleUserConfig() } } diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts index cfae307..398d8d2 100644 --- a/packages/config/src/types.ts +++ b/packages/config/src/types.ts @@ -1,3 +1,21 @@ +import type { IClassGeneratorOptions } from '@tailwindcss-mangle/shared' + +export interface ClassMapOutputOptions { + enable?: boolean + filename?: string + loose?: boolean +} + +export interface MangleUserConfig { + mangleClassFilter?: (className: string) => boolean + classGenerator?: IClassGeneratorOptions + exclude?: string[] + include?: string[] + classListPath?: string + classMapOutput?: ClassMapOutputOptions + disabled?: boolean +} + export interface PatchUserConfig { output?: { filename?: string @@ -16,4 +34,5 @@ export interface PatchUserConfig { export interface UserConfig { patch?: PatchUserConfig + mangle?: MangleUserConfig } diff --git a/packages/config/test/__snapshots__/defaults.test.ts.snap b/packages/config/test/__snapshots__/defaults.test.ts.snap index 98eebb6..25cf33d 100644 --- a/packages/config/test/__snapshots__/defaults.test.ts.snap +++ b/packages/config/test/__snapshots__/defaults.test.ts.snap @@ -13,6 +13,23 @@ exports[`defaults > getDefaultPatchConfig 1`] = ` exports[`defaults > getDefaultUserConfig 1`] = ` { + "mangle": { + "classListPath": ".tw-patch/tw-class-list.json", + "classMapOutput": { + "enable": false, + "filename": ".tw-patch/tw-map-list.json", + "loose": true, + }, + "disabled": false, + "exclude": [ + "node_modules/**/*", + "**/*.{css,scss,less,sass,postcss,html,htm}", + ], + "include": [ + "**/*.{js,jsx,ts,tsx,svelte,vue}", + ], + "mangleClassFilter": [Function], + }, "patch": { "output": { "filename": ".tw-patch/tw-class-list.json", diff --git a/packages/config/test/index.test.ts b/packages/config/test/index.test.ts index a96030f..8a9154d 100644 --- a/packages/config/test/index.test.ts +++ b/packages/config/test/index.test.ts @@ -3,7 +3,7 @@ import { existsSync } from 'node:fs' import { deleteAsync } from 'del' import { fixturesRoot } from './utils' import { initConfig, getConfig } from '@/index' -import { getDefaultUserConfig } from '@/defaults' +import { getDefaultUserConfig, getDefaultMangleUserConfig } from '@/defaults' import { configName } from '@/constants' describe('config', () => { @@ -34,7 +34,8 @@ describe('config', () => { tailwindcss: { cwd: 'aaa/bbb/cc' } - } + }, + mangle: getDefaultMangleUserConfig() }) }) }) diff --git a/packages/core/package.json b/packages/core/package.json index 477fdd7..105af0c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@tailwindcss-mangle/core", - "version": "2.0.2", + "version": "2.0.4", "description": "The core of tailwindcss-mangle", "exports": { ".": { diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index f14ae6e..a8ba668 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,4 +1,4 @@ -import { ClassGenerator } from './shared' +import type { ClassGenerator } from './shared' export interface IClassGeneratorContextItem { name: string @@ -32,21 +32,3 @@ export interface ICssHandlerOptions extends IHandlerOptions { ignoreVueScoped?: boolean file?: string } - -export interface ClassSetOutputOptions { - filename: string - dir?: string - type: 'all' | 'partial' -} - -export interface ClassMapOutputOptions { - filename: string - dir?: string -} -export interface Options { - classGenerator?: IClassGeneratorOptions - exclude?: string[] - include?: string[] - classSetOutput?: boolean | ClassSetOutputOptions - classMapOutput?: boolean | ClassMapOutputOptions -} diff --git a/packages/core/test/fixtures/prod/0.js b/packages/core/test/fixtures/prod/0.js new file mode 100644 index 0000000..a27b003 --- /dev/null +++ b/packages/core/test/fixtures/prod/0.js @@ -0,0 +1,11 @@ +// Unterminated string constant +if (isSvg) { + let source = serializeString(svgNode); + // add xml declaration + source = '\r\n' + source; + // convert svg source to URI data scheme. + url = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(source); + saveAs(url, "graph.svg"); + onAlreadySerialized(); + return; +} \ No newline at end of file diff --git a/packages/core/test/fixtures/prod/1.js b/packages/core/test/fixtures/prod/1.js new file mode 100644 index 0000000..6160807 --- /dev/null +++ b/packages/core/test/fixtures/prod/1.js @@ -0,0 +1,11 @@ +// Missing semicolon. +if (typeof parent === 'string') { + parent = d.querySelectorAll(config.parent); + if (parent.length > 1) { + console.warn('WARNING: the given `parent` query(' + config.parent + ') matched more than one element, the first one will be used'); + } + if (parent.length === 0) { + throw 'ERROR: the given `parent` doesn\'t exists!'; + } + parent = parent[0]; +} \ No newline at end of file diff --git a/packages/shared/package.json b/packages/shared/package.json index fad3ae5..cb9211c 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@tailwindcss-mangle/shared", - "version": "2.0.0", + "version": "2.0.4", "description": "The shared utils of tailwindcss-mangle", "exports": { ".": { @@ -25,7 +25,7 @@ ], "scripts": { "build": "unbuild", - "test": "vitest run", + "test": "vitest run --coverage.enabled", "test:dev": "vitest" }, "keywords": [ diff --git a/packages/tailwindcss-patch/package.json b/packages/tailwindcss-patch/package.json index e1edabe..f315877 100644 --- a/packages/tailwindcss-patch/package.json +++ b/packages/tailwindcss-patch/package.json @@ -1,6 +1,6 @@ { "name": "tailwindcss-patch", - "version": "2.0.3", + "version": "2.0.4", "description": "patch tailwindcss for exposing context and extract classes", "exports": { ".": { @@ -32,7 +32,7 @@ "build": "unbuild", "test": "vitest run --coverage.enabled", "test:dev": "vitest", - "cli": "node bin/tw-patch.js install" + "patch": "node bin/tw-patch.js install" }, "keywords": [ "tailwindcss", @@ -51,7 +51,7 @@ }, "peerDependenciesMeta": { "tailwindcss": { - "optional": false + "optional": true } }, "devDependencies": { diff --git a/packages/tailwindcss-patch/test/config.test.ts b/packages/tailwindcss-patch/test/config.test.ts index 408f17e..edf0868 100644 --- a/packages/tailwindcss-patch/test/config.test.ts +++ b/packages/tailwindcss-patch/test/config.test.ts @@ -2,7 +2,7 @@ import { resolve } from 'node:path' import { existsSync } from 'node:fs' import { deleteAsync } from 'del' import { fixturesRoot } from './utils' -import { initConfig, getConfig, getDefaultUserConfig } from '@/core/config' +import { initConfig, getConfig, getDefaultUserConfig, getDefaultMangleUserConfig } from '@/core/config' describe('config', () => { it('0.default', async () => { @@ -32,7 +32,8 @@ describe('config', () => { tailwindcss: { cwd: 'aaa/bbb/cc' } - } + }, + mangle: getDefaultMangleUserConfig() }) }) }) diff --git a/packages/tailwindcss-patch/vitest.setup.ts b/packages/tailwindcss-patch/vitest.setup.ts index 4b03bbc..e69de29 100644 --- a/packages/tailwindcss-patch/vitest.setup.ts +++ b/packages/tailwindcss-patch/vitest.setup.ts @@ -1,4 +0,0 @@ -// import 'tailwindcss-patch/cli' -import { TailwindcssPatcher } from './dist' -const twPatcher = new TailwindcssPatcher() -twPatcher.patch() diff --git a/packages/unplugin-tailwindcss-mangle/package.json b/packages/unplugin-tailwindcss-mangle/package.json index 073ed0a..b52a170 100644 --- a/packages/unplugin-tailwindcss-mangle/package.json +++ b/packages/unplugin-tailwindcss-mangle/package.json @@ -1,6 +1,6 @@ { "name": "unplugin-tailwindcss-mangle", - "version": "2.0.2", + "version": "2.0.4", "description": "mangle tailwindcss utilities class plugin. support vite and webpack!", "main": "./dist/index.cjs", "module": "./dist/index.mjs", @@ -36,6 +36,11 @@ "require": "./dist/nuxt.cjs", "import": "./dist/nuxt.mjs" }, + "./utils": { + "types": "./dist/utils.d.ts", + "require": "./dist/utils.cjs", + "import": "./dist/utils.mjs" + }, "./*": "./*" }, "typesVersions": { @@ -48,7 +53,7 @@ }, "scripts": { "build": "unbuild", - "test": "vitest run", + "test": "vitest run --coverage.enabled", "test:dev": "vitest" }, "files": [ diff --git a/packages/unplugin-tailwindcss-mangle/src/constants.ts b/packages/unplugin-tailwindcss-mangle/src/constants.ts index ed51d30..3b32348 100644 --- a/packages/unplugin-tailwindcss-mangle/src/constants.ts +++ b/packages/unplugin-tailwindcss-mangle/src/constants.ts @@ -1,3 +1 @@ export const pluginName = 'unplugin-tailwindcss-mangle' - -export const webpackCacheKey = pluginName + '-sources-cache-key' diff --git a/packages/unplugin-tailwindcss-mangle/src/core/context.ts b/packages/unplugin-tailwindcss-mangle/src/core/context.ts new file mode 100644 index 0000000..8216ebf --- /dev/null +++ b/packages/unplugin-tailwindcss-mangle/src/core/context.ts @@ -0,0 +1,85 @@ +import fs from 'node:fs' +import { resolve } from 'node:path' +import { ClassGenerator } from '@tailwindcss-mangle/shared' +import { getConfig, getDefaultMangleUserConfig } from '@tailwindcss-mangle/config' +import type { MangleUserConfig } from '@tailwindcss-mangle/config' +import { sort } from 'fast-sort' +import defu from 'defu' +import { createGlobMatcher, defaultMangleClassFilter } from '@/utils' + +export class Context { + options: MangleUserConfig + includeMatcher: (file: string) => boolean + excludeMatcher: (file: string) => boolean + classSet: Set + replaceMap: Map + classGenerator: ClassGenerator + constructor(opts: MangleUserConfig) { + this.options = defu(opts, getDefaultMangleUserConfig()) + this.classSet = new Set() + this.replaceMap = new Map() + this.includeMatcher = createGlobMatcher(this.options.include, true) + this.excludeMatcher = createGlobMatcher(this.options.exclude, false) + this.classGenerator = new ClassGenerator(this.options.classGenerator) + } + + mergeOptions(opts?: MangleUserConfig) { + // 插件选项优先 + this.options = defu(this.options, opts) + this.includeMatcher = createGlobMatcher(this.options.include, true) + this.excludeMatcher = createGlobMatcher(this.options.exclude, false) + this.classGenerator = new ClassGenerator(this.options.classGenerator) + } + + isInclude(file: string) { + return this.includeMatcher(file) && !this.excludeMatcher(file) + } + + currentMangleClassFilter(className: string) { + return (this.options.mangleClassFilter ?? defaultMangleClassFilter)(className) + } + + getClassSet() { + return this.classSet + } + + getReplaceMap() { + return this.replaceMap + } + + addToUsedBy(key: string, file: string) { + const hit = this.classGenerator.newClassMap[key] + if (hit) { + hit.usedBy.add(file) + } + } + + async initConfig() { + const { config } = await getConfig() + const mangleConfig = config?.mangle + this.mergeOptions(mangleConfig) + const jsonPath = this.options.classListPath ?? resolve(process.cwd(), config?.patch?.output?.filename as string) + + if (jsonPath && fs.existsSync(jsonPath)) { + const rawClassList = fs.readFileSync(jsonPath, 'utf8') + const list = JSON.parse(rawClassList) as string[] + // why? + // case bg-red-500 and bg-red-500/50 + // transform bg-red-500/50 first + const classList = sort(list).desc((c) => c.length) + for (const className of classList) { + if (this.currentMangleClassFilter(className)) { + this.classSet.add(className) + } + } + } + for (const cls of this.classSet) { + this.classGenerator.generateClassName(cls) + } + + for (const x of Object.entries(this.classGenerator.newClassMap)) { + this.replaceMap.set(x[0], x[1].name) + } + return config + } +} diff --git a/packages/unplugin-tailwindcss-mangle/src/core/index.ts b/packages/unplugin-tailwindcss-mangle/src/core/index.ts index 5ca6fde..7a41162 100644 --- a/packages/unplugin-tailwindcss-mangle/src/core/index.ts +++ b/packages/unplugin-tailwindcss-mangle/src/core/index.ts @@ -1,2 +1,2 @@ export * from './plugin' -export * from './options' +export * from './context' diff --git a/packages/unplugin-tailwindcss-mangle/src/core/options.ts b/packages/unplugin-tailwindcss-mangle/src/core/options.ts deleted file mode 100644 index 598ad85..0000000 --- a/packages/unplugin-tailwindcss-mangle/src/core/options.ts +++ /dev/null @@ -1,114 +0,0 @@ -import fs from 'node:fs' -import { resolve } from 'node:path' -import { ClassGenerator } from '@tailwindcss-mangle/shared' -import { getConfig, getDefaultUserConfig } from '@tailwindcss-mangle/config' -import type { UserConfig } from '@tailwindcss-mangle/config' -import defu from 'defu' -import { sort } from 'fast-sort' -import { htmlHandler, cssHandler, jsHandler, preProcessJs } from '@tailwindcss-mangle/core' -import type { Options, ClassMapOutputOptions } from '@/types' -import { createGlobMatcher, defaultMangleClassFilter } from '@/utils' - -export function getOptions(opts: Options | undefined = {}) { - const { - include, - exclude, - disabled, - mangleClassFilter, - classMapOutput, - classGenerator: classGeneratorOptions, - classListPath: _classListPath - } = defu(opts, { - include: ['**/*.{js,jsx,ts,tsx,svelte,vue}'], - exclude: ['**/*.{css,scss,less,sass,postcss,html,htm}'], - disabled: process.env.NODE_ENV === 'development' - }) - const includeMatcher = createGlobMatcher(include, true) - const excludeMatcher = createGlobMatcher(exclude, false) - const currentMangleClassFilter = mangleClassFilter ?? defaultMangleClassFilter - - function isInclude(file: string) { - return includeMatcher(file) && !excludeMatcher(file) - } - - const classSet: Set = new Set() - const replaceMap: Map = new Map() - let userConfig: UserConfig = getDefaultUserConfig() - - const classMapOutputOptions: ClassMapOutputOptions = { - filename: 'classMap.json' - } - - if (typeof classMapOutput === 'object') { - Object.assign(classMapOutputOptions, classMapOutput) - } - - // let cached: boolean - const classGenerator = new ClassGenerator(classGeneratorOptions) - - function getCachedClassSet() { - return classSet - } - - function getReplaceMap() { - return replaceMap - } - - async function initConfig() { - const { config } = await getConfig() - userConfig = config as UserConfig - let classListPath: string = '' - if (userConfig) { - classListPath = resolve(process.cwd(), userConfig.patch?.output?.filename as string) - } - if (_classListPath) { - classListPath = _classListPath - } - - if (classListPath && fs.existsSync(classListPath)) { - const rawClassList = fs.readFileSync(classListPath, 'utf8') - const list = JSON.parse(rawClassList) as string[] - // why? - // case bg-red-500 and bg-red-500/50 - // transform bg-red-500/50 first - const classList = sort(list).desc((c) => c.length) - for (const className of classList) { - if (currentMangleClassFilter(className)) { - classSet.add(className) - } - } - } - for (const cls of classSet) { - classGenerator.generateClassName(cls) - } - - for (const x of Object.entries(classGenerator.newClassMap)) { - replaceMap.set(x[0], x[1].name) - } - return config - } - - function addToUsedBy(key: string, file: string) { - const hit = classGenerator.newClassMap[key] - if (hit) { - hit.usedBy.add(file) - } - } - - return { - getCachedClassSet, - classGenerator, - includeMatcher, - excludeMatcher, - isInclude, - classMapOutputOptions, - initConfig, - getReplaceMap, - addToUsedBy, - disabled, - htmlHandler, - cssHandler, - jsHandler, - preProcessJs - } -} diff --git a/packages/unplugin-tailwindcss-mangle/src/core/plugin.ts b/packages/unplugin-tailwindcss-mangle/src/core/plugin.ts index 91bb897..f25a15d 100644 --- a/packages/unplugin-tailwindcss-mangle/src/core/plugin.ts +++ b/packages/unplugin-tailwindcss-mangle/src/core/plugin.ts @@ -1,15 +1,16 @@ +import { dirname } from 'node:path' +import fs from 'node:fs/promises' import { createUnplugin } from 'unplugin' import type { OutputAsset } from 'rollup' -import { getOptions } from './options' -import type { Options } from '@/types' +import { htmlHandler, cssHandler, jsHandler, preProcessJs } from '@tailwindcss-mangle/core' +import type { ClassMapOutputOptions, MangleUserConfig } from '@tailwindcss-mangle/config' +import { Context } from './context' import { pluginName } from '@/constants' -import { cacheDump, getGroupedEntries } from '@/utils' -export { defaultMangleClassFilter } from '@tailwindcss-mangle/shared' +import { ensureDir, getGroupedEntries } from '@/utils' -export const unplugin = createUnplugin((options: Options | undefined = {}) => { - const { isInclude, initConfig, getReplaceMap, classGenerator, addToUsedBy, classMapOutputOptions, disabled, htmlHandler, cssHandler, jsHandler, preProcessJs } = - getOptions(options) - if (disabled) { +export const unplugin = createUnplugin((options: MangleUserConfig = {}) => { + const ctx = new Context(options) + if (ctx.options.disabled) { return { name: pluginName } @@ -18,19 +19,19 @@ export const unplugin = createUnplugin((options: Options | undefined = {}) => { name: pluginName, enforce: 'pre', async buildStart() { - await initConfig() + await ctx.initConfig() }, transformInclude(id) { - return isInclude(id) + return ctx.isInclude(id) }, transform(code, id) { - const replaceMap = getReplaceMap() + const replaceMap = ctx.getReplaceMap() // 直接忽略 css 文件,因为此时 tailwindcss 还没有展开 - if (id.endsWith('.js') || id.endsWith('.ts') || id.endsWith('.tsx') || id.endsWith('.jsx')) { + if (/\.[jt]sx?$/.test(id)) { const str = preProcessJs({ code, replaceMap, - addToUsedBy, + addToUsedBy: ctx.addToUsedBy.bind(ctx), id }) return str @@ -45,7 +46,7 @@ export const unplugin = createUnplugin((options: Options | undefined = {}) => { vite: { generateBundle: { async handler(options, bundle) { - const replaceMap = getReplaceMap() + const replaceMap = ctx.getReplaceMap() const groupedEntries = getGroupedEntries(Object.entries(bundle)) if (Array.isArray(groupedEntries.css) && groupedEntries.css.length > 0) { @@ -55,7 +56,7 @@ export const unplugin = createUnplugin((options: Options | undefined = {}) => { const { css } = await cssHandler(cssSource.source.toString(), { file, replaceMap, - classGenerator + classGenerator: ctx.classGenerator }) cssSource.source = css } @@ -74,7 +75,7 @@ export const unplugin = createUnplugin((options: Options | undefined = {}) => { stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE }, async (assets) => { - const replaceMap = getReplaceMap() + const replaceMap = ctx.getReplaceMap() const groupedEntries = getGroupedEntries(Object.entries(assets)) if (groupedEntries.js.length > 0) { @@ -83,7 +84,7 @@ export const unplugin = createUnplugin((options: Options | undefined = {}) => { const code = jsHandler(chunk.source().toString(), { replaceMap, - classGenerator + classGenerator: ctx.classGenerator }).code if (code) { const source = new ConcatSource(code) @@ -99,7 +100,7 @@ export const unplugin = createUnplugin((options: Options | undefined = {}) => { const { css } = await cssHandler(cssSource.source().toString(), { replaceMap, file, - classGenerator + classGenerator: ctx.classGenerator }) const source = new ConcatSource(css) @@ -113,7 +114,7 @@ export const unplugin = createUnplugin((options: Options | undefined = {}) => { const [file, asset] = groupedEntries.html[i] const html = htmlHandler(asset.source().toString(), { - classGenerator, + classGenerator: ctx.classGenerator, replaceMap }) const source = new ConcatSource(html) @@ -124,20 +125,29 @@ export const unplugin = createUnplugin((options: Options | undefined = {}) => { ) }) }, - writeBundle() { - const entries = Object.entries(classGenerator.newClassMap) - if (entries.length > 0 && classMapOutputOptions) { - cacheDump( - classMapOutputOptions.filename, - entries.map((x) => { - return { - origin: x[0], - replacement: x[1].name, - usedBy: [...x[1].usedBy] - } - }), - classMapOutputOptions.dir - ) + async writeBundle() { + if (ctx.options.classMapOutput?.enable) { + const opts = ctx.options.classMapOutput as Required + const entries = Object.entries(ctx.classGenerator.newClassMap) + if (entries.length > 0 && opts) { + await ensureDir(dirname(opts.filename)) + await fs.writeFile( + opts.filename, + JSON.stringify( + entries.map((x) => { + return { + origin: x[0], + replacement: x[1].name, + usedBy: [...x[1].usedBy] + } + }), + null, + 2 + ), + 'utf8' + ) + console.log(`✨ ${opts.filename} generated!`) + } } } } diff --git a/packages/unplugin-tailwindcss-mangle/src/nuxt.ts b/packages/unplugin-tailwindcss-mangle/src/nuxt.ts index 4d8e55b..be04d16 100644 --- a/packages/unplugin-tailwindcss-mangle/src/nuxt.ts +++ b/packages/unplugin-tailwindcss-mangle/src/nuxt.ts @@ -1,15 +1,15 @@ -import type { Options } from './types' +import type { MangleUserConfig } from './types' import { unplugin } from './core' -export default function (options: Options = {}, nuxt: any) { +export default function (options: MangleUserConfig = {}, nuxt: any) { // install webpack plugin - nuxt.hook('webpack:config', async (config: any) => { + nuxt.hook('webpack:config', (config: any) => { config.plugins = config.plugins || [] config.plugins.unshift(unplugin.webpack(options)) }) // install vite plugin - nuxt.hook('vite:extendConfig', async (config: any) => { + nuxt.hook('vite:extendConfig', (config: any) => { config.plugins = config.plugins || [] config.plugins.push(unplugin.vite(options)) }) diff --git a/packages/unplugin-tailwindcss-mangle/src/types.ts b/packages/unplugin-tailwindcss-mangle/src/types.ts index e513ed3..aa67f5f 100644 --- a/packages/unplugin-tailwindcss-mangle/src/types.ts +++ b/packages/unplugin-tailwindcss-mangle/src/types.ts @@ -1,16 +1 @@ -import type { IClassGeneratorOptions } from '@tailwindcss-mangle/shared' - -export interface ClassMapOutputOptions { - filename: string - dir?: string -} - -export interface Options { - mangleClassFilter?: (className: string) => boolean - classGenerator?: IClassGeneratorOptions - exclude?: string[] - include?: string[] - classListPath?: string - classMapOutput?: boolean | ClassMapOutputOptions - disabled?: boolean -} +export type * from '@tailwindcss-mangle/config' diff --git a/packages/unplugin-tailwindcss-mangle/src/utils.ts b/packages/unplugin-tailwindcss-mangle/src/utils.ts index c57f6c0..7ac7bd8 100644 --- a/packages/unplugin-tailwindcss-mangle/src/utils.ts +++ b/packages/unplugin-tailwindcss-mangle/src/utils.ts @@ -1,4 +1,4 @@ -import fs from 'node:fs' +import fs from 'node:fs/promises' import path from 'node:path' import micromatch from 'micromatch' @@ -62,25 +62,14 @@ export function getCacheDir(basedir = process.cwd()) { return path.resolve(basedir, 'node_modules/.cache', pluginName) } -export function mkCacheDirectory(cwd = process.cwd()) { - const cacheDirectory = getCacheDir(cwd) - - const exists = fs.existsSync(cacheDirectory) - if (!exists) { - fs.mkdirSync(cacheDirectory, { +export async function ensureDir(p: string) { + try { + await fs.access(p) + } catch { + await fs.mkdir(p, { recursive: true }) } - return cacheDirectory -} - -export function cacheDump(filename: string, data: any[] | Set, basedir?: string) { - try { - const dir = mkCacheDirectory(basedir) - fs.writeFileSync(path.resolve(dir, filename), JSON.stringify([...data], undefined, 2), 'utf8') - } catch (error) { - console.log(error) - } } export { defaultMangleClassFilter, isMap, isRegexp } from '@tailwindcss-mangle/shared' diff --git a/packages/unplugin-tailwindcss-mangle/test/pre.test.ts b/packages/unplugin-tailwindcss-mangle/test/pre.test.ts index e812547..bb03e99 100644 --- a/packages/unplugin-tailwindcss-mangle/test/pre.test.ts +++ b/packages/unplugin-tailwindcss-mangle/test/pre.test.ts @@ -1,41 +1,41 @@ import path from 'node:path' import fs from 'node:fs/promises' import { preProcessJs } from '@tailwindcss-mangle/core' -import { getOptions } from '@/core/options' +import { Context } from '@/core/context' const fixturesRoot = path.resolve(__dirname, './fixtures') const tsxRoot = path.resolve(fixturesRoot, './tsx') const tsRoot = path.resolve(fixturesRoot, './ts') describe('babel plugin', () => { it('tsx app0', async () => { - const { initConfig, getReplaceMap, addToUsedBy } = getOptions({ + const ctx = new Context({ classListPath: path.resolve(tsxRoot, './.tw-patch/tw-class-list.json') }) - await initConfig() - const replaceMap = getReplaceMap() + await ctx.initConfig() + const replaceMap = ctx.getReplaceMap() const file = path.resolve(tsxRoot, 'app0.tsx') const code = await fs.readFile(file, 'utf8') const res = preProcessJs({ code, replaceMap, - addToUsedBy, + addToUsedBy: ctx.addToUsedBy.bind(ctx), id: file }) expect(res).toMatchSnapshot() }) it('ts vanilla-0', async () => { - const { initConfig, getReplaceMap, addToUsedBy } = getOptions({ + const ctx = new Context({ classListPath: path.resolve(tsRoot, './.tw-patch/tw-class-list.json') }) - await initConfig() - const replaceMap = getReplaceMap() + await ctx.initConfig() + const replaceMap = ctx.getReplaceMap() const file = path.resolve(tsRoot, 'vanilla-0.ts') const code = await fs.readFile(file, 'utf8') const res = preProcessJs({ code, replaceMap, - addToUsedBy, + addToUsedBy: ctx.addToUsedBy.bind(ctx), id: file }) expect(res).toMatchSnapshot() diff --git a/packages/unplugin-tailwindcss-mangle/vitest.setup.ts b/packages/unplugin-tailwindcss-mangle/vitest.setup.ts index 68ac183..e69de29 100644 --- a/packages/unplugin-tailwindcss-mangle/vitest.setup.ts +++ b/packages/unplugin-tailwindcss-mangle/vitest.setup.ts @@ -1,4 +0,0 @@ -// import 'tailwindcss-patch/cli' -import { TailwindcssPatcher } from 'tailwindcss-patch' -const twPatcher = new TailwindcssPatcher() -twPatcher.patch() diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e90353e..af03d9a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -486,6 +486,9 @@ importers: packages/config: dependencies: + '@tailwindcss-mangle/shared': + specifier: workspace:^ + version: link:../shared c12: specifier: ^1.4.2 version: 1.4.2 diff --git a/vitest.workspace.ts b/vitest.workspace.ts index 2523d07..8b1c55c 100644 --- a/vitest.workspace.ts +++ b/vitest.workspace.ts @@ -1,3 +1,3 @@ import { defineWorkspace } from 'vitest/config' -export default defineWorkspace(['packages/*', '!packages/tailwindcss-patch']) +export default defineWorkspace(['packages/*'])