soranoo / next-css-obfuscator

A package deeply inspired by PostCSS-Obfuscator but for Next.js.
https://next-css-obfuscator.vercel.app
MIT License
83 stars 3 forks source link

[BUG] Support more complex cases of custom arbitrary values `[&_*]`: without `direct properties` and comma `\2c` #23

Open hoangnhan2ka3 opened 9 months ago

hoangnhan2ka3 commented 9 months ago

Type

Checklist

  1. [x] Updated the package to the latest version
  2. [x] Read all documentation
  3. [x] No related issue
  4. [x] Meaningful issue title

Environment

Describe the bug Custom arbitrary values [&_*]like without direct properties or comma \2c seems not to be obfuscated.

To Reproduce Steps to reproduce the behavior:

  1. In whatever page.tsx u like:
    <div className="[&_a]:[transition:padding-left_cubic-bezier(.15,1.6,.75,1)_300ms,color_ease_300ms] [&_a]:will-change-[padding-left,_color]">
    <a href="/">
    Sample
    </a>
    {/* or */}
    <Link href="/">
    Sample
    </Link>
    </div>
  2. Delete the .next/cache folder and old css-obfuscator folder with old conversion.json file in it.
  3. Setting config like me below.
  4. yarn build -> yarn start
  5. On the web, open Dev Tool, point to the <a> tag above.
  6. See error:
    • no transition or will-change properties applied.
    • those classes aren't obfuscated.

Expected behavior Those classes succesfully obfuscated and properties applied.

Screenshot 2024-02-18 172623

Screenshots

Screenshot 2024-02-18 175744

Config

module.exports = {
  enable: true,
  mode: "random",
  buildFolderPath: ".next",
  classConversionJsonFolderPath: "./css-obfuscator",
  refreshClassConversionJson: false,

  classLength: 6,
  classPrefix: "n",
  classSuffix: "",

  classIgnore: ["dark", "light", "no-transition", "opa-hidden", "big", "small", /nprogress*/, /__.*/, /os-*/],

  allowExtensions: [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"],

  contentIgnoreRegexes: [/\.jsxs\)\("\w+"/g],

  whiteListedFolderPaths: [],

  blackListedFolderPaths: ["./.next/cache", "./src", "./public", "./tests"],

  enableMarkers: false,
  markers: ["obf"],
  removeMarkersAfterObfuscated: true,

  removeOriginalCss: true,

  generatorSeed: "84817818898",

  //! Experimental feature (Alpha)
  enableJsAst: false,

  logLevel: "info",
};

Related files With this plugin enable: 1117e71e50ac0253.css.txt

With this plugin disable: 1117e71e50ac0253.css.txt

Demos (if any) N/A

Additional context A expanded version of https://github.com/soranoo/next-css-obfuscator/issues/9

Screenshot 2024-02-18 172937

No direct property means:

image

and not:

image

both valid in Tailwind.

Screenshot 2024-02-18 173014 Screenshot 2024-02-18 181049

All of 3 example above contain comma ,, which will convert to \2c in className.

Note: Everything works perfectly with yarn dev, which means the above classes work perfectly even though there are spaces in the class names (caused by Tailwind)

image

I don't understand why it still works with those 'spaces' πŸ€”.

soranoo commented 9 months ago

I found this issue is way way more complex than I imagined. I can't replace the \2c with , directly. I tried 2 methods,

const v = ".nuzql9z\\[transition\\:nu3pow5\\(\\.nupku0r 1\\.nu3sdzf \\.nu3lzbe 1\\)_300ms\\2c color_ease_300ms\\] a"

 // 1. Direct replace 
console.log(v.replace("\\2c", ","))

// 2. Regex replace 
console.log(v.replace(/\\2c/, ","))

image

Yes, it seems to work but NO.

It failed in the implementation. image (regex replace) image (direct replace)

hoangnhan2ka3 commented 9 months ago

Do you think it's because of these spaces? I wonder how they made these classes work?

image

I don't understand why it still works with those 'spaces' πŸ€”.

hoangnhan2ka3 commented 9 months ago

image image

hoangnhan2ka3 commented 9 months ago

I don't see any documentation about \2c === , in regex or HTML entity, maybe Tailwind has a rule to convert , in className to \2c or \2c (with following space)

And because of that, maybe it wouldn't make sense here?

image
hoangnhan2ka3 commented 9 months ago

(ref)

image

hoangnhan2ka3 commented 9 months ago

I found this may help

image

In CSS, class names cannot contain certain special characters like a comma (,). To get around this, special characters are escaped using a backslash () and their Unicode code. In your case, \2c represents a comma.

However, after each Unicode escape sequence in CSS, you need to add a space to prevent the CSS parser from continuing to read the next characters as part of the escape sequence. This explains why there is a space after \2c in your class name.

For example, if you have a class name .class\2c1, the CSS parser will interpret \2c1 as a Unicode escape sequence, not \2c. To avoid this, you need to add a space after the escape sequence: .class\2c 1.

In your case, the space after \2c does not affect the CSS syntax because it only appears in the class name, not in the value of a property.


In CSS, commas (,) are used to separate values ​​in a list or function. According to CSS syntax rules, you can add a space after a comma without affecting the syntax.

In your case, \2c represents a comma and the space after it does not affect the CSS syntax. In fact, adding space after a comma can help increase the readability of your CSS code.

And it solved my question

image

I don't understand why it still works with those 'spaces' πŸ€”.


image

The Unicode code for the comma character (,) is U+002C. In hexadecimal notation, 2C is the representation of the number 44 in decimal notation, which corresponds to the comma in the ASCII character table.

When you want to represent a special character in CSS using its Unicode code, you need to convert the Unicode code from hexadecimal to decimal, then prepend a backslash (). In the case of the comma, the Unicode code U+002C in hexadecimal becomes \2c in CSS.

So this is probably not a Tailwind rule, it's official!

hoangnhan2ka3 commented 9 months ago

I found this issue is way way more complex than I imagined. I can't replace the \2c with , directly. I tried 2 methods,

const v = ".nuzql9z\\[transition\\:nu3pow5\\(\\.nupku0r 1\\.nu3sdzf \\.nu3lzbe 1\\)_300ms\\2c color_ease_300ms\\] a"

 // 1. Direct replace 
console.log(v.replace("\\2c", ","))

// 2. Regex replace 
console.log(v.replace(/\\2c/, ","))

image

Yes, it seems to work but NO.

It failed in the implementation. image (regex replace) image (direct replace)

So I'm wondering what you're trying to replace \2c with a , for, for testing purposes or are you actually planning to replace it before obfuscate?

I think just add the regex \\2c (maybe \\2c or \\2c\s) to the existing regex?

Because the important thing here is that it seems that in current and previous versions, \s (or... I don't know) was used to stop searching class names with regex. And now we have a problem with spaces other than \2c that aren't used to break the class name, so we need to add \\2c\s or \\2c to avoid interrupt searching for a whole class names?

soranoo commented 9 months ago

I don't see any documentation about \2c === , in regex or HTML entity, maybe Tailwind has a rule to convert , in className to \2c or \2c (with following space)

And because of that, maybe it wouldn't make sense here?

image

Yes, you are right. It is just for my convenience.

I found this issue is way way more complex than I imagined. I can't replace the \2c with , directly. I tried 2 methods,

const v = ".nuzql9z\\[transition\\:nu3pow5\\(\\.nupku0r 1\\.nu3sdzf \\.nu3lzbe 1\\)_300ms\\2c color_ease_300ms\\] a"

 // 1. Direct replace 
console.log(v.replace("\\2c", ","))

// 2. Regex replace 
console.log(v.replace(/\\2c/, ","))

image Yes, it seems to work but NO. It failed in the implementation. image (regex replace) image (direct replace)

So I'm wondering what you're trying to replace \2c with a , for, for testing purposes or are you actually planning to replace it before obfuscate?

I think just add the regex \\2c (maybe \\2c or \\2c\s) to the existing regex?

Because the important thing here is that it seems that in current and previous versions, \s (or... I don't know) was used to stop searching class names with regex. And now we have a problem with spaces other than \2c that aren't used to break the class name, so we need to add \\2c\s or \\2c to avoid interrupt searching for a whole class names?

Here's the flow to dealing with HTML specific "symbols/things"

  1. Align symbols, make sure the CSS selector uses the same set of symbols as the HTML
  2. Match the CSS selector(or "class" in this stage) to the HTML
  3. Replace So it is meaningless to add the \\2c or \\2c\s to the matching regex.

image image

As I said it doesn't work in implementation.

Just in case I understand you wrongly, you can express it in code(pseudo code / actual code).

hoangnhan2ka3 commented 9 months ago

Please check my repo which shared with you, my all latest problem is in that repo

hoangnhan2ka3 commented 9 months ago

Files related to this issue:

src/app/_layouts/SideBar.tsx

src/styles/_globals.css

Then paste these 2 classNames:

  1. In whatever page.tsx u like:
    <div className="[&_a]:[transition:padding-left_cubic-bezier(.15,1.6,.75,1)_300ms,color_ease_300ms] [&_a]:will-change-[padding-left,_color]">
    <a href="/">
    Sample
    </a>
    {/* or */}
    <Link href="/">
    Sample
    </Link>
    </div>

into nav tag in SideBar.tsx like this:

<nav className={clsx(
  "fixed left-0 z-0 flex h-[calc(100%-var(--nhn-header-height))] w-44 flex-col justify-between gap-6 pb-6",
  "[&_a]:[transition:padding-left_cubic-bezier(.15,1.6,.75,1)_300ms,color_ease_300ms] [&_a]:will-change-[padding-left,_color]",
  "a:relative a:flex a:h-12 a:items-center a:pr-12",
)}>
  {/* remains */}
</nav>

and delete these lines in _globals.css:

/* TEMPORARY */

nav a {
  transition: padding-left 300ms cubic-bezier(.15,1.6,.75,1), color 300ms ease;
  will-change: padding-left, color;
}

/* END TEMPORARY */
hoangnhan2ka3 commented 9 months ago

Expected behavior is the smooth padding and color transition of the page label in hoangnhan.uk or hoangnhan.co.uk when u move between pages:

image