├── .changeset ├── README.md └── config.json ├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── config.yml └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .npmrc ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── SECURITY.md ├── apps ├── .gitignore ├── astro-app │ ├── astro.config.mjs │ ├── package.json │ ├── public │ │ └── robots.txt │ ├── src │ │ ├── env.d.ts │ │ └── pages │ │ │ └── index.astro │ └── tsconfig.json ├── next-app-router │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── app │ │ ├── favicon.ico │ │ ├── globals.css │ │ ├── layout.tsx │ │ └── page.tsx │ ├── next.config.mjs │ ├── package.json │ ├── postcss.config.mjs │ ├── public │ │ ├── next.svg │ │ └── vercel.svg │ ├── tailwind.config.ts │ ├── tailwindcss-mangle.config.ts │ └── tsconfig.json ├── next-app │ ├── .eslintrc.json │ ├── .gitignore │ ├── .tw-patch │ │ └── tw-class-list.json │ ├── README.md │ ├── next.config.js │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ ├── _document.tsx │ │ ├── api │ │ │ └── hello.ts │ │ └── index.tsx │ ├── postcss.config.js │ ├── public │ │ ├── favicon.ico │ │ ├── next.svg │ │ └── vercel.svg │ ├── styles │ │ └── globals.css │ ├── tailwind.config.js │ ├── tailwindcss-mangle.config.ts │ └── tsconfig.json ├── nuxt-app │ ├── .gitignore │ ├── .npmrc │ ├── .tw-patch │ │ └── tw-class-list.json │ ├── app.vue │ ├── assets │ │ ├── css │ │ │ └── main.css │ │ ├── next.svg │ │ └── vercel.svg │ ├── nuxt.config.ts │ ├── package.json │ ├── public │ │ └── favicon.ico │ ├── tailwind.config.js │ ├── tailwindcss-mangle.config.ts │ └── tsconfig.json ├── remix-app │ ├── .eslintrc.js │ ├── .gitignore │ ├── .tw-patch │ │ └── tw-class-list.json │ ├── app │ │ ├── entry.client.tsx │ │ ├── entry.server.tsx │ │ ├── root.tsx │ │ ├── routes │ │ │ └── _index.tsx │ │ └── tailwind.css │ ├── package.json │ ├── public │ │ └── favicon.ico │ ├── remix.config.js │ ├── remix.env.d.ts │ ├── tailwind.config.js │ ├── tailwindcss-mangle.config.ts │ └── tsconfig.json ├── solid-app │ ├── .gitignore │ ├── .tw-patch │ │ └── tw-class-list.json │ ├── README.md │ ├── index.html │ ├── package.json │ ├── pnpm-lock.yaml │ ├── postcss.config.js │ ├── src │ │ ├── App.module.css │ │ ├── App.tsx │ │ ├── assets │ │ │ └── favicon.ico │ │ ├── index.css │ │ ├── index.tsx │ │ └── logo.svg │ ├── tailwind.config.js │ ├── tailwindcss-mangle.config.ts │ ├── tsconfig.json │ └── vite.config.ts ├── vite-lit │ ├── .gitignore │ ├── .tw-patch │ │ └── tw-class-list.json │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── assets │ │ │ └── lit.svg │ │ ├── index.css │ │ ├── my-element.ts │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tailwindcss-mangle.config.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── vite-react │ ├── .gitignore │ ├── .tw-patch │ │ └── tw-class-list.json │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.tsx │ │ ├── assets │ │ │ └── react.svg │ │ ├── index.scss │ │ ├── main.tsx │ │ ├── next.tsx │ │ ├── utils.ts │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tailwindcss-mangle.config.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── vite-svelte │ ├── .gitignore │ ├── .tw-patch │ │ └── tw-class-list.json │ ├── .vscode │ │ └── extensions.json │ ├── README.md │ ├── index.html │ ├── package.json │ ├── postcss.config.cjs │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.svelte │ │ ├── app.css │ │ ├── assets │ │ │ └── svelte.svg │ │ ├── lib │ │ │ └── Counter.svelte │ │ ├── main.ts │ │ └── vite-env.d.ts │ ├── svelte.config.js │ ├── tailwind.config.js │ ├── tailwindcss-mangle.config.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── vite-vanilla │ ├── .gitignore │ ├── .tw-patch │ │ └── tw-class-list.json │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── counter.ts │ │ ├── main.ts │ │ ├── style.css │ │ ├── typescript.svg │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tailwindcss-mangle.config.ts │ ├── tsconfig.json │ └── vite.config.ts ├── vite-vue │ ├── .gitignore │ ├── .tw-patch │ │ └── tw-class-list.json │ ├── .vscode │ │ └── extensions.json │ ├── README.md │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.vue │ │ ├── assets │ │ │ └── vue.svg │ │ ├── components │ │ │ └── HelloWorld.vue │ │ ├── index.scss │ │ ├── main.ts │ │ ├── style.css │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tailwindcss-mangle.config.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts └── webpack5-vue3 │ ├── .browserslistrc │ ├── .editorconfig │ ├── .gitignore │ ├── .tw-patch │ └── tw-class-list.json │ ├── babel.config.js │ ├── package.json │ ├── postcss.config.js │ ├── public │ ├── favicon.ico │ └── index.html │ ├── src │ ├── App.vue │ ├── assets │ │ └── logo.png │ ├── components │ │ └── HelloWorld.vue │ ├── main.ts │ ├── router │ │ └── index.ts │ ├── shims-vue.d.ts │ └── views │ │ ├── AboutView.vue │ │ └── HomeView.vue │ ├── tailwind.config.js │ ├── tailwindcss-mangle.config.ts │ ├── tsconfig.json │ └── vue.config.js ├── assets └── package.json ├── commitlint.config.ts ├── eslint.config.js ├── lint-staged.config.js ├── package.json ├── packages ├── config │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ │ ├── config.ts │ │ ├── constants.ts │ │ ├── defaults.ts │ │ ├── index.ts │ │ └── types.ts │ ├── test │ │ ├── __snapshots__ │ │ │ ├── config.test.ts.snap │ │ │ ├── defaults.test.ts.snap │ │ │ └── index.test.ts.snap │ │ ├── config.test.ts │ │ ├── defaults.test.ts │ │ ├── fixtures │ │ │ └── config │ │ │ │ ├── 0.default │ │ │ │ └── tailwindcss-mangle.config.ts │ │ │ │ ├── 1.change-options │ │ │ │ └── tailwindcss-mangle.config.ts │ │ │ │ ├── 2.mangle-options │ │ │ │ └── tailwindcss-mangle.config.ts │ │ │ │ └── initConfig │ │ │ │ └── tailwindcss-mangle.config.ts │ │ ├── index.test.ts │ │ └── utils.ts │ ├── tsconfig.json │ ├── tsup.config.ts │ └── vitest.config.ts ├── core │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── babel │ │ │ └── index.ts │ │ ├── constants.ts │ │ ├── css │ │ │ ├── index.ts │ │ │ └── plugins.ts │ │ ├── ctx │ │ │ └── index.ts │ │ ├── env.ts │ │ ├── html │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── js │ │ │ ├── index.ts │ │ │ └── utils.ts │ │ ├── math.ts │ │ ├── shared.ts │ │ └── types.ts │ ├── test │ │ ├── __snapshots__ │ │ │ ├── css.test.ts.snap │ │ │ ├── html.test.ts.snap │ │ │ ├── index.test.ts.snap │ │ │ └── js.test.ts.snap │ │ ├── css.test.ts │ │ ├── exports.test.ts │ │ ├── fixtures │ │ │ ├── app0.json │ │ │ ├── app0.tsx │ │ │ ├── comment-ignore.js │ │ │ ├── css-loader.js │ │ │ ├── hello-world.css │ │ │ ├── hello-world.html │ │ │ ├── hello-world.js │ │ │ ├── next-app-dev-inline-tw.js │ │ │ ├── next-server-page.js │ │ │ ├── nuxt-app-all-class-set.json │ │ │ ├── nuxt-app-partial-class-set.json │ │ │ ├── nuxt-raw-page.css │ │ │ ├── nuxt-raw-page.js │ │ │ ├── package.json │ │ │ ├── preserve-fn-case0.js │ │ │ ├── preserve-fn-case0.ts │ │ │ ├── preserve-fn-case0.vue │ │ │ ├── preserve-fn-case1.json │ │ │ ├── preserve-fn-case1.vue │ │ │ ├── preserve-fn-case2.json │ │ │ ├── preserve-fn-case2.vue │ │ │ ├── prod │ │ │ │ ├── 0.js │ │ │ │ └── 1.js │ │ │ ├── tailwind.config.js │ │ │ ├── trailing-slash-0.html │ │ │ ├── trailing-slash-0.js │ │ │ ├── trailing-slash-1.js │ │ │ ├── trailing-slash-2.js │ │ │ ├── trailing-slash.html │ │ │ ├── tw-class-set.json │ │ │ ├── vanilla-0.json │ │ │ ├── vanilla-0.ts │ │ │ ├── vite-chunk.js │ │ │ ├── vite-lit.js │ │ │ ├── vue-build-dist.js │ │ │ ├── vue.scoped.css │ │ │ └── webpack-dev-content.js │ │ ├── html.test.ts │ │ ├── index.test.ts │ │ ├── js.test.ts │ │ └── utils │ │ │ ├── index.ts │ │ │ ├── svelte-to-tsx.ts │ │ │ └── vue-to-tsx.ts │ ├── tsconfig.json │ ├── tsup.config.ts │ └── vitest.config.ts ├── shared │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── classGenerator.ts │ │ ├── index.ts │ │ ├── regex.ts │ │ ├── split.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── test │ │ ├── __snapshots__ │ │ │ ├── classGenerator.test.ts.snap │ │ │ ├── reg.test.ts.snap │ │ │ └── split.test.ts.snap │ │ ├── classGenerator.test.ts │ │ ├── reg.test.ts │ │ └── split.test.ts │ ├── tsconfig.json │ ├── tsup.config.ts │ └── vitest.config.ts ├── tailwindcss-patch │ ├── .tw-patch │ │ └── tw-class-list.json │ ├── CHANGELOG.md │ ├── README-cn.md │ ├── README.md │ ├── bin │ │ └── tw-patch.js │ ├── dev │ │ └── bin.ts │ ├── draft.md │ ├── how-it-works.md │ ├── index.css │ ├── package.json │ ├── src │ │ ├── babel │ │ │ └── index.ts │ │ ├── cli.ts │ │ ├── constants.ts │ │ ├── core │ │ │ ├── cache.ts │ │ │ ├── candidates.ts │ │ │ ├── index.ts │ │ │ ├── patcher.ts │ │ │ ├── patches │ │ │ │ ├── exportContext │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── postcss-v2.ts │ │ │ │ │ └── postcss-v3.ts │ │ │ │ ├── index.ts │ │ │ │ └── supportCustomUnits │ │ │ │ │ └── index.ts │ │ │ ├── postcss.ts │ │ │ └── runtime.ts │ │ ├── defaults.ts │ │ ├── index.ts │ │ ├── logger.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── test │ │ ├── __snapshots__ │ │ │ ├── context.test.ts.snap │ │ │ ├── defaults.test.ts.snap │ │ │ ├── index.test.ts.snap │ │ │ ├── inspector.test.ts.snap │ │ │ ├── patch.test.ts.snap │ │ │ ├── postcss7-v2.test.ts.snap │ │ │ ├── postcss8-v3.test.ts.snap │ │ │ └── v4.test.ts.snap │ │ ├── cache.test.ts │ │ ├── config.test.ts │ │ ├── defaults.test.ts │ │ ├── fixtures │ │ │ ├── apps │ │ │ │ ├── 0.common │ │ │ │ │ ├── .tw-patch │ │ │ │ │ │ └── tw-class-list.json │ │ │ │ │ ├── package.json │ │ │ │ │ ├── postcss.config.js │ │ │ │ │ ├── src │ │ │ │ │ │ └── index.html │ │ │ │ │ ├── tailwind.config.js │ │ │ │ │ ├── tailwindcss-mangle.config.ts │ │ │ │ │ └── tailwindcss-patch.config.ts │ │ │ │ └── 1.default │ │ │ │ │ ├── .tw-patch │ │ │ │ │ └── tw-class-list.json │ │ │ │ │ ├── package.json │ │ │ │ │ ├── postcss.config.js │ │ │ │ │ ├── src │ │ │ │ │ └── index.html │ │ │ │ │ ├── tailwind.config.js │ │ │ │ │ ├── tailwindcss-mangle.config.ts │ │ │ │ │ └── tailwindcss-patch.config.ts │ │ │ ├── cache │ │ │ │ ├── index.json │ │ │ │ ├── merge-multiple-context.json │ │ │ │ └── raw-method.json │ │ │ ├── cli │ │ │ │ └── index.css │ │ │ ├── config │ │ │ │ ├── 0.default │ │ │ │ │ └── tailwindcss-mangle.config.ts │ │ │ │ └── 1.change-options │ │ │ │ │ ├── tailwindcss-mangle.config.ts │ │ │ │ │ └── tailwindcss-patch.config.ts │ │ │ ├── hello-world.css │ │ │ ├── hello-world.html │ │ │ ├── hello-world.js │ │ │ ├── hello-world.wxml │ │ │ ├── img-url.jsx │ │ │ ├── postcss7-compat │ │ │ │ └── lib │ │ │ │ │ └── jit │ │ │ │ │ ├── index.js │ │ │ │ │ └── processTailwindFeatures.js │ │ │ ├── trailing-slash.vue │ │ │ ├── v4 │ │ │ │ ├── .gitignore │ │ │ │ ├── deep │ │ │ │ │ └── index.html │ │ │ │ ├── index.css │ │ │ │ ├── index.html │ │ │ │ └── patch │ │ │ │ │ └── dist │ │ │ │ │ ├── a.mjs │ │ │ │ │ ├── chunk-V2K3XTS4.mjs │ │ │ │ │ ├── default-theme.js │ │ │ │ │ └── lib.js │ │ │ └── versions │ │ │ │ ├── 2 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── jit │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── processTailwindFeatures.js │ │ │ │ │ └── processTailwindFeatures.js │ │ │ │ └── package.json │ │ │ │ ├── .eslintignore │ │ │ │ ├── .eslintrc.cjs │ │ │ │ ├── 3.0.0 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.2.1 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.2.2 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.2.3 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.2.4 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.2.6 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.2.7 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.3.0 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.3.1 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.3.2 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.3.3 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.3.4 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.3.5 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.3.6 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.3.7 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.4.0 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.4.1 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.4.12 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.4.14 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.4.2 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.4.3 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.4.4 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.4.5 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.4.6 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── 3.4.7 │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── README.md │ │ │ │ ├── copy.js │ │ │ │ ├── install.js │ │ │ │ ├── lts │ │ │ │ ├── lib │ │ │ │ │ ├── index.js │ │ │ │ │ ├── plugin.js │ │ │ │ │ ├── processTailwindFeatures.js │ │ │ │ │ └── util │ │ │ │ │ │ └── dataTypes.js │ │ │ │ └── package.json │ │ │ │ ├── package.json │ │ │ │ ├── utils.js │ │ │ │ └── yarn.lock │ │ ├── index.test.ts │ │ ├── inspector.test.ts │ │ ├── patch.test.ts │ │ ├── pkg.test.ts │ │ ├── postcss7-v2.test.ts │ │ ├── postcss8-v3.test.ts │ │ ├── splice-changes-into-string.ts │ │ ├── src │ │ │ ├── candidate.ts │ │ │ ├── utils │ │ │ │ └── design-system.ts │ │ │ └── value-parser.ts │ │ ├── tailwindcss4.test.ts │ │ ├── tw-patcher.test.ts │ │ ├── utils.test.ts │ │ ├── utils.ts │ │ └── v4.test.ts │ ├── tsconfig.json │ ├── tsup.config.ts │ ├── vitest.config.ts │ └── vitest.setup.ts └── unplugin-tailwindcss-mangle │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ ├── constants.ts │ ├── core │ │ ├── factory.ts │ │ ├── index.ts │ │ └── plugin.ts │ ├── esbuild.ts │ ├── globals.d.ts │ ├── index.ts │ ├── loader.ts │ ├── nuxt.ts │ ├── rollup.ts │ ├── types.ts │ ├── utils.ts │ ├── vite.ts │ └── webpack.ts │ ├── test │ ├── __snapshots__ │ │ ├── babel.test.ts.snap │ │ ├── fallback.test.ts.snap │ │ ├── pre.test.ts.snap │ │ ├── vite.test.ts.snap │ │ └── webpack.test.ts.snap │ ├── context.test.ts │ ├── fixtures │ │ ├── css │ │ │ ├── vite-vanilla.after.css │ │ │ └── vite-vanilla.before.css │ │ ├── fallback │ │ │ ├── index.json │ │ │ ├── index.vue │ │ │ └── tailwindcss-mangle.config.ts │ │ ├── ts │ │ │ ├── .tw-patch │ │ │ │ └── tw-class-list.json │ │ │ └── vanilla-0.ts │ │ ├── tsx │ │ │ ├── .tw-patch │ │ │ │ └── tw-class-list.json │ │ │ ├── app.tsx │ │ │ └── app0.tsx │ │ ├── vite-repo │ │ │ ├── .gitignore │ │ │ ├── .gitkeep │ │ │ ├── .tw-patch │ │ │ │ └── tw-class-list.json │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── postcss.config.cjs │ │ │ ├── src │ │ │ │ ├── counter.ts │ │ │ │ ├── main.ts │ │ │ │ ├── style.css │ │ │ │ └── vite-env.d.ts │ │ │ ├── tailwind.config.cjs │ │ │ └── tsconfig.json │ │ └── webpack-repo │ │ │ ├── .gitkeep │ │ │ ├── .tw-patch │ │ │ └── tw-class-list.json │ │ │ ├── package.json │ │ │ ├── postcss.config.js │ │ │ ├── src │ │ │ ├── index.css │ │ │ ├── index.html │ │ │ └── index.js │ │ │ └── tailwind.config.js │ ├── vite.test.ts │ └── webpack.test.ts │ ├── tsconfig.json │ ├── tsup.config.ts │ ├── vitest.config.ts │ └── vitest.setup.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── renovate.json ├── scripts └── postcss7-compat │ ├── CHANGELOG.md │ ├── index.js │ ├── package.json │ └── result.css ├── stylelint.config.js ├── tsconfig.json ├── turbo.json ├── vitest.workspace.ts └── website ├── .gitignore ├── .tw-patch └── tw-class-list.json ├── CHANGELOG.md ├── app ├── [lang] │ ├── [[...mdxPath]] │ │ └── page.tsx │ ├── layout.tsx │ ├── not-found.ts │ └── styles.css ├── _components │ ├── authors.tsx │ └── features.css ├── _dictionaries │ ├── en.ts │ ├── get-dictionary.ts │ ├── i18n-config.ts │ └── zh.ts └── env.d.ts ├── content ├── en │ ├── _meta.js │ ├── config.mdx │ ├── index.mdx │ ├── mangle.mdx │ ├── patch.mdx │ └── recommend.mdx └── zh │ ├── _meta.js │ ├── config.mdx │ ├── index.mdx │ ├── mangle.mdx │ ├── patch.mdx │ └── recommend.mdx ├── mdx-components.ts ├── middleware.ts ├── next-env.d.ts ├── next.config.ts ├── package.json ├── postcss.config.js ├── scripts └── translate.ts ├── tailwind.config.ts └── tsconfig.json /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.2/schema.json", 3 | "changelog": [ 4 | "@changesets/changelog-github", 5 | { 6 | "repo": "sonofmagic/tailwindcss-mangle" 7 | } 8 | ], 9 | "commit": false, 10 | "fixed": [], 11 | "linked": [], 12 | "access": "public", 13 | "baseBranch": "main", 14 | "updateInternalDependencies": "patch", 15 | "ignore": [] 16 | } 17 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .git 3 | .gitignore 4 | *.md 5 | dist -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = false 12 | insert_final_newline = false -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository 2 | 3 | github: [sonofmagic] 4 | custom: ['https://github.com/sonofmagic/sponsors'] 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Feature Request 4 | url: https://github.com/sonofmagic/monorepo-template/discussions 5 | about: Suggest new features for consideration 6 | # - name: Discord Chat 7 | # url: https://chat.vuejs.org 8 | # about: Ask questions and discuss with other Vue users in real time. 9 | # - name: Questions & Discussions 10 | # url: https://github.com/vuejs/core/discussions 11 | # about: Use GitHub discussions for message-board style questions and discussions. 12 | # - name: Patreon 13 | # url: https://www.patreon.com/evanyou 14 | # about: Love Vue.js? Please consider supporting us via Patreon. 15 | # - name: Open Collective 16 | # url: https://opencollective.com/vuejs/donate 17 | # about: Love Vue.js? Please consider supporting us via Open Collective. 18 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | # push: 5 | # branches: ['main'] 6 | pull_request: 7 | types: [opened, synchronize] 8 | workflow_dispatch: 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | build: 15 | name: Build and Test 16 | timeout-minutes: 15 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | os: [ubuntu-latest, windows-latest, macos-latest] 21 | node-version: [18, 20, 22] 22 | runs-on: ${{ matrix.os }} 23 | # To use Remote Caching, uncomment the next lines and follow the steps below. 24 | # env: 25 | # TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} 26 | # TURBO_TEAM: ${{ vars.TURBO_TEAM }} 27 | 28 | steps: 29 | - name: Check out code 30 | uses: actions/checkout@v4 31 | with: 32 | fetch-depth: 2 33 | 34 | - uses: pnpm/action-setup@v4 35 | 36 | - name: Setup Node.js environment 37 | uses: actions/setup-node@v4 38 | with: 39 | node-version: ${{ matrix.node-version }} 40 | cache: pnpm 41 | 42 | - name: Install dependencies 43 | run: pnpm install 44 | 45 | - name: Build 46 | run: pnpm build 47 | 48 | - name: Test 49 | run: pnpm test 50 | 51 | - name: Upload coverage reports to Codecov 52 | uses: codecov/codecov-action@v5 53 | env: 54 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | 11 | # next.js 12 | .next/ 13 | out/ 14 | build 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | 20 | # debug 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | .pnpm-debug.log* 25 | 26 | # local env files 27 | .env.local 28 | .env.development.local 29 | .env.test.local 30 | .env.production.local 31 | 32 | # turbo 33 | .turbo 34 | dist 35 | .nuxt -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | pnpm dlx commitlint --edit $1 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | pnpm lint-staged -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=http://registry.npmmirror.com/ 2 | shamefully-hoist=true 3 | git-checks=false 4 | hoist-pattern[]=!tailwindcss 5 | auto-install-peers=false 6 | package-manager-strict=false 7 | public-hoist-pattern[]=*@nextui-org/* -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "stylelint.vscode-stylelint" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | TODO 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:22-alpine AS base 2 | ENV PNPM_HOME="/pnpm" 3 | ENV PATH="$PNPM_HOME:$PATH" 4 | RUN corepack enable 5 | 6 | FROM base AS builder 7 | COPY . /usr/src/app 8 | WORKDIR /usr/src/app 9 | RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile 10 | RUN pnpm run -r build 11 | RUN pnpm deploy --filter=@icebreakers/foo --prod /prod/foo 12 | RUN pnpm deploy --filter=@icebreakers/bar --prod /prod/bar 13 | 14 | FROM base AS foo 15 | COPY --from=builder /prod/foo /prod/foo 16 | WORKDIR /prod/foo 17 | EXPOSE 8000 18 | CMD [ "pnpm", "start" ] 19 | 20 | FROM base AS bar 21 | COPY --from=builder /prod/bar /prod/bar 22 | WORKDIR /prod/bar 23 | EXPOSE 8001 24 | CMD [ "pnpm", "start" ] 25 | 26 | # docker build . --target foo --tag foo:latest 27 | # docker build . --target bar --tag bar:latest -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 ice breaker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | -------------------------------------------------------------------------------- /apps/.gitignore: -------------------------------------------------------------------------------- 1 | .tw-patch -------------------------------------------------------------------------------- /apps/astro-app/astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'astro/config' 2 | 3 | // https://astro.build/config 4 | export default defineConfig({}) 5 | -------------------------------------------------------------------------------- /apps/astro-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "astro-app", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "", 6 | "author": "", 7 | "license": "ISC", 8 | "keywords": [], 9 | "main": "index.js", 10 | "scripts": { 11 | "dev": "astro dev", 12 | "start": "astro dev", 13 | "build": "astro build", 14 | "preview": "astro preview" 15 | }, 16 | "dependencies": { 17 | "astro": "^4.16.7" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/astro-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # Example: Allow all bots to scan and index your site. 2 | # Full syntax: https://developers.google.com/search/docs/advanced/robots/create-robots-txt 3 | User-agent: * 4 | Allow: / -------------------------------------------------------------------------------- /apps/astro-app/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/astro-app/src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | // Welcome to Astro! Everything between these triple-dash code fences 3 | // is your "component frontmatter". It never runs in the browser. 4 | console.log('This runs in your terminal, not the browser!'); 5 | --- 6 | 8 | 9 | 10 |

Hello, World!

11 | 12 | 13 | 18 | -------------------------------------------------------------------------------- /apps/astro-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strictest" 3 | } 4 | -------------------------------------------------------------------------------- /apps/next-app-router/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /apps/next-app-router/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /apps/next-app-router/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonofmagic/tailwindcss-mangle/b21d639f21c84418fd04cdf2961ed77ea07fa141/apps/next-app-router/app/favicon.ico -------------------------------------------------------------------------------- /apps/next-app-router/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --foreground-rgb: 0, 0, 0; 7 | --background-start-rgb: 214, 219, 220; 8 | --background-end-rgb: 255, 255, 255; 9 | } 10 | 11 | @media (prefers-color-scheme: dark) { 12 | :root { 13 | --foreground-rgb: 255, 255, 255; 14 | --background-start-rgb: 0, 0, 0; 15 | --background-end-rgb: 0, 0, 0; 16 | } 17 | } 18 | 19 | body { 20 | color: rgb(var(--foreground-rgb)); 21 | background: linear-gradient(to bottom, transparent, rgb(var(--background-end-rgb))) rgb(var(--background-start-rgb)); 22 | } 23 | 24 | @layer utilities { 25 | .text-balance { 26 | text-wrap: balance; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /apps/next-app-router/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next' 2 | import { Inter } from 'next/font/google' 3 | import './globals.css' 4 | 5 | const inter = Inter({ subsets: ['latin'] }) 6 | 7 | export const metadata: Metadata = { 8 | title: 'Create Next App', 9 | description: 'Generated by create next app', 10 | } 11 | 12 | export default function RootLayout({ 13 | children, 14 | }: Readonly<{ 15 | children: React.ReactNode 16 | }>) { 17 | return ( 18 | 19 | {children} 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /apps/next-app-router/next.config.mjs: -------------------------------------------------------------------------------- 1 | import utwm from 'unplugin-tailwindcss-mangle/webpack' 2 | 3 | /** @type {import('next').NextConfig} */ 4 | const nextConfig = { 5 | webpack: (config) => { 6 | config.plugins.push(utwm({ 7 | classMapOutput: true, 8 | })) 9 | return config 10 | }, 11 | } 12 | 13 | export default nextConfig 14 | -------------------------------------------------------------------------------- /apps/next-app-router/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-app-router", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "tw-extract": "tw-patch extract" 11 | }, 12 | "dependencies": { 13 | "next": "15.0.1", 14 | "react": "^18", 15 | "react-dom": "^18" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "^22.8.1", 19 | "@types/react": "^18.3.12", 20 | "@types/react-dom": "^18", 21 | "eslint": "^9.13.0", 22 | "eslint-config-next": "15.0.1", 23 | "postcss": "^8", 24 | "tailwindcss": "^3.4.1", 25 | "typescript": "^5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/next-app-router/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | } 7 | 8 | export default config 9 | -------------------------------------------------------------------------------- /apps/next-app-router/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/next-app-router/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/next-app-router/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss' 2 | 3 | const config: Config = { 4 | content: [ 5 | './pages/**/*.{js,ts,jsx,tsx,mdx}', 6 | './components/**/*.{js,ts,jsx,tsx,mdx}', 7 | './app/**/*.{js,ts,jsx,tsx,mdx}', 8 | ], 9 | theme: { 10 | extend: { 11 | backgroundImage: { 12 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 13 | 'gradient-conic': 14 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 15 | }, 16 | }, 17 | }, 18 | plugins: [], 19 | } 20 | export default config 21 | -------------------------------------------------------------------------------- /apps/next-app-router/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({}) 4 | -------------------------------------------------------------------------------- /apps/next-app-router/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "jsx": "preserve", 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "module": "esnext", 7 | "moduleResolution": "bundler", 8 | "paths": { 9 | "@/*": ["./*"] 10 | }, 11 | "resolveJsonModule": true, 12 | "allowJs": true, 13 | "strict": true, 14 | "noEmit": true, 15 | "esModuleInterop": true, 16 | "isolatedModules": true, 17 | "skipLibCheck": true, 18 | "plugins": [ 19 | { 20 | "name": "next" 21 | } 22 | ] 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /apps/next-app/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": "next/core-web-vitals" 4 | } 5 | -------------------------------------------------------------------------------- /apps/next-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /apps/next-app/next.config.js: -------------------------------------------------------------------------------- 1 | // import process from 'node:process' 2 | const process = require('node:process') 3 | const utwm = require('unplugin-tailwindcss-mangle/webpack') 4 | // import utwm from 'unplugin-tailwindcss-mangle' 5 | 6 | /** @type {import('next').NextConfig} */ 7 | const nextConfig = { 8 | reactStrictMode: true, 9 | webpack: (config) => { 10 | // console.log(process.env.NODE_ENV) 11 | // if (process.env.NODE_ENV === 'production') { 12 | // config.plugins.push(utwm({ 13 | // classMapOutput: true, 14 | // })) 15 | // } 16 | config.plugins.push(utwm({ 17 | classMapOutput: true, 18 | })) 19 | return config 20 | }, 21 | } 22 | 23 | module.exports = nextConfig 24 | -------------------------------------------------------------------------------- /apps/next-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "prepare": "tw-patch install", 11 | "tw-extract": "tw-patch extract" 12 | }, 13 | "dependencies": { 14 | "@types/node": "22.8.1", 15 | "@types/react": "18.3.12", 16 | "@types/react-dom": "18.3.1", 17 | "autoprefixer": "10.4.20", 18 | "eslint": "9.13.0", 19 | "eslint-config-next": "15.0.1", 20 | "next": "15.0.1", 21 | "postcss": "8.4.47", 22 | "react": "18.3.1", 23 | "react-dom": "18.3.1", 24 | "tailwindcss": "3.4.14", 25 | "typescript": "5.6.3" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/next-app/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import type { AppProps } from 'next/app' 2 | import '@/styles/globals.css' 3 | 4 | export default function App({ Component, pageProps }: AppProps) { 5 | return 6 | } 7 | -------------------------------------------------------------------------------- /apps/next-app/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Head, Html, Main, NextScript } from 'next/document' 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /apps/next-app/pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import type { NextApiRequest, NextApiResponse } from 'next' 3 | 4 | interface Data { 5 | name: string 6 | } 7 | 8 | export default function handler( 9 | req: NextApiRequest, 10 | res: NextApiResponse, 11 | ) { 12 | res.status(200).json({ name: 'John Doe' }) 13 | } 14 | -------------------------------------------------------------------------------- /apps/next-app/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/next-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonofmagic/tailwindcss-mangle/b21d639f21c84418fd04cdf2961ed77ea07fa141/apps/next-app/public/favicon.ico -------------------------------------------------------------------------------- /apps/next-app/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/next-app/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/next-app/styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --foreground-rgb: 0, 0, 0; 7 | --background-start-rgb: 214, 219, 220; 8 | --background-end-rgb: 255, 255, 255; 9 | } 10 | 11 | @media (prefers-color-scheme: dark) { 12 | :root { 13 | --foreground-rgb: 255, 255, 255; 14 | --background-start-rgb: 0, 0, 0; 15 | --background-end-rgb: 0, 0, 0; 16 | } 17 | } 18 | 19 | body { 20 | color: rgb(var(--foreground-rgb)); 21 | background: linear-gradient(to bottom, transparent, rgb(var(--background-end-rgb))) rgb(var(--background-start-rgb)); 22 | } 23 | -------------------------------------------------------------------------------- /apps/next-app/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | './pages/**/*.{js,ts,jsx,tsx}', 5 | './components/**/*.{js,ts,jsx,tsx}', 6 | './app/**/*.{js,ts,jsx,tsx}', 7 | ], 8 | theme: { 9 | extend: { 10 | backgroundImage: { 11 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 12 | 'gradient-conic': 13 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 14 | }, 15 | }, 16 | }, 17 | plugins: [], 18 | corePlugins: { 19 | preflight: false, 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /apps/next-app/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({}) 4 | -------------------------------------------------------------------------------- /apps/next-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "target": "es5", 5 | "jsx": "preserve", 6 | "lib": ["dom", "dom.iterable", "esnext"], 7 | "module": "esnext", 8 | "moduleResolution": "node", 9 | "paths": { 10 | "@/*": ["./*"] 11 | }, 12 | "resolveJsonModule": true, 13 | "allowJs": true, 14 | "strict": true, 15 | "noEmit": true, 16 | "esModuleInterop": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "isolatedModules": true, 19 | "skipLibCheck": true 20 | }, 21 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 22 | "exclude": ["node_modules"] 23 | } 24 | -------------------------------------------------------------------------------- /apps/nuxt-app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log* 3 | .nuxt 4 | .nitro 5 | .cache 6 | .output 7 | .env 8 | dist 9 | .DS_Store 10 | -------------------------------------------------------------------------------- /apps/nuxt-app/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true -------------------------------------------------------------------------------- /apps/nuxt-app/assets/css/main.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /apps/nuxt-app/assets/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/nuxt-app/assets/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/nuxt-app/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | // import nuxtPlugin from 'unplugin-tailwindcss-mangle/nuxt' 3 | 4 | export default defineNuxtConfig({ 5 | css: ['~/assets/css/main.css'], 6 | postcss: { 7 | plugins: { 8 | tailwindcss: {}, 9 | autoprefixer: {}, 10 | }, 11 | }, 12 | // https://github.com/nuxt/nuxt/issues/20428 13 | experimental: { 14 | inlineSSRStyles: false, 15 | }, 16 | modules: [ 17 | // [ 18 | // nuxtPlugin, 19 | // { 20 | // classMapOutput: true, 21 | // }, 22 | // ], 23 | ], 24 | }) 25 | -------------------------------------------------------------------------------- /apps/nuxt-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-app", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "nuxt build", 7 | "dev": "nuxt dev", 8 | "generate": "nuxt generate", 9 | "preview": "nuxt preview", 10 | "postinstall": "nuxt prepare", 11 | "prepare": "tw-patch install", 12 | "tw-extract": "tw-patch extract" 13 | }, 14 | "devDependencies": { 15 | "@types/node": "^22.8.1", 16 | "autoprefixer": "^10.4.20", 17 | "nuxt": "^3.13.2", 18 | "postcss": "^8.4.47", 19 | "tailwindcss": "^3.4.14" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /apps/nuxt-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonofmagic/tailwindcss-mangle/b21d639f21c84418fd04cdf2961ed77ea07fa141/apps/nuxt-app/public/favicon.ico -------------------------------------------------------------------------------- /apps/nuxt-app/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./components/**/*.{js,vue,ts}', './layouts/**/*.vue', './pages/**/*.vue', './plugins/**/*.{js,ts}', './nuxt.config.{js,ts}', './app.vue'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | corePlugins: { 9 | preflight: false, 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /apps/nuxt-app/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({}) 4 | -------------------------------------------------------------------------------- /apps/nuxt-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /apps/remix-app/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type {import('eslint').Linter.Config} */ 2 | module.exports = { 3 | extends: ['@remix-run/eslint-config', '@remix-run/eslint-config/node'], 4 | } 5 | -------------------------------------------------------------------------------- /apps/remix-app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | /.cache 4 | /build 5 | /public/build 6 | .env 7 | -------------------------------------------------------------------------------- /apps/remix-app/app/entry.client.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * By default, Remix will handle hydrating your app on the client for you. 3 | * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨ 4 | * For more information, see https://remix.run/docs/en/main/file-conventions/entry.client 5 | */ 6 | 7 | import { RemixBrowser } from '@remix-run/react' 8 | import { startTransition, StrictMode } from 'react' 9 | import { hydrateRoot } from 'react-dom/client' 10 | 11 | startTransition(() => { 12 | hydrateRoot( 13 | document, 14 | 15 | 16 | , 17 | ) 18 | }) 19 | -------------------------------------------------------------------------------- /apps/remix-app/app/root.tsx: -------------------------------------------------------------------------------- 1 | import type { LinksFunction } from '@remix-run/node' 2 | import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react' 3 | import stylesheet from '~/tailwind.css' 4 | 5 | export const links: LinksFunction = () => [{ rel: 'stylesheet', href: stylesheet }] 6 | 7 | export default function App() { 8 | return ( 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /apps/remix-app/app/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /apps/remix-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remix-app", 3 | "version": "1.0.0", 4 | "private": true, 5 | "sideEffects": false, 6 | "engines": { 7 | "node": ">=14" 8 | }, 9 | "scripts": { 10 | "build": "remix build", 11 | "dev": "remix dev", 12 | "start": "remix-serve build", 13 | "typecheck": "tsc", 14 | "prepare": "tw-patch install", 15 | "tw-extract": "tw-patch extract" 16 | }, 17 | "dependencies": { 18 | "@remix-run/node": "^2.13.1", 19 | "@remix-run/react": "^2.13.1", 20 | "@remix-run/serve": "^2.13.1", 21 | "isbot": "^5.1.17", 22 | "react": "^18.3.1", 23 | "react-dom": "^18.3.1" 24 | }, 25 | "devDependencies": { 26 | "@remix-run/dev": "^2.13.1", 27 | "@remix-run/eslint-config": "^2.13.1", 28 | "@types/react": "^18.3.12", 29 | "@types/react-dom": "^18.3.1", 30 | "eslint": "^9.13.0", 31 | "tailwindcss": "^3.4.14", 32 | "typescript": "^5.6.3" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /apps/remix-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonofmagic/tailwindcss-mangle/b21d639f21c84418fd04cdf2961ed77ea07fa141/apps/remix-app/public/favicon.ico -------------------------------------------------------------------------------- /apps/remix-app/remix.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@remix-run/dev').AppConfig} */ 2 | module.exports = { 3 | ignoredRouteFiles: ['**/.*'], 4 | // appDirectory: "app", 5 | // assetsBuildDirectory: "public/build", 6 | // serverBuildPath: "build/index.js", 7 | // publicPath: "/build/", 8 | future: { 9 | v2_errorBoundary: true, 10 | v2_meta: true, 11 | v2_normalizeFormMethod: true, 12 | v2_routeConvention: true, 13 | unstable_tailwind: true, 14 | }, 15 | // where to register webpack plugin ???? 16 | } 17 | -------------------------------------------------------------------------------- /apps/remix-app/remix.env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /apps/remix-app/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./app/**/*.{js,jsx,ts,tsx}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | corePlugins: { 9 | preflight: false, 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /apps/remix-app/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({}) 4 | -------------------------------------------------------------------------------- /apps/remix-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2019", 4 | "jsx": "react-jsx", 5 | "lib": ["DOM", "DOM.Iterable", "ES2019"], 6 | "baseUrl": ".", 7 | "moduleResolution": "node", 8 | "paths": { 9 | "~/*": ["./app/*"] 10 | }, 11 | "resolveJsonModule": true, 12 | "allowJs": true, 13 | "strict": true, 14 | "esModuleInterop": true, 15 | "isolatedModules": true, 16 | 17 | // Remix takes care of building everything in `remix build`. 18 | "noEmit": true, 19 | "forceConsistentCasingInFileNames": true 20 | }, 21 | "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"] 22 | } 23 | -------------------------------------------------------------------------------- /apps/solid-app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /apps/solid-app/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | Those templates dependencies are maintained via [pnpm](https://pnpm.io) via `pnpm up -Lri`. 4 | 5 | This is the reason you see a `pnpm-lock.yaml`. That being said, any package manager will work. This file can be safely be removed once you clone a template. 6 | 7 | ```bash 8 | $ npm install # or pnpm install or yarn install 9 | ``` 10 | 11 | ### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs) 12 | 13 | ## Available Scripts 14 | 15 | In the project directory, you can run: 16 | 17 | ### `npm dev` or `npm start` 18 | 19 | Runs the app in the development mode.
20 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 21 | 22 | The page will reload if you make edits.
23 | 24 | ### `npm run build` 25 | 26 | Builds the app for production to the `dist` folder.
27 | It correctly bundles Solid in production mode and optimizes the build for the best performance. 28 | 29 | The build is minified and the filenames include the hashes.
30 | Your app is ready to be deployed! 31 | 32 | ## Deployment 33 | 34 | You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.) 35 | -------------------------------------------------------------------------------- /apps/solid-app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Solid App 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /apps/solid-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-template-solid", 3 | "version": "0.0.0", 4 | "private": true, 5 | "description": "", 6 | "license": "MIT", 7 | "scripts": { 8 | "start": "vite", 9 | "dev": "vite", 10 | "build": "vite build", 11 | "serve": "vite preview", 12 | "preview": "vite preview", 13 | "prepare": "tw-patch install", 14 | "tw-extract": "tw-patch extract" 15 | }, 16 | "dependencies": { 17 | "solid-js": "^1.9.2" 18 | }, 19 | "devDependencies": { 20 | "autoprefixer": "^10.4.20", 21 | "postcss": "^8.4.47", 22 | "tailwindcss": "^3.4.14", 23 | "typescript": "^5.6.3", 24 | "vite": "^5.4.9", 25 | "vite-plugin-solid": "^2.10.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/solid-app/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/solid-app/src/App.module.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .logo { 6 | animation: logo-spin infinite 20s linear; 7 | height: 40vmin; 8 | pointer-events: none; 9 | } 10 | 11 | .header { 12 | background-color: #282c34; 13 | min-height: 100vh; 14 | display: flex; 15 | flex-direction: column; 16 | align-items: center; 17 | justify-content: center; 18 | font-size: calc(10px + 2vmin); 19 | color: white; 20 | } 21 | 22 | .link { 23 | color: #b318f0; 24 | } 25 | 26 | @keyframes logo-spin { 27 | from { 28 | transform: rotate(0deg); 29 | } 30 | to { 31 | transform: rotate(360deg); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/solid-app/src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonofmagic/tailwindcss-mangle/b21d639f21c84418fd04cdf2961ed77ea07fa141/apps/solid-app/src/assets/favicon.ico -------------------------------------------------------------------------------- /apps/solid-app/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body { 6 | margin: 0; 7 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 8 | 'Droid Sans', 'Helvetica Neue', sans-serif; 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | } 12 | 13 | code { 14 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; 15 | } 16 | -------------------------------------------------------------------------------- /apps/solid-app/src/index.tsx: -------------------------------------------------------------------------------- 1 | /* @refresh reload */ 2 | import { render } from 'solid-js/web' 3 | 4 | import App from './App' 5 | import './index.css' 6 | 7 | const root = document.getElementById('root') 8 | 9 | if (import.meta.env.DEV && !(root instanceof HTMLElement)) { 10 | throw new Error( 11 | 'Root element not found. Did you forget to add it to your index.html? Or maybe the id attribute got mispelled?', 12 | ) 13 | } 14 | 15 | render(() => , root!) 16 | -------------------------------------------------------------------------------- /apps/solid-app/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./index.html', './src/**/*.{js,jsx,ts,tsx}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | corePlugins: { 9 | preflight: false, 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /apps/solid-app/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({}) 4 | -------------------------------------------------------------------------------- /apps/solid-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "jsx": "preserve", 5 | "jsxImportSource": "solid-js", 6 | "module": "ESNext", 7 | "moduleResolution": "node", 8 | "types": ["vite/client"], 9 | "strict": true, 10 | "noEmit": true, 11 | "allowSyntheticDefaultImports": true, 12 | "esModuleInterop": true, 13 | "isolatedModules": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/solid-app/vite.config.ts: -------------------------------------------------------------------------------- 1 | import utwm from 'unplugin-tailwindcss-mangle/vite' 2 | import { defineConfig } from 'vite' 3 | import solidPlugin from 'vite-plugin-solid' 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | solidPlugin(), 8 | utwm({ 9 | classMapOutput: true, 10 | }), 11 | ], 12 | server: { 13 | port: 3000, 14 | }, 15 | build: { 16 | target: 'esnext', 17 | }, 18 | }) 19 | -------------------------------------------------------------------------------- /apps/vite-lit/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/vite-lit/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + Lit + TS 8 | 9 | 10 | 11 | 12 | 13 |

Vite + Lit

14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /apps/vite-lit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-lit", 3 | "type": "module", 4 | "version": "0.0.0", 5 | "private": true, 6 | "exports": { 7 | ".": "./dist/my-element.es.js" 8 | }, 9 | "main": "dist/my-element.es.js", 10 | "types": "types/my-element.d.ts", 11 | "files": [ 12 | "dist", 13 | "types" 14 | ], 15 | "scripts": { 16 | "dev": "vite", 17 | "_build": "tsc && vite build", 18 | "prepare": "tw-patch install", 19 | "tw-extract": "tw-patch extract" 20 | }, 21 | "dependencies": { 22 | "lit": "^3.2.1" 23 | }, 24 | "devDependencies": { 25 | "autoprefixer": "^10.4.20", 26 | "postcss": "^8.4.47", 27 | "tailwindcss": "^3.4.14", 28 | "typescript": "^5.6.3", 29 | "vite": "^5.4.9" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /apps/vite-lit/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/vite-lit/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/vite-lit/src/assets/lit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/vite-lit/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 7 | line-height: 1.5; 8 | font-weight: 400; 9 | 10 | color-scheme: light dark; 11 | color: rgba(255, 255, 255, 0.87); 12 | background-color: #242424; 13 | 14 | font-synthesis: none; 15 | text-rendering: optimizeLegibility; 16 | -webkit-font-smoothing: antialiased; 17 | -moz-osx-font-smoothing: grayscale; 18 | -webkit-text-size-adjust: 100%; 19 | } 20 | 21 | a { 22 | font-weight: 500; 23 | color: #646cff; 24 | text-decoration: inherit; 25 | } 26 | a:hover { 27 | color: #535bf2; 28 | } 29 | 30 | body { 31 | margin: 0; 32 | display: flex; 33 | place-items: center; 34 | min-width: 320px; 35 | min-height: 100vh; 36 | } 37 | 38 | @media (prefers-color-scheme: light) { 39 | :root { 40 | color: #213547; 41 | background-color: #ffffff; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /apps/vite-lit/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/vite-lit/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | corePlugins: { 9 | preflight: false, 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /apps/vite-lit/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({}) 4 | -------------------------------------------------------------------------------- /apps/vite-lit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 4 | "useDefineForClassFields": false, 5 | "experimentalDecorators": true, 6 | "module": "ESNext", 7 | "moduleResolution": "Node", 8 | "strict": true, 9 | "noFallthroughCasesInSwitch": true, 10 | "noImplicitReturns": true, 11 | "noUnusedLocals": true, 12 | "noUnusedParameters": true, 13 | "declaration": true, 14 | "emitDeclarationOnly": true, 15 | "outDir": "./types", 16 | "allowSyntheticDefaultImports": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "isolatedModules": true, 19 | "skipLibCheck": true 20 | }, 21 | "references": [{ "path": "./tsconfig.node.json" }], 22 | "include": ["src/**/*.ts"] 23 | } 24 | -------------------------------------------------------------------------------- /apps/vite-lit/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/vite-lit/vite.config.ts: -------------------------------------------------------------------------------- 1 | import utwm from 'unplugin-tailwindcss-mangle/vite' 2 | import { defineConfig } from 'vite' 3 | // https://vitejs.dev/config/ 4 | export default defineConfig({ 5 | build: { 6 | lib: { 7 | entry: 'src/my-element.ts', 8 | formats: ['es'], 9 | }, 10 | rollupOptions: { 11 | external: /^lit/, 12 | }, 13 | }, 14 | plugins: [utwm()], 15 | }) 16 | -------------------------------------------------------------------------------- /apps/vite-react/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/vite-react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/vite-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-react", 3 | "type": "module", 4 | "version": "0.0.0", 5 | "private": true, 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview", 10 | "prepare": "tw-patch install", 11 | "tw-extract": "tw-patch extract" 12 | }, 13 | "dependencies": { 14 | "@nextui-org/button": "^2.0.38", 15 | "@nextui-org/react": "^2.4.8", 16 | "@nextui-org/system": "^2.2.6", 17 | "@nextui-org/theme": "^2.2.11", 18 | "framer-motion": "^11.11.9", 19 | "react": "^18.3.1", 20 | "react-dom": "^18.3.1", 21 | "react-router-dom": "^6.27.0" 22 | }, 23 | "devDependencies": { 24 | "@types/react": "^18.3.12", 25 | "@types/react-dom": "^18.3.1", 26 | "@vitejs/plugin-react": "^4.3.3", 27 | "autoprefixer": "^10.4.20", 28 | "postcss": "^8.4.47", 29 | "tailwindcss": "^3.4.14", 30 | "typescript": "^5.6.3", 31 | "vite": "^5.4.9" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/vite-react/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/vite-react/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/vite-react/src/main.tsx: -------------------------------------------------------------------------------- 1 | import { NextUIProvider } from '@nextui-org/react' 2 | import React from 'react' 3 | import ReactDOM from 'react-dom/client' 4 | import { 5 | createBrowserRouter, 6 | RouterProvider, 7 | } from 'react-router-dom' 8 | import App from './App' 9 | import Next from './next' 10 | import './index.scss' 11 | 12 | const router = createBrowserRouter([ 13 | { 14 | path: '/', 15 | element: , 16 | }, 17 | { 18 | path: '/next', 19 | element: , 20 | }, 21 | ]) 22 | 23 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 24 | 25 | 26 | 27 | 28 | , 29 | ) 30 | -------------------------------------------------------------------------------- /apps/vite-react/src/next.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '@nextui-org/button' 2 | 3 | export default function () { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /apps/vite-react/src/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from 'clsx' 2 | import { twMerge } from 'tailwind-merge' 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /apps/vite-react/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/vite-react/tailwind.config.js: -------------------------------------------------------------------------------- 1 | import { nextui } from '@nextui-org/react' 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | export default { 5 | content: [ 6 | './index.html', 7 | './src/**/*.{js,ts,jsx,tsx}', 8 | './node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}', 9 | ], 10 | theme: { 11 | extend: {}, 12 | }, 13 | darkMode: 'class', 14 | plugins: [nextui()], 15 | corePlugins: { 16 | preflight: false, 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /apps/vite-react/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({ 4 | mangle: { 5 | preserveFunction: ['twMerge', 'cn'], 6 | }, 7 | }) 8 | -------------------------------------------------------------------------------- /apps/vite-react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "jsx": "react-jsx", 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "useDefineForClassFields": true, 7 | "module": "ESNext", 8 | "moduleResolution": "Node", 9 | "resolveJsonModule": true, 10 | "allowJs": false, 11 | "strict": true, 12 | "noEmit": true, 13 | "allowSyntheticDefaultImports": true, 14 | "esModuleInterop": false, 15 | "forceConsistentCasingInFileNames": true, 16 | "isolatedModules": true, 17 | "skipLibCheck": true 18 | }, 19 | "references": [{ "path": "./tsconfig.node.json" }], 20 | "include": ["src"] 21 | } 22 | -------------------------------------------------------------------------------- /apps/vite-react/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "target": "ESNext", 5 | "module": "ESNext", 6 | "moduleResolution": "Bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /apps/vite-react/vite.config.ts: -------------------------------------------------------------------------------- 1 | import react from '@vitejs/plugin-react' 2 | import { register } from 'tsx/esm/api' 3 | import { defineConfig } from 'vite' 4 | 5 | register() 6 | const { default: utwm } = await import('unplugin-tailwindcss-mangle/vite') 7 | 8 | export default defineConfig({ 9 | plugins: [ 10 | react(), 11 | utwm({ 12 | classMapOutput: true, 13 | }), 14 | ], 15 | css: { 16 | preprocessorOptions: { 17 | scss: { 18 | silenceDeprecations: ['legacy-js-api'], 19 | }, 20 | }, 21 | }, 22 | }) 23 | -------------------------------------------------------------------------------- /apps/vite-svelte/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/vite-svelte/.tw-patch/tw-class-list.json: -------------------------------------------------------------------------------- 1 | [ 2 | "drop-shadow", 3 | "filter", 4 | "flex", 5 | "justify-between", 6 | "text-[40px]", 7 | "text-[red]", 8 | "transition" 9 | ] 10 | -------------------------------------------------------------------------------- /apps/vite-svelte/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["svelte.svelte-vscode"] 3 | } 4 | -------------------------------------------------------------------------------- /apps/vite-svelte/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + Svelte + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/vite-svelte/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-svelte", 3 | "type": "module", 4 | "version": "0.0.0", 5 | "private": true, 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview", 10 | "check": "svelte-check --tsconfig ./tsconfig.json", 11 | "prepare": "tw-patch install", 12 | "tw-extract": "tw-patch extract" 13 | }, 14 | "devDependencies": { 15 | "@sveltejs/vite-plugin-svelte": "^4.0.0", 16 | "@tsconfig/svelte": "^5.0.4", 17 | "autoprefixer": "^10.4.20", 18 | "postcss": "^8.4.47", 19 | "svelte": "^5.0.5", 20 | "svelte-check": "^4.0.5", 21 | "tailwindcss": "^3.4.14", 22 | "tslib": "^2.8.0", 23 | "typescript": "^5.6.3", 24 | "vite": "^5.4.9" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /apps/vite-svelte/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/vite-svelte/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/vite-svelte/src/App.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 |
8 | 16 |

Vite + Svelte

17 | 18 |
19 | 20 |
21 | 22 |

23 | Check out SvelteKit, the official Svelte app framework powered by Vite! 24 |

25 | 26 |

27 | Click on the Vite and Svelte logos to learn more 28 |

29 |
30 | 31 | 48 | -------------------------------------------------------------------------------- /apps/vite-svelte/src/lib/Counter.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /apps/vite-svelte/src/main.ts: -------------------------------------------------------------------------------- 1 | import App from './App.svelte' 2 | import './app.css' 3 | 4 | const app = new App({ 5 | target: document.getElementById('app'), 6 | }) 7 | 8 | export default app 9 | -------------------------------------------------------------------------------- /apps/vite-svelte/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /apps/vite-svelte/svelte.config.js: -------------------------------------------------------------------------------- 1 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' 2 | 3 | export default { 4 | // Consult https://svelte.dev/docs#compile-time-svelte-preprocess 5 | // for more information about preprocessors 6 | preprocess: vitePreprocess(), 7 | } 8 | -------------------------------------------------------------------------------- /apps/vite-svelte/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./index.html', './src/**/*.{html,js,svelte,ts}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | corePlugins: { 9 | preflight: false, 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /apps/vite-svelte/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({}) 4 | -------------------------------------------------------------------------------- /apps/vite-svelte/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/svelte/tsconfig.json", 3 | "compilerOptions": { 4 | "target": "ESNext", 5 | "useDefineForClassFields": true, 6 | "module": "ESNext", 7 | "resolveJsonModule": true, 8 | /** 9 | * Typecheck JS in `.svelte` and `.js` files by default. 10 | * Disable checkJs if you'd like to use dynamic types in JS. 11 | * Note that setting allowJs false does not prevent the use 12 | * of JS in `.svelte` files. 13 | */ 14 | "allowJs": true, 15 | "checkJs": true, 16 | "isolatedModules": true 17 | }, 18 | "references": [{ "path": "./tsconfig.node.json" }], 19 | "include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"] 20 | } 21 | -------------------------------------------------------------------------------- /apps/vite-svelte/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node" 6 | }, 7 | "include": ["vite.config.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /apps/vite-svelte/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { svelte } from '@sveltejs/vite-plugin-svelte' 2 | import utwm from 'unplugin-tailwindcss-mangle/vite' 3 | import { defineConfig } from 'vite' 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [ 7 | svelte(), 8 | utwm({ 9 | classMapOutput: true, 10 | }), 11 | ], 12 | }) 13 | -------------------------------------------------------------------------------- /apps/vite-vanilla/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/vite-vanilla/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/vite-vanilla/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-vanilla", 3 | "type": "module", 4 | "version": "0.0.0", 5 | "private": true, 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview", 10 | "prepare": "tw-patch install", 11 | "tw-extract": "tw-patch extract" 12 | }, 13 | "devDependencies": { 14 | "autoprefixer": "^10.4.20", 15 | "postcss": "^8.4.47", 16 | "tailwindcss": "^3.4.14", 17 | "typescript": "^5.6.3", 18 | "vite": "^5.4.9" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /apps/vite-vanilla/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/vite-vanilla/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/vite-vanilla/src/counter.ts: -------------------------------------------------------------------------------- 1 | export function setupCounter(element: HTMLButtonElement) { 2 | let counter = 0 3 | const setCounter = (count: number) => { 4 | counter = count 5 | element.innerHTML = `
count is ${counter}
` 6 | } 7 | element.addEventListener('click', () => setCounter(counter + 1)) 8 | setCounter(0) 9 | } 10 | -------------------------------------------------------------------------------- /apps/vite-vanilla/src/typescript.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/vite-vanilla/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/vite-vanilla/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | corePlugins: { 9 | preflight: false, 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /apps/vite-vanilla/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({}) 4 | -------------------------------------------------------------------------------- /apps/vite-vanilla/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["ESNext", "DOM"], 5 | "useDefineForClassFields": true, 6 | "module": "ESNext", 7 | "moduleResolution": "Node", 8 | "resolveJsonModule": true, 9 | "strict": true, 10 | "noImplicitReturns": true, 11 | "noUnusedLocals": true, 12 | "noUnusedParameters": true, 13 | "noEmit": true, 14 | "esModuleInterop": true, 15 | "isolatedModules": true, 16 | "skipLibCheck": true 17 | }, 18 | "include": ["src"] 19 | } 20 | -------------------------------------------------------------------------------- /apps/vite-vanilla/vite.config.ts: -------------------------------------------------------------------------------- 1 | import utwm from 'unplugin-tailwindcss-mangle/vite' 2 | import { defineConfig } from 'vite' 3 | // https://vitejs.dev/config/ 4 | export default defineConfig({ 5 | plugins: [ 6 | utwm({ 7 | classMapOutput: true, 8 | }), 9 | ], 10 | }) 11 | -------------------------------------------------------------------------------- /apps/vite-vue/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/vite-vue/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /apps/vite-vue/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + Vue + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/vite-vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-vue", 3 | "type": "module", 4 | "version": "0.0.0", 5 | "private": true, 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview", 10 | "prepare": "tw-patch install", 11 | "tw-extract": "tw-patch extract" 12 | }, 13 | "dependencies": { 14 | "vue": "^3.5.12" 15 | }, 16 | "devDependencies": { 17 | "@vitejs/plugin-vue": "^5.1.4", 18 | "autoprefixer": "^10.4.20", 19 | "postcss": "^8.4.47", 20 | "sass": "^1.80.3", 21 | "tailwindcss": "^3.4.14", 22 | "typescript": "^5.6.3", 23 | "vite": "^5.4.9", 24 | "vue-tsc": "^2.1.6" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /apps/vite-vue/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/vite-vue/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/vite-vue/src/assets/vue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/vite-vue/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 33 | 34 | 39 | -------------------------------------------------------------------------------- /apps/vite-vue/src/index.scss: -------------------------------------------------------------------------------- 1 | .red { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /apps/vite-vue/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import './style.css' 4 | import './index.scss' 5 | 6 | console.log('bg-[#929292]') 7 | 8 | createApp(App).mount('#app') 9 | -------------------------------------------------------------------------------- /apps/vite-vue/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/vite-vue/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ['./index.html', './src/**/*.{js,ts,jsx,tsx,vue}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | corePlugins: { 9 | preflight: false, 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /apps/vite-vue/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({ 4 | mangle: { 5 | preserveFunction: ['twMerge'], 6 | }, 7 | }) 8 | -------------------------------------------------------------------------------- /apps/vite-vue/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "jsx": "preserve", 5 | "lib": ["ESNext", "DOM"], 6 | "useDefineForClassFields": true, 7 | "module": "ESNext", 8 | "moduleResolution": "Node", 9 | "resolveJsonModule": true, 10 | "strict": true, 11 | "noEmit": true, 12 | "esModuleInterop": true, 13 | "isolatedModules": true, 14 | "skipLibCheck": true 15 | }, 16 | "references": [{ "path": "./tsconfig.node.json" }], 17 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] 18 | } 19 | -------------------------------------------------------------------------------- /apps/vite-vue/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "target": "ESNext", 5 | "module": "ESNext", 6 | "moduleResolution": "Bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /apps/vite-vue/vite.config.ts: -------------------------------------------------------------------------------- 1 | import vue from '@vitejs/plugin-vue' 2 | import { register } from 'tsx/esm/api' 3 | import { defineConfig } from 'vite' 4 | 5 | register() 6 | const { default: utwm } = await import('unplugin-tailwindcss-mangle/vite') 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | plugins: [ 10 | vue(), 11 | utwm({ 12 | classMapOutput: true, 13 | }), 14 | ], 15 | css: { 16 | preprocessorOptions: { 17 | scss: { 18 | silenceDeprecations: ['legacy-js-api'], 19 | }, 20 | }, 21 | }, 22 | }) 23 | -------------------------------------------------------------------------------- /apps/webpack5-vue3/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | not ie 11 5 | -------------------------------------------------------------------------------- /apps/webpack5-vue3/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /apps/webpack5-vue3/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /apps/webpack5-vue3/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset', 4 | ], 5 | } 6 | -------------------------------------------------------------------------------- /apps/webpack5-vue3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack5-vue3", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "preview": "vite preview", 9 | "lint": "vue-cli-service lint", 10 | "prepare": "tw-patch install", 11 | "tw-extract": "tw-patch extract" 12 | }, 13 | "dependencies": { 14 | "core-js": "^3.38.1", 15 | "vue": "^3.5.12", 16 | "vue-router": "^4.4.5" 17 | }, 18 | "devDependencies": { 19 | "@typescript-eslint/eslint-plugin": "^8.11.0", 20 | "@typescript-eslint/parser": "^8.11.0", 21 | "@vue/cli-plugin-babel": "~5.0.8", 22 | "@vue/cli-plugin-eslint": "~5.0.8", 23 | "@vue/cli-plugin-router": "~5.0.8", 24 | "@vue/cli-plugin-typescript": "~5.0.8", 25 | "@vue/cli-service": "~5.0.8", 26 | "@vue/eslint-config-standard": "^8.0.1", 27 | "@vue/eslint-config-typescript": "^14.1.2", 28 | "autoprefixer": "^10.4.20", 29 | "eslint": "^9.13.0", 30 | "eslint-plugin-import": "^2.31.0", 31 | "eslint-plugin-node": "^11.1.0", 32 | "eslint-plugin-promise": "^7.1.0", 33 | "eslint-plugin-vue": "^9.29.1", 34 | "postcss": "^8.4.47", 35 | "sass": "^1.80.3", 36 | "sass-loader": "^16.0.2", 37 | "tailwindcss": "^3.4.14", 38 | "typescript": "~5.6.3" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /apps/webpack5-vue3/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/webpack5-vue3/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonofmagic/tailwindcss-mangle/b21d639f21c84418fd04cdf2961ed77ea07fa141/apps/webpack5-vue3/public/favicon.ico -------------------------------------------------------------------------------- /apps/webpack5-vue3/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /apps/webpack5-vue3/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonofmagic/tailwindcss-mangle/b21d639f21c84418fd04cdf2961ed77ea07fa141/apps/webpack5-vue3/src/assets/logo.png -------------------------------------------------------------------------------- /apps/webpack5-vue3/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 19 | 37 | -------------------------------------------------------------------------------- /apps/webpack5-vue3/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | 5 | createApp(App).use(router).mount('#app') 6 | -------------------------------------------------------------------------------- /apps/webpack5-vue3/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import type { RouteRecordRaw } from 'vue-router' 2 | import { createRouter, createWebHistory } from 'vue-router' 3 | import HomeView from '../views/HomeView.vue' 4 | 5 | const routes: Array = [ 6 | { 7 | path: '/', 8 | name: 'home', 9 | component: HomeView, 10 | }, 11 | { 12 | path: '/about', 13 | name: 'about', 14 | // route level code-splitting 15 | // this generates a separate chunk (about.[hash].js) for this route 16 | // which is lazy-loaded when the route is visited. 17 | component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue'), 18 | }, 19 | ] 20 | 21 | const router = createRouter({ 22 | history: createWebHistory(process.env.BASE_URL), 23 | routes, 24 | }) 25 | 26 | export default router 27 | -------------------------------------------------------------------------------- /apps/webpack5-vue3/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue' 4 | const component: DefineComponent<{}, {}, any> 5 | export default component 6 | } 7 | -------------------------------------------------------------------------------- /apps/webpack5-vue3/src/views/AboutView.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /apps/webpack5-vue3/src/views/HomeView.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | -------------------------------------------------------------------------------- /apps/webpack5-vue3/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ['./src/**/*.{js,ts,jsx,tsx,vue}'], 4 | darkMode: 'class', 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | corePlugins: { 10 | preflight: false, 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /apps/webpack5-vue3/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({}) 4 | -------------------------------------------------------------------------------- /apps/webpack5-vue3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "jsx": "preserve", 5 | "lib": [ 6 | "esnext", 7 | "dom", 8 | "dom.iterable", 9 | "scripthost" 10 | ], 11 | "useDefineForClassFields": true, 12 | "baseUrl": ".", 13 | "module": "esnext", 14 | "moduleResolution": "node", 15 | "paths": { 16 | "@/*": [ 17 | "src/*" 18 | ] 19 | }, 20 | "types": [ 21 | "webpack-env" 22 | ], 23 | "strict": true, 24 | "sourceMap": true, 25 | "allowSyntheticDefaultImports": true, 26 | "esModuleInterop": true, 27 | "forceConsistentCasingInFileNames": true, 28 | "skipLibCheck": true 29 | }, 30 | "include": [ 31 | "src/**/*.ts", 32 | "src/**/*.tsx", 33 | "src/**/*.vue", 34 | "tests/**/*.ts", 35 | "tests/**/*.tsx" 36 | ], 37 | "exclude": [ 38 | "node_modules" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /apps/webpack5-vue3/vue.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('@vue/cli-service') 2 | const utwm = require('unplugin-tailwindcss-mangle/webpack') 3 | 4 | module.exports = defineConfig({ 5 | transpileDependencies: true, 6 | // configureWebpack: (config) => { 7 | // config.plugins.push(utwm({ 8 | // classMapOutput: true, 9 | // })) 10 | // }, 11 | 12 | }) 13 | -------------------------------------------------------------------------------- /assets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "assets", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "", 6 | "author": "", 7 | "license": "ISC", 8 | "keywords": [], 9 | "main": "index.js", 10 | "scripts": { 11 | "prepare": "tw-patch install", 12 | "extract": "tw-patch extract" 13 | }, 14 | "dependencies": { 15 | "tailwindcss-patch": "^5.0.1", 16 | "unplugin-tailwindcss-mangle": "^4.0.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /commitlint.config.ts: -------------------------------------------------------------------------------- 1 | import type { UserConfig } from '@commitlint/types' 2 | // import { RuleConfigSeverity } from '@commitlint/types' 3 | 4 | export default { 5 | extends: ['@commitlint/config-conventional'], 6 | } 7 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import { icebreaker } from '@icebreakers/eslint-config' 2 | 3 | export default icebreaker( 4 | { 5 | ignores: ['**/fixtures/**'], 6 | }, 7 | ) 8 | -------------------------------------------------------------------------------- /lint-staged.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | '*.{js,jsx,mjs,ts,tsx,mts,vue}': [ 3 | 'eslint --fix', 4 | ], 5 | '*.{json,md,mdx,css,html,yml,yaml,scss}': [ 6 | // 'prettier --with-node-modules --ignore-path .prettierignore --write', 7 | 'eslint --fix', 8 | ], 9 | // for rust 10 | // '*.rs': ['cargo fmt --'], 11 | } 12 | -------------------------------------------------------------------------------- /packages/config/src/config.ts: -------------------------------------------------------------------------------- 1 | import type { UserConfig } from './types' 2 | import { createDefineConfig, loadConfig } from 'c12' 3 | import fs from 'fs-extra' 4 | import path from 'pathe' 5 | import { CONFIG_NAME } from './constants' 6 | import { getDefaultUserConfig } from './defaults' 7 | 8 | export function getConfig(cwd?: string) { 9 | return loadConfig({ 10 | name: CONFIG_NAME, 11 | defaults: { 12 | ...getDefaultUserConfig(), 13 | }, 14 | cwd, 15 | }) 16 | } 17 | 18 | export const defineConfig = createDefineConfig() 19 | 20 | export function initConfig(cwd: string) { 21 | return fs.outputFile( 22 | path.resolve(cwd, `${CONFIG_NAME}.config.ts`), 23 | `import { defineConfig } from 'tailwindcss-patch' 24 | 25 | export default defineConfig({}) 26 | `, 27 | 'utf8', 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /packages/config/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const CONFIG_NAME = 'tailwindcss-mangle' 2 | -------------------------------------------------------------------------------- /packages/config/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './config' 2 | export * from './constants' 3 | export * from './defaults' 4 | export * from './types' 5 | -------------------------------------------------------------------------------- /packages/config/test/__snapshots__/config.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`config > init config 1`] = ` 4 | "import { defineConfig } from 'tailwindcss-patch' 5 | 6 | export default defineConfig({}) 7 | " 8 | `; 9 | -------------------------------------------------------------------------------- /packages/config/test/__snapshots__/defaults.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`defaults > getDefaultPatchConfig 1`] = ` 4 | { 5 | "output": { 6 | "filename": ".tw-patch/tw-class-list.json", 7 | "loose": true, 8 | "removeUniversalSelector": true, 9 | }, 10 | "tailwindcss": {}, 11 | } 12 | `; 13 | 14 | exports[`defaults > getDefaultUserConfig 1`] = ` 15 | { 16 | "mangle": { 17 | "classListPath": ".tw-patch/tw-class-list.json", 18 | "classMapOutput": { 19 | "enable": false, 20 | "filename": ".tw-patch/tw-map-list.json", 21 | "loose": true, 22 | }, 23 | "disabled": false, 24 | "exclude": [], 25 | "include": [ 26 | /\\\\\\.\\(html\\|js\\|ts\\|jsx\\|tsx\\|vue\\|svelte\\|astro\\|elm\\|php\\|phtml\\|mdx\\|md\\)\\(\\?:\\$\\|\\\\\\?\\)/, 27 | /\\\\\\.\\(css\\|less\\|sass\\|scss\\|styl\\|stylus\\|pcss\\|postcss\\|sss\\)\\(\\?:\\$\\|\\\\\\?\\)/, 28 | ], 29 | "mangleClassFilter": [Function], 30 | "preserveFunction": [], 31 | }, 32 | "patch": { 33 | "output": { 34 | "filename": ".tw-patch/tw-class-list.json", 35 | "loose": true, 36 | "removeUniversalSelector": true, 37 | }, 38 | "tailwindcss": {}, 39 | }, 40 | } 41 | `; 42 | -------------------------------------------------------------------------------- /packages/config/test/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`config > 2.mangle-options 1`] = ` 4 | { 5 | "mangle": { 6 | "classGenerator": { 7 | "log": true, 8 | }, 9 | "classListPath": "zzzzz.json", 10 | "classMapOutput": { 11 | "enable": true, 12 | "filename": "ffff.json", 13 | "loose": false, 14 | }, 15 | "disabled": false, 16 | "exclude": [], 17 | "include": [ 18 | /\\\\\\.\\(html\\|js\\|ts\\|jsx\\|tsx\\|vue\\|svelte\\|astro\\|elm\\|php\\|phtml\\|mdx\\|md\\)\\(\\?:\\$\\|\\\\\\?\\)/, 19 | /\\\\\\.\\(css\\|less\\|sass\\|scss\\|styl\\|stylus\\|pcss\\|postcss\\|sss\\)\\(\\?:\\$\\|\\\\\\?\\)/, 20 | ], 21 | "mangleClassFilter": [Function], 22 | "preserveFunction": [], 23 | }, 24 | "patch": { 25 | "output": { 26 | "filename": "xxx/yyy/zzz.json", 27 | "loose": false, 28 | "removeUniversalSelector": false, 29 | }, 30 | "tailwindcss": { 31 | "cwd": "aaa/bbb/cc", 32 | }, 33 | }, 34 | } 35 | `; 36 | -------------------------------------------------------------------------------- /packages/config/test/config.test.ts: -------------------------------------------------------------------------------- 1 | import { initConfig } from '@/config' 2 | import fs from 'fs-extra' 3 | import path from 'pathe' 4 | 5 | describe('config', () => { 6 | it('init config', async () => { 7 | const cwd = path.resolve(__dirname, './fixtures/config/initConfig') 8 | await initConfig(cwd) 9 | const dest = path.resolve(cwd, 'tailwindcss-mangle.config.ts') 10 | expect(await fs.readFile(dest, 'utf8')).toMatchSnapshot() 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /packages/config/test/defaults.test.ts: -------------------------------------------------------------------------------- 1 | import { getDefaultPatchConfig, getDefaultUserConfig } from '@/defaults' 2 | import { createFilter } from '@rollup/pluginutils' 3 | import { omit } from 'lodash-es' 4 | 5 | function omitCwdPath(o: any) { 6 | return omit(o, ['tailwindcss.cwd', 'patch.tailwindcss.cwd']) 7 | } 8 | 9 | describe('defaults', () => { 10 | it('getDefaultPatchConfig', () => { 11 | expect(omitCwdPath(getDefaultPatchConfig())).toMatchSnapshot() 12 | }) 13 | 14 | it('getDefaultUserConfig', () => { 15 | expect(omitCwdPath(getDefaultUserConfig())).toMatchSnapshot() 16 | }) 17 | }) 18 | 19 | describe('createFilter', () => { 20 | it('case 0', () => { 21 | const config = getDefaultUserConfig() 22 | const filter = createFilter(config.mangle?.include, config.mangle?.exclude) 23 | expect(filter('xx/yy.js?a=1')).toBe(true) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /packages/config/test/fixtures/config/0.default/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({}) 4 | -------------------------------------------------------------------------------- /packages/config/test/fixtures/config/1.change-options/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({ 4 | patch: { 5 | output: { 6 | filename: 'xxx/yyy/zzz.json', 7 | loose: false, 8 | removeUniversalSelector: false, 9 | }, 10 | tailwindcss: { 11 | cwd: 'aaa/bbb/cc', 12 | }, 13 | }, 14 | }) 15 | -------------------------------------------------------------------------------- /packages/config/test/fixtures/config/2.mangle-options/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({ 4 | patch: { 5 | output: { 6 | filename: 'xxx/yyy/zzz.json', 7 | loose: false, 8 | removeUniversalSelector: false, 9 | }, 10 | tailwindcss: { 11 | cwd: 'aaa/bbb/cc', 12 | }, 13 | }, 14 | mangle: { 15 | mangleClassFilter(className) { 16 | return true 17 | }, 18 | classListPath: 'zzzzz.json', 19 | classGenerator: { 20 | log: true, 21 | }, 22 | disabled: false, 23 | classMapOutput: { 24 | enable: true, 25 | loose: false, 26 | filename: 'ffff.json', 27 | }, 28 | }, 29 | }) 30 | -------------------------------------------------------------------------------- /packages/config/test/fixtures/config/initConfig/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({}) 4 | -------------------------------------------------------------------------------- /packages/config/test/utils.ts: -------------------------------------------------------------------------------- 1 | import path from 'pathe' 2 | 3 | export const fixturesRoot = path.resolve(__dirname, './fixtures') 4 | -------------------------------------------------------------------------------- /packages/config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/*": [ 7 | "src/*" 8 | ] 9 | } 10 | }, 11 | "include": [ 12 | "src", 13 | "test" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/config/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup' 2 | 3 | export default defineConfig({ 4 | entry: [ 5 | 'src/index.ts', 6 | ], // , 'src/cli.ts'], 7 | shims: true, 8 | format: ['cjs', 'esm'], 9 | clean: true, 10 | dts: true, 11 | // cjsInterop: true, 12 | // splitting: true, 13 | }) 14 | -------------------------------------------------------------------------------- /packages/config/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'pathe' 2 | import { defineProject } from 'vitest/config' 3 | 4 | export default defineProject({ 5 | test: { 6 | alias: [ 7 | { 8 | find: '@', 9 | replacement: path.resolve(__dirname, './src'), 10 | }, 11 | ], 12 | globals: true, 13 | testTimeout: 60_000, 14 | }, 15 | }) 16 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # @tailwindcss-mangle/core 2 | 3 | The core of tailwindcss-mangle 4 | 5 | ## Usage 6 | 7 | // TODO 8 | -------------------------------------------------------------------------------- /packages/core/src/babel/index.ts: -------------------------------------------------------------------------------- 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 10 | -------------------------------------------------------------------------------- /packages/core/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const ignoreIdentifier = 'twIgnore' 2 | -------------------------------------------------------------------------------- /packages/core/src/css/index.ts: -------------------------------------------------------------------------------- 1 | import type { ICssHandlerOptions, IHandlerTransformResult } from '../types' 2 | import postcss from 'postcss' 3 | import { transformSelectorPostcssPlugin } from './plugins' 4 | 5 | export async function cssHandler(rawSource: string, options: ICssHandlerOptions): Promise { 6 | const acceptedPlugins = [transformSelectorPostcssPlugin(options)] 7 | const { id } = options 8 | try { 9 | const { css: code, map } = await postcss(acceptedPlugins).process(rawSource, { 10 | from: id, 11 | to: id, 12 | }) 13 | return { 14 | code, 15 | // @ts-ignore 16 | map, 17 | } 18 | } 19 | catch (_error) { 20 | return { 21 | code: rawSource, 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/core/src/env.ts: -------------------------------------------------------------------------------- 1 | import process from 'node:process' 2 | 3 | export const isProd = () => process.env.NODE_ENV === 'production' 4 | export const isDev = () => process.env.NODE_ENV === 'development' 5 | -------------------------------------------------------------------------------- /packages/core/src/html/index.ts: -------------------------------------------------------------------------------- 1 | import type { IHandlerTransformResult, IHtmlHandlerOptions } from '../types' 2 | import { Parser } from 'htmlparser2' 3 | import MagicString from 'magic-string' 4 | import { makeRegex, splitCode } from '../shared' 5 | 6 | export function htmlHandler(raw: string | MagicString, options: IHtmlHandlerOptions): IHandlerTransformResult { 7 | const { ctx, id } = options 8 | const { replaceMap, classGenerator } = ctx 9 | const ms: MagicString = typeof raw === 'string' ? new MagicString(raw) : raw 10 | const parser = new Parser({ 11 | onattribute(name, value) { 12 | if (name === 'class') { 13 | let needUpdate = false 14 | const arr = splitCode(value, { 15 | splitQuote: false, 16 | }) 17 | let rawValue = value 18 | for (const v of arr) { 19 | if (replaceMap.has(v)) { 20 | const gen = classGenerator.generateClassName(v) 21 | rawValue = rawValue.replace(makeRegex(v), gen.name) 22 | ctx.addToUsedBy(v, id) 23 | needUpdate = true 24 | } 25 | } 26 | needUpdate && ms.update(parser.startIndex + name.length + 2, parser.endIndex - 1, rawValue) 27 | } 28 | }, 29 | }) 30 | parser.write(ms.original) 31 | parser.end() 32 | return { 33 | code: ms.toString(), 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './css' 2 | export * from './ctx' 3 | export * from './html' 4 | export * from './js' 5 | export { ClassGenerator } from './shared' 6 | export * from './types' 7 | -------------------------------------------------------------------------------- /packages/core/src/js/utils.ts: -------------------------------------------------------------------------------- 1 | import type babel from '@babel/core' 2 | // const t = babel.types 3 | export function getStringLiteralCalleeName(path: babel.NodePath) { 4 | if (path.parentPath.isCallExpression()) { 5 | const callee = path.parentPath.get('callee') 6 | if (callee.isIdentifier()) { 7 | return callee.node.name 8 | } 9 | } 10 | } 11 | 12 | export function getTemplateElementCalleeName(path: babel.NodePath) { 13 | if (path.parentPath.isTemplateLiteral()) { 14 | const pp = path.parentPath 15 | if (pp.parentPath.isCallExpression()) { 16 | const callee = pp.parentPath.get('callee') 17 | if (callee.isIdentifier()) { 18 | return callee.node.name 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/core/src/math.ts: -------------------------------------------------------------------------------- 1 | export function between(x: number | null | undefined, min: number, max: number, included: boolean = false) { 2 | if (typeof x !== 'number') { 3 | return false 4 | } 5 | return included ? x >= min && x <= max : x > min && x < max 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/src/shared.ts: -------------------------------------------------------------------------------- 1 | export * from '@tailwindcss-mangle/shared' 2 | -------------------------------------------------------------------------------- /packages/core/src/types.ts: -------------------------------------------------------------------------------- 1 | import type { TransformResult } from 'unplugin' 2 | import type { Context } from './ctx' 3 | 4 | export interface IClassGeneratorContextItem { 5 | name: string 6 | usedBy: string[] 7 | } 8 | 9 | export interface IClassGeneratorOptions { 10 | reserveClassName?: (string | RegExp)[] 11 | customGenerate?: (original: string, opts: IClassGeneratorOptions, context: Record) => string | undefined 12 | log?: boolean 13 | exclude?: (string | RegExp)[] 14 | include?: (string | RegExp)[] 15 | ignoreClass?: (string | RegExp)[] 16 | classPrefix?: string 17 | } 18 | 19 | export interface IHandler { 20 | (code: string, options: IHandlerOptions): IHandlerTransformResult 21 | } 22 | 23 | export type IHandlerTransformResult = Exclude 24 | 25 | export interface IHandlerOptions { 26 | ctx: Context 27 | id?: string 28 | } 29 | 30 | export interface IHtmlHandlerOptions extends IHandlerOptions { 31 | 32 | } 33 | 34 | export interface IJsHandlerOptions extends IHandlerOptions { 35 | splitQuote?: boolean 36 | } 37 | 38 | export interface ICssHandlerOptions extends IHandlerOptions { 39 | ignoreVueScoped?: boolean 40 | } 41 | -------------------------------------------------------------------------------- /packages/core/test/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`common usage > hello-world 1`] = ` 4 | ".text-3xl { 5 | font-size: 1.875rem; 6 | line-height: 2.25rem 7 | } 8 | .font-bold { 9 | font-weight: 700 10 | } 11 | .underline { 12 | text-decoration-line: underline 13 | }" 14 | `; 15 | 16 | exports[`common usage > hello-world with js 1`] = ` 17 | ".bg-\\[\\#123456\\] { 18 | --tw-bg-opacity: 1; 19 | background-color: rgb(18 52 86 / var(--tw-bg-opacity, 1)) 20 | } 21 | .text-3xl { 22 | font-size: 1.875rem; 23 | line-height: 2.25rem 24 | } 25 | .font-bold { 26 | font-weight: 700 27 | } 28 | .underline { 29 | text-decoration-line: underline 30 | }" 31 | `; 32 | -------------------------------------------------------------------------------- /packages/core/test/exports.test.ts: -------------------------------------------------------------------------------- 1 | import { ClassGenerator, cssHandler, handleValue, htmlHandler, jsHandler } from '@/index' 2 | 3 | describe('exports', () => { 4 | it('exports should be defined', () => { 5 | for (const x of [ClassGenerator, cssHandler, handleValue, htmlHandler, jsHandler]) { 6 | expect(x).toBeDefined() 7 | } 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/core/test/fixtures/comment-ignore.js: -------------------------------------------------------------------------------- 1 | const clipPath = [`circle()`] 2 | 3 | document.documentElement.animate( 4 | { 5 | clipPath 6 | }, 7 | { 8 | duration: 500, 9 | easing: 'ease-out', 10 | pseudoElement: '::view-transition-new(root)' 11 | } 12 | ) 13 | 14 | document.documentElement.animate( 15 | { 16 | clipPath 17 | }, 18 | { 19 | duration: 500, 20 | easing: /* tw-mangle ignore */ 'ease-out', 21 | pseudoElement: '::view-transition-new(root)' 22 | } 23 | ) 24 | -------------------------------------------------------------------------------- /packages/core/test/fixtures/hello-world.css: -------------------------------------------------------------------------------- 1 | .text-3xl { 2 | font-size: 1.875rem; 3 | line-height: 2.25rem; 4 | } 5 | .font-bold { 6 | font-weight: 700; 7 | } 8 | .underline { 9 | text-decoration-line: underline; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/test/fixtures/hello-world.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |

12 | Hello world! 13 |

14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/core/test/fixtures/hello-world.js: -------------------------------------------------------------------------------- 1 | const el = document.getElementById('#app') 2 | el && el.classList.add('bg-[#123456]') 3 | -------------------------------------------------------------------------------- /packages/core/test/fixtures/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixtures", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "prepare": "tw-patch install", 8 | "tw-extract": "tw-patch extract" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "tailwindcss-patch": "workspace:^" 15 | } 16 | } -------------------------------------------------------------------------------- /packages/core/test/fixtures/preserve-fn-case0.js: -------------------------------------------------------------------------------- 1 | import { clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | 8 | cn('w-10 h-10 bg-red-500 and bg-red-500/50') 9 | 10 | cn(`w-2 h-2 bg-red-600 and bg-red-600/50`) 11 | 12 | twMerge('w-1 h-1 bg-red-400 and bg-red-400/50') -------------------------------------------------------------------------------- /packages/core/test/fixtures/preserve-fn-case0.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from 'clsx' 2 | import { twMerge } from 'tailwind-merge' 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | 8 | cn('w-10 h-10 bg-red-500 and bg-red-500/50') 9 | 10 | cn(`w-2 h-2 bg-red-600 and bg-red-600/50`) 11 | 12 | twMerge('w-1 h-1 bg-red-400 and bg-red-400/50') 13 | -------------------------------------------------------------------------------- /packages/core/test/fixtures/preserve-fn-case2.json: -------------------------------------------------------------------------------- 1 | [ 2 | "bg-[#B91C1C]", 3 | "bg-red-100", 4 | "drop-shadow", 5 | "filter", 6 | "flex", 7 | "flex-col", 8 | "hover:bg-red-100", 9 | "hover:bg-red-800", 10 | "items-center", 11 | "justify-between", 12 | "lg:dark:bg-red-100", 13 | "min-h-screen", 14 | "p-24", 15 | "p-3", 16 | "px-2", 17 | "py-1", 18 | "transition" 19 | ] -------------------------------------------------------------------------------- /packages/core/test/fixtures/preserve-fn-case2.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 28 | 29 | 45 | -------------------------------------------------------------------------------- /packages/core/test/fixtures/prod/0.js: -------------------------------------------------------------------------------- 1 | // Unterminated string constant 2 | if (isSvg) { 3 | let source = serializeString(svgNode); 4 | // add xml declaration 5 | source = '\r\n' + source; 6 | // convert svg source to URI data scheme. 7 | url = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(source); 8 | saveAs(url, "graph.svg"); 9 | onAlreadySerialized(); 10 | return; 11 | } -------------------------------------------------------------------------------- /packages/core/test/fixtures/prod/1.js: -------------------------------------------------------------------------------- 1 | // Missing semicolon. 2 | if (typeof parent === 'string') { 3 | parent = d.querySelectorAll(config.parent); 4 | if (parent.length > 1) { 5 | console.warn('WARNING: the given `parent` query(' + config.parent + ') matched more than one element, the first one will be used'); 6 | } 7 | if (parent.length === 0) { 8 | throw 'ERROR: the given `parent` doesn\'t exists!'; 9 | } 10 | parent = parent[0]; 11 | } -------------------------------------------------------------------------------- /packages/core/test/fixtures/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./preserve-fn-case2.vue"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | } 9 | 10 | -------------------------------------------------------------------------------- /packages/core/test/fixtures/trailing-slash-0.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 |
13 |
14 | Block with bg-red-500/50 class 15 |
16 |
17 | Block with bg-red-500 class 18 |
19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/core/test/fixtures/trailing-slash-0.js: -------------------------------------------------------------------------------- 1 | document.getElementById('#app').classList.add('bg-red-500 bg-red-500/50') 2 | -------------------------------------------------------------------------------- /packages/core/test/fixtures/trailing-slash-1.js: -------------------------------------------------------------------------------- 1 | document.querySelector('#app').classList.add('bg-red-500/50 bg-red-500') 2 | -------------------------------------------------------------------------------- /packages/core/test/fixtures/trailing-slash-2.js: -------------------------------------------------------------------------------- 1 | document.querySelector('#app').classList.add('bg-red-500 bg-red-500/50 bg-red-500 s bg-red-500') 2 | -------------------------------------------------------------------------------- /packages/core/test/fixtures/trailing-slash.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 |
13 |
14 | Block with bg-red-500 class 15 |
16 |
17 | Block with bg-red-500/50 class 18 |
19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/core/test/fixtures/vue-build-dist.js: -------------------------------------------------------------------------------- 1 | const mr = (e, t) => { 2 | const n = e.__vccOpts || e 3 | for (const [s, r] of t) n[s] = r 4 | return n 5 | } 6 | const fl = mr(cl, [['__scopeId', 'data-v-1d5be6d4']]) 7 | const ul = bo( 8 | '
', 13 | 1 14 | ) 15 | const al = Gs({ 16 | __name: 'App', 17 | setup(e) { 18 | return (t, n) => ( 19 | dr(), 20 | hr( 21 | fe, 22 | null, 23 | [ 24 | ul, 25 | Ae(fl, { 26 | msg: 'Vite + Vue' 27 | }) 28 | ], 29 | 64 30 | ) 31 | ) 32 | } 33 | }) 34 | const dl = mr(al, [['__scopeId', 'data-v-fd6461cd']]) 35 | Go(dl).mount('#app') 36 | -------------------------------------------------------------------------------- /packages/core/test/html.test.ts: -------------------------------------------------------------------------------- 1 | import { Context } from '@/ctx' 2 | import { htmlHandler } from '@/html' 3 | import { splitCode } from '@tailwindcss-mangle/shared' 4 | import { getTestCase } from './utils' 5 | 6 | describe('html handler', () => { 7 | // let classGenerator: ClassGenerator 8 | let ctx: Context 9 | beforeEach(() => { 10 | // classGenerator = new ClassGenerator() 11 | ctx = new Context() 12 | }) 13 | it('common usage', () => { 14 | const replaceMap = ctx.replaceMap 15 | 16 | for (const x of splitCode('text-3xl font-bold underline')) { 17 | replaceMap.set(x, '1') 18 | } 19 | const { code } = htmlHandler(getTestCase('hello-world.html'), { 20 | ctx, 21 | }) 22 | expect(code).toMatchSnapshot() 23 | }) 24 | 25 | it('trailing slash case', () => { 26 | const replaceMap = ctx.replaceMap 27 | 28 | for (const x of splitCode('bg-red-500 bg-red-500/50')) { 29 | replaceMap.set(x, '1') 30 | } 31 | const { code } = htmlHandler(getTestCase('trailing-slash.html'), { 32 | ctx, 33 | }) 34 | expect(code).toMatchSnapshot() 35 | }) 36 | 37 | it('trailing slash case 0', () => { 38 | const replaceMap = ctx.replaceMap 39 | 40 | for (const x of splitCode('bg-red-500 bg-red-500/50')) { 41 | replaceMap.set(x, '1') 42 | } 43 | const { code } = htmlHandler(getTestCase('trailing-slash-0.html'), { 44 | ctx, 45 | }) 46 | expect(code).toMatchSnapshot() 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /packages/core/test/index.test.ts: -------------------------------------------------------------------------------- 1 | import { getCss, getTestCase } from './utils' 2 | 3 | describe('common usage', () => { 4 | it('hello-world', async () => { 5 | expect(await getCss(getTestCase('hello-world.html'))).toMatchSnapshot() 6 | }) 7 | 8 | it('hello-world with js', async () => { 9 | expect(await getCss([getTestCase('hello-world.html'), getTestCase('hello-world.js')])).toMatchSnapshot() 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /packages/core/test/utils/index.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs' 2 | import path from 'pathe' 3 | import postcss from 'postcss' 4 | import tailwindcss from 'tailwindcss' 5 | 6 | export function getTestCase(caseName: string) { 7 | return fs.readFileSync(path.resolve(__dirname, '../fixtures', caseName), 'utf8') 8 | } 9 | // @tailwind base; 10 | // @tailwind components; 11 | export async function getCss(raw: string | string[]) { 12 | if (typeof raw === 'string') { 13 | raw = [raw] 14 | } 15 | const res = await postcss([ 16 | tailwindcss({ 17 | content: raw.map((x) => { 18 | return { 19 | raw: x, 20 | } 21 | }), 22 | }), 23 | ]).process('@tailwind utilities;') 24 | return res.css 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/*": [ 7 | "src/*" 8 | ] 9 | } 10 | }, 11 | "include": [ 12 | "src", 13 | "test" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/core/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup' 2 | 3 | export default defineConfig({ 4 | entry: [ 5 | 'src/index.ts', 6 | ], // , 'src/cli.ts'], 7 | shims: true, 8 | format: ['cjs', 'esm'], 9 | clean: true, 10 | dts: true, 11 | // cjsInterop: true, 12 | // splitting: true, 13 | }) 14 | -------------------------------------------------------------------------------- /packages/core/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'pathe' 2 | import { defineProject } from 'vitest/config' 3 | 4 | export default defineProject({ 5 | test: { 6 | alias: [ 7 | { 8 | find: '@', 9 | replacement: path.resolve(__dirname, './src'), 10 | }, 11 | ], 12 | globals: true, 13 | testTimeout: 60_000, 14 | }, 15 | }) 16 | -------------------------------------------------------------------------------- /packages/shared/README.md: -------------------------------------------------------------------------------- 1 | # @tailwindcss-mangle/shared 2 | 3 | The shared utils of tailwindcss-mangle 4 | 5 | ## Install 6 | 7 | ```bash 8 | i @tailwindcss-mangle/shared 9 | ``` 10 | 11 | ## Usage 12 | 13 | ### ClassGenerator 14 | 15 | the class generator of tailwindcss-mangle 16 | 17 | ### escapeStringRegexp 18 | 19 | ### makeRegex 20 | 21 | ### splitCode 22 | 23 | ### defaultMangleClassFilter 24 | -------------------------------------------------------------------------------- /packages/shared/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './classGenerator' 2 | export * from './regex' 3 | export * from './split' 4 | export * from './types' 5 | export * from './utils' 6 | -------------------------------------------------------------------------------- /packages/shared/src/regex.ts: -------------------------------------------------------------------------------- 1 | export function escapeStringRegexp(str: string) { 2 | if (typeof str !== 'string') { 3 | throw new TypeError('Expected a string') 4 | } 5 | return str.replaceAll(/[$()*+.?[\\\]^{|}]/g, '\\$&').replaceAll('-', '\\x2d') 6 | } 7 | 8 | export interface MakeRegexOptions { 9 | /** 10 | * 这是为了进行精确提取用的 11 | * 比如同时出现了 bg-500 bg-500/50, 12 | * true 只会提取 bg-500 13 | * 而 false 会提取 2 个 bg-500 14 | */ 15 | exact?: boolean 16 | } 17 | 18 | export function makeRegex( 19 | str: string, 20 | options: MakeRegexOptions = { 21 | exact: true, 22 | }, 23 | ) { 24 | return new RegExp(`(?<=^|[\\s"])${escapeStringRegexp(str)}${options.exact ? '(?=$|[\\s"])' : ''}`, 'g') 25 | } 26 | -------------------------------------------------------------------------------- /packages/shared/src/split.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line regexp/no-obscure-range 2 | export const validateFilterRE = /[\w\u00A0-\uFFFF%-?]/ 3 | 4 | export function isValidSelector(selector = ''): selector is string { 5 | return validateFilterRE.test(selector) 6 | } 7 | 8 | export function splitCode(code: string, options: { 9 | splitQuote?: boolean 10 | } = { splitQuote: true }) { 11 | const regex = options.splitQuote ? /[\s"]+/ : /\s+/ 12 | return code.split(regex).filter(x => isValidSelector(x)) 13 | } 14 | -------------------------------------------------------------------------------- /packages/shared/src/types.ts: -------------------------------------------------------------------------------- 1 | export interface IClassGeneratorContextItem { 2 | name: string 3 | usedBy: Set 4 | } 5 | 6 | export interface IClassGeneratorOptions { 7 | reserveClassName?: (string | RegExp)[] 8 | customGenerate?: (original: string, opts: IClassGeneratorOptions, context: Record) => string | undefined 9 | log?: boolean 10 | exclude?: (string | RegExp)[] 11 | include?: (string | RegExp)[] 12 | ignoreClass?: (string | RegExp)[] 13 | classPrefix?: string 14 | } 15 | 16 | export interface IClassGenerator { 17 | newClassMap: Record 18 | newClassSize: number 19 | context: Record 20 | } 21 | -------------------------------------------------------------------------------- /packages/shared/test/__snapshots__/reg.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`regex > trailing slash should exact match case 0 1`] = ` 4 | [ 5 | [ 6 | "bg-red-500", 7 | ], 8 | [ 9 | "bg-red-500", 10 | ], 11 | ] 12 | `; 13 | 14 | exports[`regex > trailing slash should exact match case 1 1`] = ` 15 | [ 16 | [ 17 | "bg-red-500", 18 | ], 19 | ] 20 | `; 21 | 22 | exports[`regex > trailing slash should exact match case 2 1`] = ` 23 | [ 24 | [ 25 | "bg-red-500", 26 | ], 27 | [ 28 | "bg-red-500", 29 | ], 30 | ] 31 | `; 32 | 33 | exports[`regex > z-10 regex 1`] = ` 34 | [ 35 | [ 36 | "z-10", 37 | ], 38 | ] 39 | `; 40 | -------------------------------------------------------------------------------- /packages/shared/test/classGenerator.test.ts: -------------------------------------------------------------------------------- 1 | import { ClassGenerator } from '@/classGenerator' 2 | 3 | describe('classGenerator', () => { 4 | it('size 26*27+1', () => { 5 | const result: string[] = [] 6 | const clsGen = new ClassGenerator() 7 | for (let i = 0; i < 26 * 27 + 1; i++) { 8 | result.push(clsGen.defaultClassGenerate()) 9 | clsGen.newClassSize++ 10 | } 11 | expect(result).toMatchSnapshot() 12 | }) 13 | 14 | it('26*27+1', () => { 15 | const clsGen = new ClassGenerator() 16 | clsGen.newClassSize = 26 * 27 17 | expect(clsGen.defaultClassGenerate()).toBe('tw-aaa') 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /packages/shared/test/reg.test.ts: -------------------------------------------------------------------------------- 1 | import { makeRegex } from '@/regex' 2 | 3 | describe('regex', () => { 4 | it('z-10 regex', () => { 5 | const testCase = 'z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex' 6 | const regex = makeRegex('z-10') 7 | const arr = [...testCase.matchAll(regex)] 8 | expect(arr.length).toBe(1) 9 | expect(arr).toMatchSnapshot() 10 | }) 11 | 12 | it('trailing slash should exact match case 0', () => { 13 | const testCase = 'bg-red-500 bg-red-500/50' 14 | const regex = makeRegex('bg-red-500', { 15 | exact: false, 16 | }) 17 | const arr = [...testCase.matchAll(regex)] 18 | expect(arr.length).toBe(2) 19 | expect(arr).toMatchSnapshot() 20 | }) 21 | 22 | it('trailing slash should exact match case 1', () => { 23 | const testCase = 'bg-red-500 bg-red-500/50' 24 | const regex = makeRegex('bg-red-500') 25 | const arr = [...testCase.matchAll(regex)] 26 | expect(arr.length).toBe(1) 27 | expect(arr).toMatchSnapshot() 28 | }) 29 | 30 | it('trailing slash should exact match case 2', () => { 31 | const testCase = 's bg-red-500 bg-red-500/50 bg-red-500' 32 | const regex = makeRegex('bg-red-500') 33 | const arr = [...testCase.matchAll(regex)] 34 | expect(arr.length).toBe(2) 35 | expect(arr).toMatchSnapshot() 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /packages/shared/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/*": [ 7 | "src/*" 8 | ] 9 | } 10 | }, 11 | "include": [ 12 | "src", 13 | "test" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/shared/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup' 2 | 3 | export default defineConfig({ 4 | entry: [ 5 | 'src/index.ts', 6 | ], // , 'src/cli.ts'], 7 | shims: true, 8 | format: ['cjs', 'esm'], 9 | clean: true, 10 | dts: true, 11 | // cjsInterop: true, 12 | // splitting: true, 13 | }) 14 | -------------------------------------------------------------------------------- /packages/shared/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'pathe' 2 | import { defineProject } from 'vitest/config' 3 | 4 | export default defineProject({ 5 | test: { 6 | alias: [ 7 | { 8 | find: '@', 9 | replacement: path.resolve(__dirname, './src'), 10 | }, 11 | ], 12 | globals: true, 13 | testTimeout: 60_000, 14 | }, 15 | }) 16 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/bin/tw-patch.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../dist/cli.js') 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/dev/bin.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S npx tsx 2 | import '../src/cli.ts' 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/index.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/src/babel/index.ts: -------------------------------------------------------------------------------- 1 | import _babelGenerate from '@babel/generator' 2 | import _babelTraverse from '@babel/traverse' 3 | 4 | export { parse, parseExpression } from '@babel/parser' 5 | 6 | export function _interopDefaultCompat(e: any) { 7 | return e && typeof e === 'object' && 'default' in e ? e.default : e 8 | } 9 | 10 | export const generate = _interopDefaultCompat(_babelGenerate) as typeof _babelGenerate 11 | 12 | export const traverse = _interopDefaultCompat(_babelTraverse) as typeof _babelTraverse 13 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const pkgName = 'tailwindcss-patch' 2 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/src/core/index.ts: -------------------------------------------------------------------------------- 1 | export * from './cache' 2 | export * from './patcher' 3 | export * from './patches' 4 | export * from './runtime' 5 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/src/core/patches/index.ts: -------------------------------------------------------------------------------- 1 | export * from './exportContext' 2 | export * from './supportCustomUnits' 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/src/defaults.ts: -------------------------------------------------------------------------------- 1 | import type { InternalPatchOptions, PatchOptions } from './types' 2 | import process from 'node:process' 3 | import { defu } from '@tailwindcss-mangle/shared' 4 | 5 | export function getDefaultPatchOptions() { 6 | return { 7 | packageName: 'tailwindcss', 8 | applyPatches: { 9 | exportContext: true, 10 | extendLengthUnits: false, 11 | }, 12 | overwrite: true, 13 | filter: () => true, 14 | } 15 | } 16 | 17 | export function getPatchOptions(options?: PatchOptions) { 18 | return defu[]>( 19 | options, 20 | { 21 | output: { 22 | removeUniversalSelector: true, 23 | }, 24 | basedir: process.cwd(), 25 | }, 26 | getDefaultPatchOptions(), 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './core' 2 | export { default as logger } from './logger' 3 | export * from './types' 4 | export { defineConfig } from '@tailwindcss-mangle/config' 5 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/src/logger.ts: -------------------------------------------------------------------------------- 1 | import { createConsola } from 'consola' 2 | 3 | const logger = createConsola() 4 | 5 | export default logger 6 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/src/utils.ts: -------------------------------------------------------------------------------- 1 | export function isObject(val: any) { 2 | return val !== null && typeof val === 'object' && Array.isArray(val) === false 3 | }; 4 | 5 | export interface StringChange { 6 | start: number 7 | end: number 8 | replacement: string 9 | } 10 | 11 | /** 12 | * Apply the changes to the string such that a change in the length 13 | * of the string does not break the indexes of the subsequent changes. 14 | */ 15 | export function spliceChangesIntoString(str: string, changes: StringChange[]) { 16 | // If there are no changes, return the original string 17 | if (!changes[0]) { 18 | return str 19 | } 20 | 21 | // Sort all changes in order to make it easier to apply them 22 | changes.sort((a, b) => { 23 | return a.end - b.end || a.start - b.start 24 | }) 25 | 26 | // Append original string between each chunk, and then the chunk itself 27 | // This is sort of a String Builder pattern, thus creating less memory pressure 28 | let result = '' 29 | 30 | let previous = changes[0] 31 | 32 | result += str.slice(0, previous.start) 33 | result += previous.replacement 34 | 35 | for (let i = 1; i < changes.length; ++i) { 36 | const change = changes[i] 37 | 38 | result += str.slice(previous.end, change.start) 39 | result += change.replacement 40 | 41 | previous = change 42 | } 43 | 44 | // Add leftover string from last chunk to end 45 | result += str.slice(previous.end) 46 | 47 | return result 48 | } 49 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/__snapshots__/context.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`common usage > hello-world 1`] = ` 4 | ".text-3xl { 5 | font-size: 1.875rem; 6 | line-height: 2.25rem 7 | } 8 | .font-bold { 9 | font-weight: 700 10 | } 11 | .underline { 12 | text-decoration-line: underline 13 | }" 14 | `; 15 | 16 | exports[`common usage > hello-world with js 1`] = ` 17 | ".bg-\\[\\#123456\\] { 18 | --tw-bg-opacity: 1; 19 | background-color: rgb(18 52 86 / var(--tw-bg-opacity)) 20 | } 21 | .text-3xl { 22 | font-size: 1.875rem; 23 | line-height: 2.25rem 24 | } 25 | .font-bold { 26 | font-weight: 700 27 | } 28 | .underline { 29 | text-decoration-line: underline 30 | }" 31 | `; 32 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/__snapshots__/defaults.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`defaults > getDefaultPatchOptions 1`] = ` 4 | { 5 | "applyPatches": { 6 | "exportContext": true, 7 | "extendLengthUnits": false, 8 | }, 9 | "filter": [Function], 10 | "overwrite": true, 11 | "packageName": "tailwindcss", 12 | } 13 | `; 14 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/__snapshots__/v4.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`v4 > extractRawCandidates case 0 1`] = ` 4 | [ 5 | "--x", 6 | "bg-[#123456]", 7 | "charset", 8 | "class", 9 | "content", 10 | "en", 11 | "initial-scale", 12 | "lang", 13 | "name", 14 | "text-(--x)", 15 | "text-xs", 16 | "viewport", 17 | "width", 18 | ] 19 | `; 20 | 21 | exports[`v4 > tailwindcssPatcher case 0 1`] = ` 22 | [ 23 | "bg-[#123456]", 24 | "content", 25 | "text-(--x)", 26 | "text-xs", 27 | ] 28 | `; 29 | 30 | exports[`v4 > tailwindcssPatcher case 1 1`] = ` 31 | [ 32 | "bg-[#123456]", 33 | "bg-gradient-to-b", 34 | "content", 35 | "from-[#2f73f1]", 36 | "h-[30px]", 37 | "text-(--x)", 38 | "text-xs", 39 | "to-[#4bcefd]", 40 | "w-[323px]", 41 | ] 42 | `; 43 | 44 | exports[`v4 > tailwindcssPatcher getClassSet case 2 1`] = ` 45 | Set { 46 | "bg-[#123456]", 47 | "bg-gradient-to-b", 48 | "content", 49 | "from-[#2f73f1]", 50 | "h-[30px]", 51 | "text-(--x)", 52 | "text-xs", 53 | "to-[#4bcefd]", 54 | "w-[323px]", 55 | } 56 | `; 57 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/config.test.ts: -------------------------------------------------------------------------------- 1 | import { getConfig, getDefaultMangleUserConfig, getDefaultUserConfig, initConfig } from '@tailwindcss-mangle/config' 2 | import { deleteAsync } from 'del' 3 | import { existsSync } from 'fs-extra' 4 | import { resolve } from 'pathe' 5 | import { fixturesRoot } from './utils' 6 | 7 | describe('config', () => { 8 | it('0.default', async () => { 9 | const cwd = resolve(fixturesRoot, './config/0.default') 10 | const configPath = resolve(cwd, 'tailwindcss-mangle.config.ts') 11 | if (existsSync(configPath)) { 12 | await deleteAsync(configPath) 13 | } 14 | 15 | await initConfig(cwd) 16 | expect(existsSync(configPath)).toBe(true) 17 | 18 | const { config } = await getConfig(cwd) 19 | expect(config).toEqual(getDefaultUserConfig()) 20 | }) 21 | 22 | it('1.change-options', async () => { 23 | const cwd = resolve(fixturesRoot, './config/1.change-options') 24 | const { config } = await getConfig(cwd) 25 | expect(config).toEqual({ 26 | patch: { 27 | output: { 28 | filename: 'xxx/yyy/zzz.json', 29 | loose: false, 30 | removeUniversalSelector: false, 31 | }, 32 | tailwindcss: { 33 | cwd: 'aaa/bbb/cc', 34 | }, 35 | }, 36 | mangle: getDefaultMangleUserConfig(), 37 | }) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/defaults.test.ts: -------------------------------------------------------------------------------- 1 | import { getDefaultPatchOptions } from '@/defaults' 2 | 3 | describe('defaults', () => { 4 | it('getDefaultPatchOptions', () => { 5 | expect(getDefaultPatchOptions()).toMatchSnapshot() 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/apps/0.common/.tw-patch/tw-class-list.json: -------------------------------------------------------------------------------- 1 | [ 2 | "font-bold", 3 | "text-3xl", 4 | "underline" 5 | ] -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/apps/0.common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "0.common", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "tw-init": "npx tw-patch init", 8 | "tw-extract": "npx tw-patch extract" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC" 13 | } -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/apps/0.common/postcss.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: { 6 | config: path.resolve(__dirname, './tailwind.config.js') 7 | }, 8 | autoprefixer: {}, 9 | } 10 | } -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/apps/0.common/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |

12 | Hello world! 13 |

14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/apps/0.common/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | /** @type {import('tailwindcss').Config} */ 3 | module.exports = { 4 | content: [path.resolve(__dirname, "./src/**/*.{html,js}")], 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | } -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/apps/0.common/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({}) 4 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/apps/0.common/tailwindcss-patch.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({}) 4 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/apps/1.default/.tw-patch/tw-class-list.json: -------------------------------------------------------------------------------- 1 | [ 2 | "font-bold", 3 | "text-3xl", 4 | "underline" 5 | ] -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/apps/1.default/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "1.default", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "tw-init": "npx tw-patch init", 8 | "tw-extract": "npx tw-patch extract" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC" 13 | } 14 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/apps/1.default/postcss.config.js: -------------------------------------------------------------------------------- 1 | // const path = require('path') 2 | 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {} 6 | } 7 | } -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/apps/1.default/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |

12 | Hello world! 13 |

14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/apps/1.default/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | /** @type {import('tailwindcss').Config} */ 3 | module.exports = { 4 | content: ["./src/**/*.{html,js}"],//[path.resolve(__dirname, "./src/**/*.{html,js}")], 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | } -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/apps/1.default/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({}) 4 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/apps/1.default/tailwindcss-patch.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({}) 4 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/cache/index.json: -------------------------------------------------------------------------------- 1 | ["*","bg-[#123456]","font-bold","text-3xl","underline"] 2 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/cache/merge-multiple-context.json: -------------------------------------------------------------------------------- 1 | ["*","text-[99px]","text-[100px]"] 2 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/cache/raw-method.json: -------------------------------------------------------------------------------- 1 | ["a","b","c"] 2 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/cli/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/config/0.default/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({}) 4 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/config/1.change-options/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({ 4 | patch: { 5 | output: { 6 | filename: 'xxx/yyy/zzz.json', 7 | loose: false, 8 | removeUniversalSelector: false, 9 | }, 10 | tailwindcss: { 11 | cwd: 'aaa/bbb/cc', 12 | }, 13 | }, 14 | }) 15 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/config/1.change-options/tailwindcss-patch.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({ 4 | output: { 5 | filename: 'xxx/yyy/zzz.json', 6 | loose: false, 7 | removeUniversalSelector: false, 8 | }, 9 | tailwindcss: { 10 | cwd: 'aaa/bbb/cc', 11 | }, 12 | }) 13 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/hello-world.css: -------------------------------------------------------------------------------- 1 | .text-3xl { 2 | font-size: 1.875rem; 3 | line-height: 2.25rem; 4 | } 5 | .font-bold { 6 | font-weight: 700; 7 | } 8 | .underline { 9 | text-decoration-line: underline; 10 | } 11 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/hello-world.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |

12 | Hello world! 13 |

14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/hello-world.js: -------------------------------------------------------------------------------- 1 | const el = document.getElementById('#app') 2 | el && el.classList.add('bg-[#123456]') 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/hello-world.wxml: -------------------------------------------------------------------------------- 1 | 111 -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/img-url.jsx: -------------------------------------------------------------------------------- 1 | export function Hello() { 2 | return
3 | } 4 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/postcss7-compat/lib/jit/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = _default; 7 | 8 | var _setupTrackingContext = _interopRequireDefault(require("./lib/setupTrackingContext")); 9 | 10 | var _setupWatchingContext = _interopRequireDefault(require("./lib/setupWatchingContext")); 11 | 12 | var _sharedState = require("./lib/sharedState"); 13 | 14 | var _processTailwindFeatures = _interopRequireDefault(require("./processTailwindFeatures")); 15 | 16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 17 | 18 | function _default(configOrPath = {}) { 19 | return [_sharedState.env.DEBUG && function (root) { 20 | console.log('\n'); 21 | console.time('JIT TOTAL'); 22 | return root; 23 | }, function (root, result) { 24 | let setupContext = _sharedState.env.TAILWIND_MODE === 'watch' ? (0, _setupWatchingContext.default)(configOrPath) : (0, _setupTrackingContext.default)(configOrPath); 25 | (0, _processTailwindFeatures.default)(setupContext)(root, result); 26 | }, _sharedState.env.DEBUG && function (root) { 27 | console.timeEnd('JIT TOTAL'); 28 | console.log('\n'); 29 | return root; 30 | }].filter(Boolean); 31 | } -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/trailing-slash.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/v4/.gitignore: -------------------------------------------------------------------------------- 1 | !patch/dist -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/v4/deep/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
10 | 11 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/v4/index.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss" ; 2 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/v4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
10 | 11 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/v4/patch/dist/a.mjs: -------------------------------------------------------------------------------- 1 | var K = ["cm","mm","Q","in","pc","pt","px","em","ex","ch","rem","lh","rlh","vw","vh","vmin","vmax","vb","vi","svw","svh","lvw","lvh","dvw","dvh","cqw","cqh","cqi","cqb","cqmin","cqmax","rpx"], 2 | Y = new RegExp(`^${g.source}(${K.join("|")})$`); -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/.eslintignore: -------------------------------------------------------------------------------- 1 | lts 2 | 3.* -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | es2021: true, 5 | node: true 6 | }, 7 | extends: [ 8 | 'standard' 9 | ], 10 | parserOptions: { 11 | ecmaVersion: 'latest', 12 | sourceType: 'module' 13 | }, 14 | rules: { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/2/lib/jit/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = _default; 7 | 8 | var _setupTrackingContext = _interopRequireDefault(require("./lib/setupTrackingContext")); 9 | 10 | var _setupWatchingContext = _interopRequireDefault(require("./lib/setupWatchingContext")); 11 | 12 | var _sharedState = require("./lib/sharedState"); 13 | 14 | var _processTailwindFeatures = _interopRequireDefault(require("./processTailwindFeatures")); 15 | 16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 17 | 18 | function _default(configOrPath = {}) { 19 | return [_sharedState.env.DEBUG && function (root) { 20 | console.log('\n'); 21 | console.time('JIT TOTAL'); 22 | return root; 23 | }, function (root, result) { 24 | let setupContext = _sharedState.env.TAILWIND_MODE === 'watch' ? (0, _setupWatchingContext.default)(configOrPath) : (0, _setupTrackingContext.default)(configOrPath); 25 | (0, _processTailwindFeatures.default)(setupContext)(root, result); 26 | }, _sharedState.env.DEBUG && function (root) { 27 | console.timeEnd('JIT TOTAL'); 28 | console.log('\n'); 29 | return root; 30 | }].filter(Boolean); 31 | } -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.2.6/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | if (process.env.OXIDE) { 3 | module.exports = require("./oxide/postcss-plugin"); 4 | } else { 5 | module.exports = require("./plugin"); 6 | } 7 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.2.7/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | if (process.env.OXIDE) { 3 | module.exports = require("./oxide/postcss-plugin"); 4 | } else { 5 | module.exports = require("./plugin"); 6 | } 7 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.3.0/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | if (process.env.OXIDE) { 3 | module.exports = require("./oxide/postcss-plugin"); 4 | } else { 5 | module.exports = require("./plugin"); 6 | } 7 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.3.1/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | if (process.env.OXIDE) { 3 | module.exports = require("./oxide/postcss-plugin"); 4 | } else { 5 | module.exports = require("./plugin"); 6 | } 7 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.3.2/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = require("./plugin"); 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.3.3/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = require("./plugin"); 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.3.4/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = require("./plugin"); 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.3.5/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = require("./plugin"); 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.3.6/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = require("./plugin"); 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.3.7/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = require("./plugin"); 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.4.0/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = require("./plugin"); 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.4.1/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = require("./plugin"); 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.4.12/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = require("./plugin"); 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.4.14/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = require("./plugin"); 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.4.2/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = require("./plugin"); 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.4.3/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = require("./plugin"); 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.4.4/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = require("./plugin"); 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.4.5/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = require("./plugin"); 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.4.6/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = require("./plugin"); 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/3.4.7/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = require("./plugin"); 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/README.md: -------------------------------------------------------------------------------- 1 | # tailwindcss-versions 2 | 3 | yarn add tailwindcss 4 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/install.js: -------------------------------------------------------------------------------- 1 | import { execa } from 'execa' 2 | // import { getCurrentFilename } from './utils.js' 3 | // import path from 'path' 4 | // import fs from 'fs/promises' 5 | 6 | const args = process.argv.slice(2) 7 | 8 | const version = args[0] 9 | 10 | if (version) { 11 | try { 12 | const cwd = process.cwd() 13 | // pnpm EPERM: operation not permitted, 14 | // try yarn 15 | const { stdout } = await execa('yarn', ['add', `tailwindcss${version}@npm:tailwindcss@${version}`], { 16 | cwd 17 | }).pipeStdout(process.stdout) 18 | // const filename = getCurrentFilename(import.meta.url) 19 | // const dirname = path.dirname(filename) 20 | // const nodeModulesPath = path.resolve(dirname, 'node_modules') 21 | // await fs.rmdir(path.resolve(nodeModulesPath, '.bin')) 22 | console.log(stdout) 23 | } catch (error) { 24 | console.error(error) 25 | } 26 | } else { 27 | console.warn('version is required!') 28 | } 29 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/lts/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = require("./plugin"); 3 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/fixtures/versions/utils.js: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs/promises' 2 | import path from 'pathe' 3 | import { fileURLToPath } from 'node:url' 4 | 5 | export function getCurrentFilename (importMetaUrl) { 6 | return fileURLToPath(importMetaUrl) 7 | } 8 | 9 | export async function ensureDir (p) { 10 | await fs.mkdir(p, { 11 | recursive: true 12 | }) 13 | } 14 | 15 | export async function copyFiles (arr) { 16 | if (Array.isArray(arr)) { 17 | for (let i = 0; i < arr.length; i++) { 18 | const { src, dest } = arr[i] 19 | await ensureDir(path.dirname(dest)) 20 | 21 | const isExisted = await fs.access(src).then(() => true).catch(() => false) 22 | 23 | if (isExisted) { 24 | await fs.copyFile(src, dest) 25 | } else { 26 | console.warn(`[warning]: 404 ${src}`) 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/inspector.test.ts: -------------------------------------------------------------------------------- 1 | import { inspectPostcssPlugin, inspectProcessTailwindFeaturesReturnContext } from '@/core/patches/exportContext/postcss-v3' 2 | import fs from 'fs-extra' 3 | import path from 'pathe' 4 | 5 | const tailwindcssCasePath = path.resolve(__dirname, 'fixtures') 6 | const twltsLibPath = path.resolve(tailwindcssCasePath, 'versions/3.3.1/lib') 7 | 8 | describe('inspector', () => { 9 | it('inspectPostcssPlugin patch snap', () => { 10 | const rawCode = fs.readFileSync(path.resolve(twltsLibPath, 'plugin.js'), 'utf8') 11 | const { code, hasPatched } = inspectPostcssPlugin(rawCode) 12 | expect(hasPatched).toBe(false) 13 | expect(code).toMatchSnapshot() 14 | const { code: newCode, hasPatched: hasPatched0 } = inspectPostcssPlugin(code) 15 | expect(hasPatched0).toBe(true) 16 | expect(code).toBe(newCode) 17 | }) 18 | 19 | it('inspectProcessTailwindFeaturesReturnContext patch snap', () => { 20 | const rawCode = fs.readFileSync(path.resolve(twltsLibPath, 'processTailwindFeatures.js'), 'utf8') 21 | const { code, hasPatched } = inspectProcessTailwindFeaturesReturnContext(rawCode) 22 | expect(hasPatched).toBe(false) 23 | expect(code).toMatchSnapshot() 24 | const { code: newCode, hasPatched: hasPatched0 } = inspectProcessTailwindFeaturesReturnContext(code) 25 | expect(hasPatched0).toBe(true) 26 | expect(code).toBe(newCode) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/patch.test.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import { monkeyPatchForSupportingCustomUnitV4 } from '@/core/patches/supportCustomUnits/index' 3 | import { fixturesRoot } from './utils' 4 | 5 | describe('patch', () => { 6 | describe('monkeyPatchForSupportingCustomUnitV4', () => { 7 | it('should patch v4', () => { 8 | const { files } = monkeyPatchForSupportingCustomUnitV4(path.resolve(fixturesRoot, 'v4/patch'), { 9 | 10 | }) 11 | expect(files.map((x) => { 12 | return { 13 | code: x.code, 14 | hasPatched: x.hasPatched, 15 | } 16 | })).toMatchSnapshot() 17 | }) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/pkg.test.ts: -------------------------------------------------------------------------------- 1 | import { isCI } from 'ci-info' 2 | import { getPackageInfoSync } from 'local-pkg' 3 | import { gte } from 'semver' 4 | 5 | describe.skipIf(isCI)('pkg', () => { 6 | it('tailwindcss', () => { 7 | const tailwindcss = getPackageInfoSync('tailwindcss') 8 | // @ts-ignore 9 | expect(gte(tailwindcss?.packageJson.version, '3.4.17')).toBe(true) 10 | }) 11 | 12 | it('tailwindcss paths 0', () => { 13 | const tailwindcss = getPackageInfoSync('tailwindcss', { 14 | paths: [import.meta.dirname], 15 | }) 16 | // @ts-ignore 17 | expect(gte(tailwindcss?.packageJson.version, '4.0.0')).toBe(true) 18 | }) 19 | 20 | it('tailwindcss paths 1', () => { 21 | const tailwindcss = getPackageInfoSync('tailwindcss', { 22 | paths: [process.cwd()], 23 | }) 24 | expect(tailwindcss?.packageJson.version).toBe('3.4.17') 25 | }) 26 | 27 | it('tailwindcss3', () => { 28 | const tailwindcss = getPackageInfoSync('tailwindcss-3') 29 | expect(tailwindcss?.packageJson.version).toBe('3.4.17') 30 | }) 31 | 32 | // it('tailwindcss4', () => { 33 | // const tailwindcss = getPackageInfoSync('tailwindcss-4') 34 | // expect(tailwindcss?.packageJson.version).toBe('4.0.6') 35 | // }) 36 | }) 37 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/postcss7-v2.test.ts: -------------------------------------------------------------------------------- 1 | import { inspectPostcssPlugin, inspectProcessTailwindFeaturesReturnContext } from '@/core/patches/exportContext/postcss-v2' 2 | import { getTestCase } from './utils' 3 | 4 | describe('postcss7-compat', () => { 5 | it('processTailwindFeatures patch', async () => { 6 | const code = getTestCase('postcss7-compat/lib/jit/processTailwindFeatures.js') 7 | const r = inspectProcessTailwindFeaturesReturnContext(code) 8 | 9 | expect(r.code).toMatchSnapshot() 10 | expect(r.hasPatched).toBe(false) 11 | }) 12 | 13 | it('jit plugins patch', async () => { 14 | const code = getTestCase('postcss7-compat/lib/jit/index.js') 15 | const r = inspectPostcssPlugin(code) 16 | 17 | expect(r.code).toMatchSnapshot() 18 | expect(r.hasPatched).toBe(false) 19 | }) 20 | 21 | it('jit plugins patch case 0', async () => { 22 | const code = getTestCase('postcss7-compat/lib/jit/index.js') 23 | let r = inspectPostcssPlugin(code) 24 | expect(r.hasPatched).toBe(false) 25 | r = inspectPostcssPlugin(r.code) 26 | expect(r.code).toMatchSnapshot() 27 | expect(r.hasPatched).toBe(true) 28 | }) 29 | }) 30 | 31 | // const XXXEnum = { 32 | // A: 1, 33 | // B: 2, 34 | // C: 'xxx' 35 | // } as const 36 | 37 | // type EnumType = (T)[keyof T] 38 | 39 | // function xx(x: EnumType) { 40 | 41 | // } 42 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/splice-changes-into-string.ts: -------------------------------------------------------------------------------- 1 | export interface StringChange { 2 | start: number 3 | end: number 4 | replacement: string 5 | } 6 | 7 | /** 8 | * Apply the changes to the string such that a change in the length 9 | * of the string does not break the indexes of the subsequent changes. 10 | */ 11 | export function spliceChangesIntoString(str: string, changes: StringChange[]) { 12 | // If there are no changes, return the original string 13 | if (!changes[0]) { 14 | return str 15 | } 16 | 17 | // Sort all changes in order to make it easier to apply them 18 | changes.sort((a, b) => { 19 | return a.end - b.end || a.start - b.start 20 | }) 21 | 22 | // Append original string between each chunk, and then the chunk itself 23 | // This is sort of a String Builder pattern, thus creating less memory pressure 24 | let result = '' 25 | 26 | let previous = changes[0] 27 | 28 | result += str.slice(0, previous.start) 29 | result += previous.replacement 30 | 31 | for (let i = 1; i < changes.length; ++i) { 32 | const change = changes[i] 33 | 34 | result += str.slice(previous.end, change.start) 35 | result += change.replacement 36 | 37 | previous = change 38 | } 39 | 40 | // Add leftover string from last chunk to end 41 | result += str.slice(previous.end) 42 | 43 | return result 44 | } 45 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/src/candidate.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonofmagic/tailwindcss-mangle/b21d639f21c84418fd04cdf2961ed77ea07fa141/packages/tailwindcss-patch/test/src/candidate.ts -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/src/utils/design-system.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonofmagic/tailwindcss-mangle/b21d639f21c84418fd04cdf2961ed77ea07fa141/packages/tailwindcss-patch/test/src/utils/design-system.ts -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { existsSync, statSync } from 'node:fs' 2 | import { deleteAsync } from 'del' 3 | import fs from 'fs-extra' 4 | import path from 'pathe' 5 | import { fixturesRoot } from './utils' 6 | 7 | describe('utils', () => { 8 | it('ensureDir', async () => { 9 | const dir = path.resolve(fixturesRoot, './xxx/yyy') 10 | if (existsSync(dir)) { 11 | await deleteAsync(dir) 12 | } 13 | expect(existsSync(dir)).toBe(false) 14 | await fs.ensureDir(dir) 15 | const stat = statSync(dir) 16 | expect(stat.isDirectory()).toBe(true) 17 | expect(existsSync(dir)).toBe(true) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/test/utils.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra' 2 | import path from 'pathe' 3 | import postcss from 'postcss' 4 | import tailwindcss from 'tailwindcss-3' 5 | 6 | export const fixturesRoot = path.resolve(__dirname, './fixtures') 7 | 8 | export const appRoot = path.resolve(fixturesRoot, './apps') 9 | 10 | export function getTestCase(caseName: string) { 11 | return fs.readFileSync(path.resolve(__dirname, 'fixtures', caseName), 'utf8') 12 | } 13 | // @tailwind base; 14 | // @tailwind components; 15 | export async function getCss(raw: string | string[]) { 16 | if (typeof raw === 'string') { 17 | raw = [raw] 18 | } 19 | const res = await postcss([ 20 | tailwindcss({ 21 | content: raw.map((x) => { 22 | return { 23 | raw: x, 24 | } 25 | }), 26 | }), 27 | ]).process('@tailwind utilities;', { 28 | from: undefined, 29 | }) 30 | return res.css 31 | } 32 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/*": [ 7 | "src/*" 8 | ] 9 | } 10 | }, 11 | "include": [ 12 | "src", 13 | "test", 14 | "dev" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup' 2 | 3 | export default defineConfig({ 4 | entry: ['src/index.ts', 'src/cli.ts'], 5 | shims: true, 6 | clean: true, 7 | format: ['cjs', 'esm'], // , 'esm' 8 | dts: true, 9 | cjsInterop: true, 10 | splitting: true, 11 | external: ['tailwindcss', '@tailwindcss/node', '@tailwindcss/oxide'], 12 | }) 13 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'pathe' 2 | import { defineProject } from 'vitest/config' 3 | 4 | export default defineProject({ 5 | test: { 6 | alias: [ 7 | { 8 | find: '@', 9 | replacement: path.resolve(__dirname, './src'), 10 | }, 11 | ], 12 | globals: true, 13 | testTimeout: 60_000, 14 | setupFiles: ['./vitest.setup.ts'], 15 | coverage: { 16 | enabled: true, 17 | all: false, 18 | }, 19 | }, 20 | }) 21 | -------------------------------------------------------------------------------- /packages/tailwindcss-patch/vitest.setup.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonofmagic/tailwindcss-mangle/b21d639f21c84418fd04cdf2961ed77ea07fa141/packages/tailwindcss-patch/vitest.setup.ts -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const pluginName = 'unplugin-tailwindcss-mangle' 2 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/src/core/index.ts: -------------------------------------------------------------------------------- 1 | import factory from './factory' 2 | import unplugin from './plugin' 3 | 4 | export { 5 | factory, 6 | unplugin, 7 | } 8 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/src/core/plugin.ts: -------------------------------------------------------------------------------- 1 | import { createUnplugin } from 'unplugin' 2 | import factory from './factory' 3 | 4 | const unplugin = createUnplugin(factory) 5 | 6 | export default unplugin 7 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/src/esbuild.ts: -------------------------------------------------------------------------------- 1 | import { unplugin } from './core' 2 | 3 | export default unplugin.esbuild 4 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/src/globals.d.ts: -------------------------------------------------------------------------------- 1 | declare const __DEV__: boolean 2 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/src/index.ts: -------------------------------------------------------------------------------- 1 | export { unplugin as default } from './core' 2 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/src/loader.ts: -------------------------------------------------------------------------------- 1 | import type { Context } from '@tailwindcss-mangle/core' 2 | import type { LoaderContext } from 'webpack' 3 | import { cssHandler } from '@tailwindcss-mangle/core' 4 | 5 | const TailwindcssMangleWebpackLoader = async function (this: LoaderContext<{ ctx: Context }>, source: string) { 6 | const callback = this.async() 7 | const { ctx } = this.getOptions() 8 | 9 | const { code } = await cssHandler(source, { 10 | ctx, 11 | id: this.resource, 12 | }) 13 | 14 | callback(null, code) 15 | } 16 | 17 | export default TailwindcssMangleWebpackLoader 18 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/src/nuxt.ts: -------------------------------------------------------------------------------- 1 | import type { MangleUserConfig } from './types' 2 | import { unplugin } from './core' 3 | 4 | export default function (options: MangleUserConfig = {}, nuxt: any) { 5 | // install webpack plugin 6 | nuxt.hook('webpack:config', (config: any) => { 7 | config.plugins = config.plugins || [] 8 | config.plugins.unshift(unplugin.webpack(options)) 9 | }) 10 | 11 | // install vite plugin 12 | nuxt.hook('vite:extendConfig', (config: any) => { 13 | config.plugins = config.plugins || [] 14 | config.plugins.push(unplugin.vite(options)) 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/src/rollup.ts: -------------------------------------------------------------------------------- 1 | import { unplugin } from './core' 2 | 3 | export default unplugin.rollup 4 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/src/types.ts: -------------------------------------------------------------------------------- 1 | export type * from '@tailwindcss-mangle/config' 2 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/src/vite.ts: -------------------------------------------------------------------------------- 1 | import type {} from 'vite' 2 | import { unplugin } from './core' 3 | 4 | export default unplugin.vite 5 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/src/webpack.ts: -------------------------------------------------------------------------------- 1 | import { unplugin } from './core' 2 | 3 | export default unplugin.webpack 4 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/context.test.ts: -------------------------------------------------------------------------------- 1 | function createContext() { 2 | let a = 1 3 | function inc() { 4 | a++ 5 | } 6 | return { 7 | a, 8 | inc, 9 | } 10 | } 11 | 12 | describe('contxt', () => { 13 | it('contxt change', () => { 14 | const ctx = createContext() 15 | expect(ctx.a).toBe(1) 16 | ctx.inc() 17 | expect(ctx.a).toBe(1) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/fallback/tailwindcss-mangle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tailwindcss-patch' 2 | 3 | export default defineConfig({ 4 | mangle: { 5 | classListPath: './index.json', 6 | }, 7 | }) 8 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/tsx/app.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 7 | 8 | 9 | 10 | ) -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/vite-repo/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/vite-repo/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonofmagic/tailwindcss-mangle/b21d639f21c84418fd04cdf2961ed77ea07fa141/packages/unplugin-tailwindcss-mangle/test/fixtures/vite-repo/.gitkeep -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/vite-repo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/vite-repo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-typescript-starter", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview", 10 | "extract": "npx tw-patch extract" 11 | }, 12 | "devDependencies": { 13 | "typescript": "^5.0.2", 14 | "vite": "^4.3.9" 15 | } 16 | } -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/vite-repo/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: { 6 | config: path.resolve(__dirname, './tailwind.config.cjs') 7 | }, 8 | // autoprefixer: {}, 9 | } 10 | } -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/vite-repo/src/counter.ts: -------------------------------------------------------------------------------- 1 | export function setupCounter(element: HTMLButtonElement) { 2 | element.animate({ 3 | clipPath: [], 4 | }, { 5 | duration: 400, 6 | easing: 'ease-out', 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/vite-repo/src/style.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/vite-repo/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/vite-repo/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | module.exports = { 5 | content: [path.resolve(__dirname, "./src/**/*.{html,js,ts}")], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [], 10 | corePlugins: { 11 | preflight: false 12 | } 13 | } -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/vite-repo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noFallthroughCasesInSwitch": true 21 | }, 22 | "include": ["src"] 23 | } 24 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/webpack-repo/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonofmagic/tailwindcss-mangle/b21d639f21c84418fd04cdf2961ed77ea07fa141/packages/unplugin-tailwindcss-mangle/test/fixtures/webpack-repo/.gitkeep -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/webpack-repo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-repo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC" 12 | } 13 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/webpack-repo/postcss.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: { 6 | config: path.resolve(__dirname, './tailwind.config.js') 7 | }, 8 | // autoprefixer: {}, 9 | } 10 | } -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/webpack-repo/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/webpack-repo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 |
bg-[#123456]
12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/test/fixtures/webpack-repo/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | module.exports = { 5 | content: [path.resolve(__dirname, "./src/**/*.{html,js,ts}")], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [], 10 | corePlugins: { 11 | preflight: false 12 | } 13 | } -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/*": [ 7 | "src/*" 8 | ] 9 | } 10 | }, 11 | "include": [ 12 | "src", 13 | "test" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup' 2 | 3 | export default defineConfig({ 4 | entry: [ 5 | 'src/index.ts', 6 | 'src/vite.ts', 7 | 'src/esbuild.ts', 8 | 'src/nuxt.ts', 9 | 'src/rollup.ts', 10 | 'src/webpack.ts', 11 | 'src/utils.ts', 12 | 'src/loader.ts', 13 | ], // , 'src/cli.ts'], 14 | shims: true, 15 | format: ['cjs', 'esm'], 16 | clean: true, 17 | dts: true, 18 | cjsInterop: true, 19 | splitting: true, 20 | define: { 21 | __DEV__: 'false', 22 | }, 23 | }) 24 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'pathe' 2 | import { defineProject } from 'vitest/config' 3 | 4 | export default defineProject({ 5 | define: { 6 | __DEV__: true, 7 | }, 8 | test: { 9 | alias: [ 10 | { 11 | find: '@', 12 | replacement: path.resolve(__dirname, './src'), 13 | }, 14 | ], 15 | globals: true, 16 | testTimeout: 60_000, 17 | setupFiles: ['./vitest.setup.ts'], 18 | }, 19 | 20 | // build: { 21 | // commonjsOptions: { 22 | // transformMixedEsModules: true 23 | // } 24 | // } 25 | }) 26 | -------------------------------------------------------------------------------- /packages/unplugin-tailwindcss-mangle/vitest.setup.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonofmagic/tailwindcss-mangle/b21d639f21c84418fd04cdf2961ed77ea07fa141/packages/unplugin-tailwindcss-mangle/vitest.setup.ts -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - packages/* 3 | - scripts/* 4 | - website 5 | onlyBuiltDependencies: 6 | - esbuild 7 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ], 6 | // https://answers.netlify.com/t/how-to-prevent-deploy-previews-for-renovate-prs/44131/3 7 | "commitBody": "[skip netlify]" 8 | } 9 | -------------------------------------------------------------------------------- /scripts/postcss7-compat/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('node:fs') 2 | const path = require('pathe') 3 | const postcss = require('postcss') 4 | const tailwindcss = require('tailwindcss') 5 | const { TailwindcssPatcher } = require('tailwindcss-patch') 6 | 7 | async function main() { 8 | const twPatcher = new TailwindcssPatcher() 9 | twPatcher.patch() 10 | 11 | const tw = tailwindcss({ 12 | mode: 'jit', 13 | purge: { 14 | content: [{ 15 | raw: 'w-[99px]', 16 | }], 17 | }, 18 | // corePlugins: { 19 | // preflight: false 20 | // } 21 | }) 22 | const result = postcss([tw]).process(`@tailwind base; 23 | @tailwind components; 24 | @tailwind utilities;`) 25 | // console.log(result.css) 26 | fs.writeFileSync(path.join(__dirname, 'result.css'), result.css, 'utf8') 27 | 28 | // const ctx = require('tailwindcss/lib/jit/index') 29 | // console.log(ctx) 30 | 31 | const ctx = twPatcher.getClassSet() 32 | console.log(ctx) 33 | } 34 | 35 | main() 36 | -------------------------------------------------------------------------------- /scripts/postcss7-compat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss7-compat", 3 | "version": "0.0.18", 4 | "private": true, 5 | "description": "", 6 | "author": "", 7 | "license": "ISC", 8 | "keywords": [], 9 | "main": "index.js", 10 | "scripts": { 11 | "start": "node .", 12 | "do-patch": "tw-patch install" 13 | }, 14 | "dependencies": { 15 | "tailwindcss-patch": "workspace:^" 16 | }, 17 | "devDependencies": { 18 | "autoprefixer": "^10.4.21", 19 | "postcss": "^8.5.3", 20 | "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /stylelint.config.js: -------------------------------------------------------------------------------- 1 | import { icebreaker } from '@icebreakers/stylelint-config' 2 | 3 | export default icebreaker() 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "Bundler", 6 | "resolveJsonModule": true, 7 | "types": [ 8 | "vitest/globals" 9 | ], 10 | "allowJs": true, 11 | "strict": true, 12 | "allowSyntheticDefaultImports": true, 13 | "esModuleInterop": true, 14 | "skipLibCheck": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "tasks": { 4 | "build": { 5 | "dependsOn": [ 6 | "^build" 7 | ], 8 | "outputs": [ 9 | "dist/**" 10 | ] 11 | }, 12 | "lint": { 13 | "outputs": [] 14 | }, 15 | "dev": { 16 | "cache": false 17 | }, 18 | "test": { 19 | "dependsOn": [ 20 | "^build" 21 | ] 22 | }, 23 | "release": {}, 24 | "sync": {} 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /vitest.workspace.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkspace } from 'vitest/config' 2 | 3 | export default defineWorkspace( 4 | [ 5 | 'packages/*', 6 | ], 7 | ) 8 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | public/_pagefind 2 | .env -------------------------------------------------------------------------------- /website/.tw-patch/tw-class-list.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /website/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @tailwindcss-mangle/website 2 | 3 | ## 1.1.0 4 | 5 | ### Minor Changes 6 | 7 | - [`0404f90`](https://github.com/sonofmagic/tailwindcss-mangle/commit/0404f90cc10716a84f3137f4c76a58c4c7edf019) Thanks [@sonofmagic](https://github.com/sonofmagic)! - feat: support tailwindcss@4.1.1 8 | 9 | ## 1.0.1 10 | 11 | ### Patch Changes 12 | 13 | - [`034f9f3`](https://github.com/sonofmagic/tailwindcss-mangle/commit/034f9f30ebfee915a564f95e2bf1959e8fbce3e6) Thanks [@sonofmagic](https://github.com/sonofmagic)! - chore: bump deps and add patch only for tailwindcss v2 and v3 14 | -------------------------------------------------------------------------------- /website/app/[lang]/[[...mdxPath]]/page.tsx: -------------------------------------------------------------------------------- 1 | import { generateStaticParamsFor, importPage } from 'nextra/pages' 2 | import { useMDXComponents } from '../../../mdx-components' 3 | 4 | export const generateStaticParams = generateStaticParamsFor('mdxPath') 5 | 6 | export async function generateMetadata(props: PageProps) { 7 | const params = await props.params 8 | const { metadata } = await importPage(params.mdxPath, params.lang) 9 | return metadata 10 | } 11 | 12 | type PageProps = Readonly<{ 13 | params: Promise<{ 14 | mdxPath: string[] 15 | lang: string 16 | }> 17 | }> 18 | const Wrapper = useMDXComponents().wrapper 19 | 20 | export default async function Page(props: PageProps) { 21 | const params = await props.params 22 | const result = await importPage(params.mdxPath, params.lang) 23 | const { default: MDXContent, toc, metadata } = result 24 | return ( 25 | 26 | 27 | 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /website/app/[lang]/not-found.ts: -------------------------------------------------------------------------------- 1 | export { NotFoundPage as default } from 'nextra-theme-docs' 2 | -------------------------------------------------------------------------------- /website/app/[lang]/styles.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | @import 'nextra-theme-docs/style.css'; 3 | @import '../_components/features.css'; 4 | 5 | @theme { 6 | } 7 | 8 | body { 9 | --bg-dot-color: #d1d1d1; 10 | 11 | .dark & { 12 | --bg-dot-color: #313131; 13 | } 14 | 15 | background: 16 | linear-gradient(to bottom, transparent, rgb(var(--nextra-bg)) 300px), 17 | fixed 0 0 / 20px 20px 18 | radial-gradient(var(--bg-dot-color) 1px, transparent 0), 19 | fixed 10px 10px / 20px 20px 20 | radial-gradient(var(--bg-dot-color) 1px, transparent 0); 21 | } 22 | -------------------------------------------------------------------------------- /website/app/_components/authors.tsx: -------------------------------------------------------------------------------- 1 | import type { Locale } from '@app/_dictionaries/i18n-config' 2 | import type { FC } from 'react' 3 | import { getDictionary } from '@app/_dictionaries/get-dictionary' 4 | 5 | export const TopContent: FC<{ 6 | title: string 7 | date: string 8 | authors: { 9 | name: string 10 | link: string 11 | }[] 12 | lang: Locale 13 | }> = async ({ title, date, authors, lang }) => { 14 | const dictionary = await getDictionary(lang) 15 | const dateObj = new Date(date) 16 | return ( 17 | <> 18 |

{title}

19 |
20 | 27 | {' '} 28 | {dictionary.by} 29 | {' '} 30 | {authors.map(author => ( 31 | 32 | 38 | {author.name} 39 | 40 | 41 | ))} 42 |
43 | 44 | ) 45 | } 46 | -------------------------------------------------------------------------------- /website/app/_components/features.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: grid; 3 | grid-template-columns: 1fr 1fr 1fr 1fr; 4 | gap: 1rem 2rem; 5 | margin: 2.5rem 0 2rem; 6 | } 7 | .feature { 8 | @apply inline-flex items-center gap-2 max-sm:justify-center; 9 | } 10 | .feature h4 { 11 | font-weight: 700; 12 | font-size: 1.1rem; 13 | white-space: nowrap; 14 | } 15 | @media (max-width: 860px) { 16 | .features { 17 | gap: 1rem 0.5rem; 18 | } 19 | .feature h4 { 20 | font-size: 0.9rem; 21 | } 22 | } 23 | @media (max-width: 660px) { 24 | .features { 25 | grid-template-columns: 1fr 1fr; 26 | } 27 | } 28 | @media (max-width: 370px) { 29 | .feature h4 { 30 | font-size: 0.8rem; 31 | } 32 | .feature svg { 33 | width: 16px; 34 | stroke-width: 2.5px; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /website/app/_dictionaries/en.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | dark: 'Dark', 3 | light: 'Light', 4 | system: 'System', 5 | backToTop: 'Scroll to top', 6 | lastUpdated: 'Last updated on', 7 | logo: { 8 | title: 'React Hooks for Data Fetching', 9 | }, 10 | poweredBy: 'Powered by', 11 | lightweight: 'Lightweight', 12 | realtime: 'Realtime', 13 | suspense: 'Suspense', 14 | pagination: 'Pagination', 15 | backendAgnostic: 'Backend Agnostic', 16 | renderingStrategies: 'SSR / SSG Ready', 17 | typescript: 'TypeScript Ready', 18 | remoteLocal: 'Remote + Local', 19 | editPage: 'Edit this page on GitHub', 20 | by: 'by', 21 | } 22 | -------------------------------------------------------------------------------- /website/app/_dictionaries/get-dictionary.ts: -------------------------------------------------------------------------------- 1 | import type { Dictionaries, Dictionary, Locale } from './i18n-config' 2 | import 'server-only' 3 | 4 | // We enumerate all dictionaries here for better linting and TypeScript support 5 | // We also get the default import for cleaner types 6 | const dictionaries: Dictionaries = { 7 | en: () => import('./en'), 8 | zh: () => import('./zh'), 9 | } 10 | 11 | export async function getDictionary(locale: string): Promise { 12 | const { default: dictionary } = await ( 13 | dictionaries[locale] || dictionaries.en 14 | )() 15 | 16 | return dictionary 17 | } 18 | 19 | export function getDirection(locale: Locale): 'ltr' | 'rtl' { 20 | return 'ltr' 21 | } 22 | -------------------------------------------------------------------------------- /website/app/_dictionaries/i18n-config.ts: -------------------------------------------------------------------------------- 1 | import type EnglishLocale from './en' 2 | 3 | export const i18n = { 4 | defaultLocale: 'en', 5 | locales: ['en', 'zh'], 6 | } as const 7 | 8 | export type Locale = (typeof i18n)['locales'][number] 9 | 10 | export type Dictionary = typeof EnglishLocale 11 | 12 | export type Dictionaries = Record< 13 | Locale, 14 | () => Promise<{ default: Dictionary }> 15 | > 16 | -------------------------------------------------------------------------------- /website/app/_dictionaries/zh.ts: -------------------------------------------------------------------------------- 1 | import type { Dictionary } from './i18n-config' 2 | 3 | export default { 4 | dark: 'Dark', 5 | light: 'Light', 6 | system: 'System', 7 | backToTop: 'Scroll to top', 8 | lastUpdated: 'Last updated on', 9 | logo: { 10 | title: 'React Hooks for Data Fetching', 11 | }, 12 | poweredBy: 'Powered by', 13 | lightweight: 'Lightweight', 14 | realtime: 'Realtime', 15 | suspense: 'Suspense', 16 | pagination: 'Pagination', 17 | backendAgnostic: 'Backend Agnostic', 18 | renderingStrategies: 'SSR / SSG Ready', 19 | typescript: 'TypeScript Ready', 20 | remoteLocal: 'Remote + Local', 21 | editPage: 'Edit this page on GitHub', 22 | by: 'by', 23 | } satisfies Dictionary 24 | -------------------------------------------------------------------------------- /website/app/env.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.mdx' { 2 | import type { MDXComponents } from 'nextra/mdx-components' 3 | import type { FC } from 'react' 4 | 5 | const ReactComponent: FC<{ 6 | components?: MDXComponents 7 | }> 8 | export default ReactComponent 9 | } 10 | 11 | declare module '*.svg?svgr' { 12 | import type { FC, SVGProps } from 'react' 13 | 14 | const ReactComponent: FC> 15 | 16 | export default ReactComponent 17 | } 18 | -------------------------------------------------------------------------------- /website/content/en/_meta.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'index': 'Homepage', 3 | '---': { 4 | type: 'separator', 5 | }, 6 | 'patch': '1.Patch', 7 | 'mangle': '2.Mangle', 8 | 'config': '3.Config', 9 | 'recommend': { 10 | display: 'hidden', 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /website/content/en/config.mdx: -------------------------------------------------------------------------------- 1 | # 待办事项 -------------------------------------------------------------------------------- /website/content/en/index.mdx: -------------------------------------------------------------------------------- 1 | import { Callout } from 'nextra/components' 2 | 3 | # Welcome to Tailwindcss-mangle 4 | 5 | ## What is tailwindcss-mangle? 6 | 7 | This is a project designed to compress and obfuscate class names generated by `tailwindcss`. 8 | 9 | The purpose of creating this library is that the pages we develop using `tailwindcss` are too easily copied and plagiarized. 10 | 11 | Using `tailwindcss-mangle` can add a little cost to those who attempt to copy. 12 | 13 | ## How to use? 14 | 15 | Currently, the usage is mainly divided into `2` steps: 16 | 17 | 1. `Patch` 18 | 2. `Mangle` 19 | 20 | ## Patch 21 | 22 | This step is used to patch `tailwindcss` to expose its context, thereby extracting the `Token` it generates from its internals. 23 | 24 | Finally, it exports this into a `Json` file. 25 | 26 | ## Mangle 27 | 28 | Read the `Json` exported by `Patch`, and escape all string literals and template strings corresponding to the `Token` inside. -------------------------------------------------------------------------------- /website/content/zh/_meta.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'index': 'Homepage', 3 | '---': { 4 | type: 'separator', 5 | }, 6 | 'patch': '1.Patch', 7 | 'mangle': '2.Mangle', 8 | 'config': '3.Config', 9 | 'recommend': { 10 | display: 'hidden', 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /website/content/zh/config.mdx: -------------------------------------------------------------------------------- 1 | # TODO -------------------------------------------------------------------------------- /website/content/zh/index.mdx: -------------------------------------------------------------------------------- 1 | import { Callout } from 'nextra/components' 2 | 3 | # 欢迎来到 Tailwindcss-mangle 4 | 5 | ## 什么是 tailwindcss-mangle ? 6 | 7 | 这是一个给 `tailwindcss` 生成的类名进行压缩混淆的一个项目。 8 | 9 | 制作这个库的目的在于,我们使用 `tailwindcss` 开发的页面,实在是太容易被拷贝抄袭了。 10 | 11 | 而使用 `tailwindcss-mangle` 就能来给抄袭者带去一点点的抄袭成本。 12 | 13 | ## 如何使用? 14 | 15 | 目前使用方式上,主要分为 `2` 步: 16 | 17 | 1. `Patch` 18 | 2. `Mangle` 19 | 20 | ## Patch 21 | 22 | 此步骤用于给 `tailwindcss` 打上暴露上下文的补丁,从而从它的内部提取出它提取的 `Token` 23 | 24 | 最终把它导出成一个 `Json` 文件。 25 | 26 | ## Mangle 27 | 28 | 读取 `Patch` 导出的 `Json` , 将里面的 `Token` 对应的字符串字面量和模板字符串全部转义。 29 | 30 | 31 | -------------------------------------------------------------------------------- /website/mdx-components.ts: -------------------------------------------------------------------------------- 1 | import { useMDXComponents as getDocsMDXComponents } from 'nextra-theme-docs' 2 | import { Pre, withIcons } from 'nextra/components' 3 | import { GitHubIcon } from 'nextra/icons' 4 | 5 | const docsComponents = getDocsMDXComponents({ 6 | pre: withIcons(Pre, { js: GitHubIcon }) 7 | }) 8 | 9 | export const useMDXComponents: typeof getDocsMDXComponents = components => ({ 10 | ...docsComponents, 11 | ...components 12 | }) 13 | -------------------------------------------------------------------------------- /website/middleware.ts: -------------------------------------------------------------------------------- 1 | export { middleware } from 'nextra/locales' 2 | 3 | export const config = { 4 | // Matcher ignoring `/_next/` and `/api/` 5 | matcher: [ 6 | '/((?!api|_next/static|_next/image|favicon.ico|icon.svg|apple-icon.png|manifest|_pagefind).*)' 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /website/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. 6 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tailwindcss-mangle/website", 3 | "type": "module", 4 | "version": "1.1.0", 5 | "private": true, 6 | "description": "", 7 | "author": "", 8 | "license": "ISC", 9 | "keywords": [], 10 | "scripts": { 11 | "analyze": "ANALYZE=true pnpm build", 12 | "build": "next build", 13 | "debug": "NODE_OPTIONS='--inspect' next dev", 14 | "dev": "next --turbopack", 15 | "postbuild": "pagefind --site .next/server/app --output-path public/_pagefind", 16 | "start": "next start", 17 | "types:check": "tsc --noEmit", 18 | "translate": "tsx scripts/translate.ts" 19 | }, 20 | "dependencies": { 21 | "markdown-to-jsx": "^7.7.6", 22 | "next": "^15.3.1", 23 | "nextra": "^4.2.17", 24 | "nextra-theme-docs": "^4.2.17", 25 | "react": "19.1.0", 26 | "react-dom": "19.1.0", 27 | "react-intersection-observer": "^9.16.0" 28 | }, 29 | "devDependencies": { 30 | "@next/bundle-analyzer": "^15.3.1", 31 | "@svgr/webpack": "^8.1.0", 32 | "@tailwindcss/postcss": "^4.1.7", 33 | "@types/react": "19.1.5", 34 | "pagefind": "^1.3.0", 35 | "tailwindcss": "^4.0.9" 36 | }, 37 | "browserslist": [ 38 | ">= .25%", 39 | "not dead" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /website/postcss.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss').Postcss} */ 2 | export default { 3 | plugins: { 4 | '@tailwindcss/postcss': {} 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /website/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: [ 4 | './pages/**/*.{js,jsx,ts,tsx,md,mdx}', 5 | './components/**/*.{js,jsx,ts,tsx,md,mdx}', 6 | './theme.config.tsx', 7 | ], 8 | theme: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /website/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": false, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "incremental": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "moduleResolution": "bundler", 17 | "baseUrl": "./", 18 | "paths": { 19 | "@app/*": ["./app/*"] 20 | }, 21 | "plugins": [ 22 | { 23 | "name": "next" 24 | } 25 | ], 26 | "strictNullChecks": true, 27 | "noUncheckedIndexedAccess": true 28 | }, 29 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 30 | "exclude": ["node_modules", ".next"] 31 | } 32 | --------------------------------------------------------------------------------