NEXT-CSS-OBFUSCATOR
Project start on 30-10-2023
Visit the GitHub Page for better reading experience and latest docs. 😎
🎉 Version 2.1.0 has NOW been released 🎉
Shout out to hoangnhan2ka3 for providing a 💪wonderful issue report and a demo site.
📌 Changes
- Much Much Much better quality of CSS selector obfuscation
- Delete original CSS automatically after obfuscation (only apply at full obfuscation)
- Support TailwindCSS Universal Selector (eg.
*:pt-4
) - More tests
📌 Configuration Changes
- Removed
customTailwindDarkModeSelector
option, the dark mode selector will be automatically obfuscated at full obfuscation. - Merged
includeAnyMatchRegexes
andexcludeAnyMatchRegexes
options intowhiteListedFolderPaths
andblackListedFolderPaths
options. (Directly move the regexes to thewhiteListedFolderPaths
andblackListedFolderPaths
options) - Added
removeOriginalCss
option, default tofalse
. Set totrue
to delete original CSS from CSS files if it has a obfuscated version. -
classIgnore
option now supports Regex.
💥 Version 2 (Major Update)
This version is deeply inspired by PostCSS-Obfuscator. Shout out to n4j1Br4ch1D for creating such a great package and thank you tremor for sponsoring this project.
📌 Changes
- Support basic partial obfuscation
- Support TailwindCSS Dark Mode
- New configuration file
next-css-obfuscator.config.cjs
- More configuration options
- Now become a independent sulotion (no need to patch
PostCSS-Obfuscator
anymore) - More tests
- Better CSS parsing
📚 Migration Guides
Give me a ⭐ if you like it.
📖 Table of Contents
- 🤔 Why this?
- 💡 How does it work?
- 🗝️ Features
- 🛠️ Development Environment
- 📦 Requirements
- 🚀 Getting Started
- 🔧 My Setting
- 📖 Config Options Reference
- 💻 CLI
-
💡 Tips
- 1. Not work at Vercel after updated
- 2. Lazy Setup - Obfuscate all files
- 3. It was working normally just now, but not now?
- 4. Why are some original selectors still in the obfuscated CSS file even the
removeOriginalCss
option is set totrue
? - 5. Why did I get a copy of the original CSS after partial obfuscation?
- 6. How to deal with CSS cache in PaaS like Vercel?
- 👀 Demos
- ⭐ TODO
- 🐛 Known Issues
- 💖 Sponsors
- 🦾 Special Thanks
- 🤝 Contributing
- 📝 License
- ☕ Donation
🤔 Why this?
Because in the current version of PostCSS-Obfuscator does not work with Next.js. (see this issue for more details)
💡 How does it work?
Where is issue in PostCSS-Obfuscator?
PostCSS-Obfuscator
will not edit the build files instead it will create a new folder and put the obfuscated source code files in it. This is where the issue is. Next.js will not recognize the obfuscated files and will not include them in the build. I tried to point Nextjs to build the obfuscated files (by simply change the obfuscated source code folder to src
) but it didn't work.
How does this package solve the issue?
Edit the build files directly. (It may not be the best solution but it works.)
How does this package work?
- Extract and parse CSS files from the build files.
- Obfuscate the CSS selectors and save to a JSON file.
- Search and replace the related class names in the build files with the obfuscated class names.
🗝️ Features
- WORK WITH NEXT.JS !!!!!!!!!!!!!!!!!!!
[!NOTE]
This package is NOT guaranteed to work with EVERYONE. Check the site carefully before using it in production.
[!WARNING]
As a trade-off, the obfuscation will make your CSS files larger.
🛠️ Development Environment
Environment | Version |
---|---|
OS | Windows 11 & Ubuntu 22.04 |
Node.js | v.18.17.1 |
NPM | v.10.1.0 |
Next.js (Page Router) | v.13.5.4 & v.13.4.1 |
Next.js (App Router) | v.14.0.4 |
TailwindCSS | v.3.3.3 |
- ✅ Tested and works with Next.js Page Router and App Router.
- ✅ Tested and works with Vercel.
(Theoretically it supports all CSS frameworks but I only tested it with TailwindCSS.)
📦 Requirements
- ⌛ TIME 🕛
🚀 Getting Started
Installation
npm install -D next-css-obfuscator
Visit the npm page.
Setup
-
Create and add the following code to
next-css-obfuscator.config.cjs
ornext-css-obfuscator.config.ts
:Obfuscate all files
module.exports = { enable: true, mode: "random", // random | simplify refreshClassConversionJson: false, // recommended set to true if not in production allowExtensions: [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"], };
Partially obfuscate
module.exports = { enable: true, mode: "random", // random | simplify refreshClassConversionJson: false, // recommended set to true if not in production allowExtensions: [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"], enableMarkers: true, };
TypeScript
import { Options } from "next-css-obfuscator"; module.exports = { // other options ... } as Options;
Feel free to checkout 📖 Config Options Reference for more options and details.
[!NOTE]
The obfuscation will never work as expected, tweak the options with your own needs. -
Add the following code to
package.json
:"scripts": { // other scripts ... "obfuscate-build": "next-css-obfuscator" },
Read 💻 CLI for more details.
Usage 🎉
- Run
npm run build
to build the project. - Run
npm run obfuscate-build
to obfuscate the css files.
(You may need to delete the .next/cache
folder before running npm run start
to make sure the obfuscation takes effect. And don't forget to shift + F5
refresh the page.`)
[!WARNING]
NEVER runobfuscate-build
twice in a row. It may mess up the build files and obfuscation convertion table. You can remove theclassConversionJsonFolderPath
(default:css-obfuscator
) folder to reset the convertion table.
[!NOTE]
For better development experience, it is recommanded to enablerefreshClassConversionJson
option innext-css-obfuscator.config.cjs
and disable it in production.
For convenience, you may update your build script to:
// package.json
"scripts": {
// other scripts ...
"build": "next build && npm run obfuscate-build"
},
to make sure the build is always obfuscated and no need to run obfuscate-build
manually.
[!NOTE]
It is a good idea to add the/css-obfuscator
folder to.gitignore
to prevent the convertion table from being uploaded to the repository.
Partially obfuscate
To partially obfuscate your project, you have to add the obfuscate marker class to the components you want to obfuscate.
// example
export default function HomePage() {
return (
<main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#fac3e3] to-[#5c9cbd] text-white">
<div className="container flex flex-col items-center justify-center gap-12 px-4 py-16 ">
<h1 className="text-5xl font-extrabold tracking-tight text-white sm:text-[5rem]">
Next14 App Router
</h1>
</div>
- <div className="container flex flex-col items-center justify-center gap-12 px-4 py-16 ">
+ <div className="next-css-obfuscation container flex flex-col items-center justify-center gap-12 px-4 py-16 ">
<span className="text-2xl font-extrabold tracking-tight text-gray-700 border-2 border-blue-950 rounded-lg p-4">
My classes are obfuscated
</span>
</div>
</main>
);
}
See Next 14 App Router Partially Obfuscated Demo for more details.
🔧 My Setting
If you are interested in my setting (from my production site), here it is
// next-css-obfuscator.config.cjs
module.exports = {
enable: true,
mode: "random", // random | simplify
refreshClassConversionJson: false, // recommended set to true if not in production
allowExtensions: [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"],
blackListedFolderPaths: [
"./.next/cache",
/\.next\/server\/pages\/api/,
/_document..*js/,
/_app-.*/,
/__.*/, // <= maybe helpful if you are using Next.js Lcal Fonts [1*]
],
};
[*1] See this comment
It may not be the best setting but it works for me. :)
📖 Config Options Reference
Option | Type | Default | Description |
---|---|---|---|
enable | boolean | true | Enable or disable the obfuscation. |
mode | string | "random" | Obfuscate mode, "random" or "simplify". |
buildFolderPath | string | "./.next" | The folder path to store the build files built by Next.js. |
classConversionJsonFolderPath | string | "./css-obfuscator" | The folder path to store the before obfuscate and after obfuscated classes conversion table. |
refreshClassConversionJson | boolean | false | Refresh the class conversion JSON file(s) at every obfuscation. Good for setting tweaking but not recommended for production. |
classLength | number | 5 | The length of the obfuscated class name if in random mode. It is not recommended to set the length to less than 4. |
classPrefix | string | "" | The prefix of the obfuscated class name. |
classSuffix | string | "" | The suffix of the obfuscated class name. |
classIgnore | (string | Regex)[ ] | [ ] | The class names to be ignored during obfuscation. |
allowExtensions | string[ ] | [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"] | The file extensions to be processed. |
contentIgnoreRegexes | RegExp[ ] | [/.jsxs)("\w+"/g] | The regexes to match the content to be ignored during obfuscation. |
whiteListedFolderPaths | (string | Regex)[ ] | [ ] | The folder paths/Regex to be processed. Empty array means all folders will be processed. |
blackListedFolderPaths | (string | Regex)[ ] | [ ] | The folder paths/Regex to be ignored. |
enableMarkers | boolean | false | Enable or disable the obfuscation markers. |
markers | string[ ] | [ ] | Classes that indicate component(s) need to obfuscate. |
removeMarkersAfterObfuscated | boolean | true | Remove the obfuscation markers from HTML elements after obfuscation. |
removeOriginalCss | boolean | false | Delete original CSS from CSS files if it has a obfuscated version. (NOT recommended using in partial obfuscation) |
generatorSeed | string | "-1" | The seed for the random class name generator. "-1" means use random seed. |
logLevel | "debug" | "info" | "warn" | "error" | "success" | "info" | The log level. |
All options in one place
module.exports = {
enable: true, // Enable or disable the plugin.
mode: "random", // Obfuscate mode, "random" or "simplify".
buildFolderPath: ".next", // Build folder of your project.
classConversionJsonFolderPath: "./css-obfuscator", // The folder path to store the before obfuscate and after obfuscated classes conversion table.
refreshClassConversionJson: false, // Refresh the class conversion JSON file.
classLength: 5, // Length of the obfuscated class name.
classPrefix: "", // Prefix of the obfuscated class name.
classSuffix: "", // Suffix of the obfuscated class name.
classIgnore: [], // The class names to be ignored during obfuscation.
allowExtensions: [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"], // The file extensions to be processed.
contentIgnoreRegexes: [
/\.jsxs\)\("\w+"/g, // avoid accidentally obfuscate the HTML tag
], // The regexes to match the file content to be ignored during obfuscation.
whiteListedFolderPaths: [], // Only obfuscate files in these folders
blackListedFolderPaths: ["./.next/cache"], // Don't obfuscate files in these folders
enableMarkers: false, // Enable or disable the obfuscate marker classes.
markers: ["next-css-obfuscation"], // Classes that indicate component(s) need to obfuscate.
removeMarkersAfterObfuscated: true, // Remove the obfuscation markers from HTML elements after obfuscation.
removeOriginalCss: false, // Delete original CSS from CSS files if it has a obfuscated version.
generatorSeed: "-1", // The seed for the random generator. "-1" means use random seed.
logLevel: "info", // Log level
};
💻 CLI
next-css-obfuscator --config ./path/to/your/config/file
💡 Tips
1. Not work at Vercel after updated ?
If you are using this package with Vercel, you may found the package not work as expected after updated. This is because Vercel will cache the last build for a faster build time. To fix this you have to redeploy with the Use existing build cache
option disabled.
2. Lazy Setup - Obfuscate all files
Enable enableMarkers
and put the obfuscate marker class at every component included the index page. But if you want to set and forget, you must play with the options to ensure the obfuscation works as expected.
3. It was working normally just now, but not now?
Your convertion table may be messed up. Try to delete the classConversionJsonFolderPath
(default: css-obfuscator
) folder to reset the convertion table.
removeOriginalCss
option is set to true
?
4. Why are some original selectors still in the obfuscated CSS file even the In a normal situation, the package will only remove the original CSS that is related to the obfuscation and you should not see any CSS sharing the same declaration block.
You are not expected to see this:
/* example.css */
/* original form */
.text-stone-300 {
--tw-text-opacity: 1;
color: rgb(214 211 209 / var(--tw-text-opacity));
}
/* obfuscated form */
.d8964 {
--tw-text-opacity: 1;
color: rgb(214 211 209 / var(--tw-text-opacity));
}
But this:
/* example.css */
/* obfuscated form */
.d8964 {
--tw-text-opacity: 1;
color: rgb(214 211 209 / var(--tw-text-opacity));
}
If you encounter the first situation, it means something is wrong with the obfuscation. You may need to raise an issue with your configuration and the related code.
5. Why did I get a copy of the original CSS after partial obfuscation?
Since the original CSS may referenced by other components not included in the obfuscation, the package will not remove the original CSS to prevent breaking the the site.
Vercel?
6. How to deal with CSS cache in PaaS like(I will take Vercel as an example)
You may discover that the obfuscated class conversion table updates every time you deploy your site to Vercel even if the refreshClassConversionJson
option is set to false
. As a result, the CSS file will update in every deployment and break the CDN cache. This is because Vercel will not keep the files generated by the previous deployment. To fix this, you can simply provide a fixed generatorSeed
to make sure the obfuscated class name will be the same as the previous.
[!NOTE]
Promotion🗯️
Do you know the mechanism behind it is powered by my another packagerecoverable-random
? Check it out
👀 Demos
⭐ TODO
- [x] Partial obfuscation
- [x] To be a totally independent package (remove dependency on PostCSS-Obfuscator)
- [ ] More tests
- [ ] More demos ?
🐛 Known Issues
- Partial Obfuscation
- Not work with complex component. (eg. A component with children components)
- Reason: The obfuscation marker can't locate the correct code block to obfuscate.
- Potential Solution: track the function/variable call stack to locate the correct code block to obfuscate. (under PoC)
- Not work with complex component. (eg. A component with children components)
💖 Sponsors
Organizations (1)
tremor |
Individuals (0)
🦾 Special Thanks
hoangnhan2ka3 |
🤝 Contributing
Contributions are welcome! If you find a bug or have a feature request, please open an issue. If you want to contribute code, please fork the repository and run npm run test
before submit a pull request.
📝 License
This project is licensed under the MIT License - see the LICENSE file for details
☕ Donation
Love it? Consider a donation to support my work.