Skip to content

feat-support-tailwindcss-v4 #68

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Apr 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Release

on:
push:
branches:
- main
- beta

permissions:
contents: write # to be able to publish a GitHub release
issues: write # to be able to comment on released issues
pull-requests: write # to be able to comment on released pull requests
id-token: write # to enable use of OIDC for npm provenance

jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install dependencies
run: npm install

- name: Build NPM package
run: npm run build

- name: Create Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm run semantic-release
5 changes: 0 additions & 5 deletions .prettierignore

This file was deleted.

7 changes: 0 additions & 7 deletions .prettierrc

This file was deleted.

109 changes: 77 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,31 @@ Visit the [GitHub Page](https://github.com/soranoo/next-css-obfuscator/) for bet
---


### 🎉 Version 2.1.0 has NOW been released 🎉
Shout out to [hoangnhan2ka3](https://github.com/hoangnhan2ka3) for providing a 💪wonderful [issue](https://github.com/soranoo/next-css-obfuscator/issues/6) report and a demo site.
### 🎉 Version 3 has NOW been released 🎉 (💥 Breaking Changes)

#### 📌 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
>[!IMPORTANT]\
> This version is a major update and has breaking changes. Please read the [migration guide](docs/upgrade-to-v3.md) carefully before upgrading.

>[!TIP]\
> Don't upgrade to this version unless you are using TailwindCSS 4.0.0 or above. "If it works, don't touch it." :)

#### 📌 Feature Changes

- Support TailwindCSS 4.
- Support nested CSS.
- Support CSS idents obfuscation.

#### 📌 Configuration Changes
- Removed `customTailwindDarkModeSelector` option, the dark mode selector will be automatically obfuscated at full obfuscation.
- Merged `includeAnyMatchRegexes` and `excludeAnyMatchRegexes` options into `whiteListedFolderPaths` and `blackListedFolderPaths` options. (Directly move the regexes to the `whiteListedFolderPaths` and `blackListedFolderPaths` options)
- Added `removeOriginalCss` option, default to `false`. Set to `true` to delete original CSS from CSS files if it has a obfuscated version.
- `classIgnore` option now supports Regex.

- `enableJsAst` option is now enabled by default.
- Default `generatorSeed` not longer fixed to `-1`, but a random string.
- `simplify-seedable` mode is not longer supported. Use `random` mode instead.
- Removed `includeAnyMatchRegexes` and `excludeAnyMatchRegexes` options, the `whiteListedFolderPaths` and `blackListedFolderPaths` options will be used instead.
- Deprecated `classLength` option, not longer supported.
- Added `ignorePatterns` option to ignore the class names and idents that match the regexes or strings.
- Not longer preserve TailwindCSS dark mode class names (ie `.dark`). Add the dark mode class name to the `ignorePatterns.selectors` option to preserve it.
- Merge `classIgnore` into `ignorePatterns.selectors` option.
- Renamed `classPrefix` and `classSuffix` to `prefix` and `suffix`.

### 💥 Version 2 (Major Update)
This version is deeply inspired by [PostCSS-Obfuscator](https://github.com/n4j1Br4ch1D/postcss-obfuscator). Shout out to [n4j1Br4ch1D](https://github.com/n4j1Br4ch1D) for creating such a great package and thank you [tremor](https://github.com/tremorlabs) for sponsoring this project.
Expand All @@ -47,10 +58,13 @@ Visit the [GitHub Page](https://github.com/soranoo/next-css-obfuscator/) for bet

### 📚 Migration Guides
- [Migrate from version 1.x to 2.x](docs/upgrade-to-v2.md)
- [Migrate from version 2.x to 3.x](docs/upgrade-to-v3.md)


[version 1.x README](https://github.com/soranoo/next-css-obfuscator/tree/v.1.1.0)

[version 2.x README](https://github.com/soranoo/next-css-obfuscator/tree/v.2.2.19)

Give me a ⭐ if you like it.

## 📖 Table of Contents
Expand Down Expand Up @@ -156,17 +170,21 @@ Visit the [npm](https://www.npmjs.com/package/next-css-obfuscator) page.
```javascript
module.exports = {
enable: true,
mode: "random", // random | simplify | simplify-seedable
mode: "random", // random | simplify
refreshClassConversionJson: false, // recommended set to true if not in production
allowExtensions: [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"],
};

```
##### Partially obfuscate

> [!CAUTION]\
> Partially obfuscate can be EXTREMELY buggy. Be cautious when using this feature.

```javascript
module.exports = {
enable: true,
mode: "random", // random | simplify | simplify-seedable
mode: "random", // random | simplify
refreshClassConversionJson: false, // recommended set to true if not in production
allowExtensions: [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"],

Expand All @@ -181,14 +199,14 @@ Visit the [npm](https://www.npmjs.com/package/next-css-obfuscator) page.

module.exports = {
// other options ...
} as Options;
} satisfies Options;
```



Feel free to checkout [📖 Config Options Reference](#-config-options-reference) for more options and details.

> [!NOTE]\
> [!TIP]\
> The obfuscation will never work as expected, tweak the options with your own needs.

2. Add the following code to `package.json`:
Expand Down Expand Up @@ -260,14 +278,14 @@ See [Next 14 App Router Partially Obfuscated Demo](https://github.com/soranoo/ne

## 🔧 My Setting

If you are interested in my setting (from my production site), here it is
If you are interested in my setting, here it is

```javascript
// next-css-obfuscator.config.cjs

module.exports = {
enable: true,
mode: "random", // random | simplify | simplify-seedable
mode: "random", // random | simplify
refreshClassConversionJson: false, // recommended set to true if not in production
allowExtensions: [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"],

Expand All @@ -280,6 +298,7 @@ module.exports = {
],
};
```

[*1] See this [comment](https://github.com/soranoo/next-css-obfuscator/issues/6#issuecomment-1919495298)

It may not be the best setting but it works for me. :)
Expand All @@ -289,14 +308,13 @@ It may not be the best setting but it works for me. :)
| Option | Type | Default | Description |
| ---------------------------- | ----------------------------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| enable | boolean | true | Enable or disable the obfuscation. |
|mode| "random" \| "simplify" \| "simplify-seedable" | "random" | Obfuscate mode, <br><br>**random**: Fixed size random class name <br><br>**simplify**: Alphabetic class name, like [medium](https://medium.com/) <br><br>**simplify-seedable**: Random dynamic size class name|
| mode | "random" \| "simplify" | "random" | Obfuscate mode, <br><br>**random**: Fixed size random class name <br><br>**simplify**: Alphabetic class name, like [medium](https://medium.com/)|
|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. <br><br>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.|
|prefix|string|""|The prefix of the obfuscated class and ident name.|
|suffix|string|""|The suffix of the obfuscated class and ident name.|
|ignorePatterns|{selectors: [], idents: []}|{selectors: [], idents: []}|The patterns 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.|
Expand All @@ -305,13 +323,13 @@ It may not be the best setting but it works for me. :)
|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. <br><br>For "random" and "simplify-seedable" mode only. |
|generatorSeed|string \| undefined|{random string}|The seed for the random class name generator. "undefined" means use random seed. <br><br>For "random" mode only. |
|logLevel|"debug" \| "info" \| "warn" \| "error" \| "success"| "info"|The log level.|

### Experimental Features Options 🚧
| Option| Type| Default| Description| Stage |
| - | - | - | - | - |
|enableJsAst|boolean|false|Whether to obfuscate JS files using abstract syntax tree parser. <br><br>`contentIgnoreRegexes` option will be ignored if this option is enabled.|Alpha|
|enableJsAst|boolean|true|Whether to obfuscate JS files using abstract syntax tree parser. <br><br>`contentIgnoreRegexes` option will be ignored if this option is enabled.|Alpha|

> [!NOTE]\
> The above options are still at the early stages of development and may not work as expected.
Expand All @@ -331,15 +349,38 @@ It may not be the best setting but it works for me. :)

module.exports = {
enable: true, // Enable or disable the plugin.
mode: "random", // Obfuscate mode, "random", "simplify" or "simplify-seedable"
mode: "random", // Obfuscate mode, "random", "simplify" or "simplify-seedable".
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.

/**
* @deprecated Not longer used from v3.0.0 and will be removed in the next major version.
*/
classLength: 5, // Length of the obfuscated class name.

/**
* @deprecated Merged into `prefix` from v3.0.0 and will be removed in the next major version.
*/
classPrefix: "", // Prefix of the obfuscated class name.

/**
* @deprecated Merged into `suffix` from v3.0.0 and will be removed in the next major version.
*/
classSuffix: "", // Suffix of the obfuscated class name.

prefix: "", // Prefix of the obfuscated class and ident name.
suffix: "", // Suffix of the obfuscated class and ident name.

/**
* @deprecated Merged into `ignorePatterns.selectors` from v3.0.0 and will be removed in the next major version.
*/
classIgnore: [], // The class names to be ignored during obfuscation.
ignorePatterns: { // The patterns to be ignored during obfuscation.
selectors: [], // The selectors to be ignored during obfuscation.
idents: [], // The idents 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
Expand All @@ -351,10 +392,12 @@ module.exports = {
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.
generatorSeed: undefined, // The seed for the random generator. "undefined" means use random seed.

//! Experimental feature
enableJsAst: false, // Whether to obfuscate JS files using abstract syntax tree parser (Experimental feature)
/**
* Experimental feature
*/
enableJsAst: true, // Whether to obfuscate JS files using abstract syntax tree parser. (Experimental feature)

logLevel: "info", // Log level
};
Expand Down Expand Up @@ -396,18 +439,20 @@ You are not expected to see this:

/* obfuscated form */
.d8964 {
--tw-text-opacity: 1;
color: rgb(214 211 209 / var(--tw-text-opacity));
--d89645: 1;
color: rgb(214 211 209 / var(--d89645));
}
```

But this:

```css
/* example.css */

/* obfuscated form */
.d8964 {
--tw-text-opacity: 1;
color: rgb(214 211 209 / var(--tw-text-opacity));
--d89645: 1;
color: rgb(214 211 209 / var(--d89645));
}
```

Expand Down
2 changes: 1 addition & 1 deletion bin/cli.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

import { obfuscateCli } from "../dist/index.js";

obfuscateCli();
obfuscateCli();
74 changes: 74 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"vcs": {
"enabled": false,
"clientKind": "git",
"useIgnoreFile": false
},
"files": {
"ignoreUnknown": false,
"ignore": [
"node_modules",
".next",
"packages/tsconfig",
"demos",
"coverage",
"dist"
]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"lineWidth": 80,
"indentWidth": 2,
"lineEnding": "lf"
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,

"complexity": {
"noUselessTypeConstraint": "error",
"useLiteralKeys": "error",
"useOptionalChain": "error",
"noForEach": "off"
},
"correctness": {
"noUnusedVariables": "info",
"useArrayLiterals": "error"
},
"style": {
"noInferrableTypes": "error",
"noNamespace": "error",
"useAsConstAssertion": "error",
"useBlockStatements": "error",
"useConsistentArrayType": "error",
"useForOf": "error",
"useShorthandFunctionType": "error",
"useImportType": "error"
},
"suspicious": {
"noDebugger": "info",
"noConsoleLog": "info",
"noEmptyBlockStatements": "error",
"noExplicitAny": "error",
"noExtraNonNullAssertion": "error",
"noMisleadingInstantiator": "error",
"noUnsafeDeclarationMerging": "error",
"useAwait": "warn",
"useNamespaceKeyword": "error"
}
}
},
"javascript": {
"formatter": {
"quoteStyle": "double",
"semicolons": "always",
"trailingCommas": "all"
}
}
}
20 changes: 20 additions & 0 deletions docs/upgrade-to-v3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Toward to version 3

Version 3 introduces several breaking changes, primarily focused on supporting TailwindCSS 4, nested CSS, and CSS ident obfuscation. Please review the configuration changes below carefully before upgrading.

## Configuration

The following table outlines the changes to the configuration options from version 2.x to 3.x:

| Old configuration (v2.x) | New configuration (v3.x) | Notes |
| ------------------------------ | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| `generatorSeed` (default: -1) | `generatorSeed` (default: {random}) | Default seed is now a random string. Provide a fixed string if you need consistent output across builds (e.g., for CDN caching). |
| `mode: "simplify-seedable"` | ⛔ (Removed) | Use `mode: "random"` with a fixed `generatorSeed` instead. |
| `classLength` | ⛔ (Deprecated) | No longer supported. Will be removed in the next major version. |
| `classPrefix` | `prefix` | Renamed for clarity, now applies to both selectors and idents. `classPrefix` will be removed in the next major version. |
| `classSuffix` | `suffix` | Renamed for clarity, now applies to both selectors and idents. `classSuffix` will be removed in the next major version. |
| `classIgnore` | `ignorePatterns.selectors` | Merged into the new `ignorePatterns` object. `classIgnore` will be removed in the next major version. |
| ➡️ | `ignorePatterns.idents` | New option to ignore specific CSS idents|
| `includeAnyMatchRegexes` | ⛔ (Removed) | Use `whiteListedFolderPaths` instead. |
| `excludeAnyMatchRegexes` | ⛔ (Removed) | Use `blackListedFolderPaths` instead. |
| (Implicit `.dark` preservation)| (No implicit preservation) | TailwindCSS `.dark` class is no longer preserved by default. Add `.dark` or relevant selectors to `ignorePatterns.selectors`. |
Loading