-
-
Notifications
You must be signed in to change notification settings - Fork 3
v3.0.0 #74
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
Conversation
Moves test files from the `src/handlers` directory to the `src/__tests__` directory for better organization and separation of concerns. This change improves the project structure and makes it easier to locate and manage test files. Also, updates import paths in test files.
Moves the test file from the `src` directory to the `__tests__` directory for better organization and separation of concerns. This change improves the project structure and makes it easier to locate test files.
Migrates the project's code formatting and linting from Prettier to Biome. This change removes the existing Prettier configuration and implements Biome. The Biome configuration enforces consistent code style and catches potential issues, improving code quality and maintainability.
Upgrades the core CSS transformation engine to `css-seasoning` and `lightningcss-wasm`. - Replaces PostCSS with `css-seasoning` for CSS parsing and transformation. - Integrates `lightningcss-wasm` for CSS minification and optimization. - Introduces a new `obfuscateCssFiles` function for processing CSS files. - Removes the deprecated `classLength` option. - Improves the way of css obfuscation with new logic and features. This change significantly enhances the performance and flexibility of the CSS obfuscation process.
Refactors the CSS obfuscation process to leverage the `css-seasoning` library, replacing custom CSS parsing and manipulation logic. This change improves maintainability and leverages a dedicated library for CSS transformations. Removes the old CSS test file as the functionality is now covered by `css-seasoning`.
Refactors utility functions for better organization and reusability. - Removes unused random string generation and string simplification functions. - Exports previously internal functions to enhance module accessibility and facilitate testing. - Streamlines the codebase by removing dead code and improving overall structure.
Introduces breaking changes to support TailwindCSS 4, nested CSS, and CSS ident obfuscation. - Updates configuration options, including renaming, merging, and removing deprecated options. - Migrates to css-seasoning v1.4.1. - Enables JS AST parsing by default. - Updates documentation with v3 migration guide.
Sets up semantic release to automate the release process. This includes: - Adding a release workflow file. - Installing semantic-release and related plugins. - Creating a release configuration file. This automates versioning, changelog generation, and package publishing.
…tency in tests and handlers
# 1.0.0-beta.1 (2025-04-25) ### Bug Fixes * fix html tests ([f517839](f517839)) * **html:** fix [#57](#57) ([a638538](a638538)) * **html:** fix incorrect html class name truncation ([7cb985d](7cb985d)) * **js-ast:** added support to `MemberExpression` [#45](#45) ([8c95ba0](8c95ba0)) * **js-ast:** added support to `TemplateLiteral` & `TemplateElement` [#45](#45) ([0dd46b3](0dd46b3)) ### Features * Enables semantic release for automated publishing ([b43a194](b43a194)) * Upgrades to v3 with new CSS transformation ([0afd780](0afd780)) * v3 migration with TailwindCSS 4 support ([668a5f6](668a5f6))
…ts for semantic release
# 3.0.0-beta.3 (2025-04-25) ### Bug Fixes * fix html tests ([f517839](f517839)) * **html:** fix [#57](#57) ([a638538](a638538)) * **html:** fix incorrect html class name truncation ([7cb985d](7cb985d)) * **js-ast:** added support to `MemberExpression` [#45](#45) ([8c95ba0](8c95ba0)) * **js-ast:** added support to `TemplateLiteral` & `TemplateElement` [#45](#45) ([0dd46b3](0dd46b3)) ### Features * Enables semantic release for automated publishing ([b43a194](b43a194)) * Upgrades to v3 with new CSS transformation ([0afd780](0afd780)) * v3 migration with TailwindCSS 4 support ([668a5f6](668a5f6))
# [3.0.0-beta.4](v3.0.0-beta.3...v3.0.0-beta.4) (2025-05-30) ### Features * support ident prefix/suffix options ([32952bf](32952bf))
# [3.0.0-beta.5](v3.0.0-beta.4...v3.0.0-beta.5) (2025-05-30) ### Bug Fixes * update key names in loadConversionTables for consistency ([3d6035d](3d6035d))
Reviewer's GuideThis PR overhauls the v3 release by rebuilding the CSS obfuscation core on css-seasoning and lightningcss to support nested rules and idents, introduces a unified path filtering mechanism, refactors utilities and handlers for CSS/HTML/JS with AST fallback, updates configuration and CLI integration, and refreshes documentation and CI workflows (including semantic-release). Sequence Diagram: CSS Obfuscation and File Update Process in v3.0.0sequenceDiagram
actor Developer
participant CLI
participant MainProcess as "index.ts (obfuscateCli -> obfuscate)"
participant CssHandler as "css.ts (obfuscateCssFiles)"
participant CssSeasoningLib as "css-seasoning"
participant LightningCssLib as "lightningcss"
participant FileSystem
participant UtilsProcess as "utils.ts (replaceJsonKeysInFiles)"
Developer->>CLI: Runs obfuscate command (e.g., obfuscate-build)
CLI->>MainProcess: obfuscateCli()
MainProcess->>MainProcess: Loads ConfigOptions_v3
alt Config.refreshClassConversionJson is true
MainProcess->>FileSystem: Deletes existing conversion.json
end
MainProcess->>CssHandler: obfuscateCssFiles(config)
CssHandler->>CssSeasoningLib: initTransform()
CssHandler->>LightningCssLib: init()
CssHandler->>UtilsProcess: findAllFilesWithExt(".css", buildFolderPath, pathFilters)
UtilsProcess-->>CssHandler: cssPaths[]
loop For each cssPath
CssHandler->>FileSystem: Read CSS file content
CssHandler->>CssSeasoningLib: transform(cssContent, mode, prefix, suffix, seed, ignorePatterns, existingTables)
CssSeasoningLib-->>CssHandler: obfuscatedCss, newPartialConversionTables
alt Config.removeOriginalCss is false
CssHandler->>CssHandler: Merge original CSS + obfuscatedCss
end
CssHandler->>LightningCssLib: transform(mergedOrObfuscatedCss, minify:true)
LightningCssLib-->>CssHandler: finalCssCode
CssHandler->>FileSystem: Write finalCssCode to outCssPath
CssHandler->>CssHandler: Aggregate newPartialConversionTables
end
CssHandler-->>MainProcess: finalAggregatedConversionTables
MainProcess->>FileSystem: Write finalAggregatedConversionTables to "conversion.json"
MainProcess->>UtilsProcess: replaceJsonKeysInFiles(finalAggregatedConversionTables, config)
UtilsProcess->>FileSystem: Read/Write HTML, JS, etc. files (applying obfuscated class names)
MainProcess-->>CLI: Log "Obfuscation Completed"
CLI-->>Developer: Shows logs
ER Diagram: Configuration Options for v3.0.0erDiagram
ConfigOptions {
boolean enable
string mode "random | simplify"
string buildFolderPath
string classConversionJsonFolderPath
boolean refreshClassConversionJson
PrefixSuffixOptions prefix
PrefixSuffixOptions suffix
IgnorePatterns ignorePatterns
string_array allowExtensions
RegExp_array contentIgnoreRegexes
string_RegExp_array whiteListedFolderPaths
string_RegExp_array blackListedFolderPaths
boolean enableMarkers
string_array markers
boolean removeMarkersAfterObfuscated
boolean removeOriginalCss
string generatorSeed "Optional, undefined for random"
boolean enableJsAst "Default: true"
string logLevel
}
PrefixSuffixOptions {
string selectors "Optional"
string idents "Optional"
}
IgnorePatterns {
string_RegExp_array selectors "Optional"
string_RegExp_array idents "Optional"
}
ConfigOptions ||--|{ PrefixSuffixOptions : has_prefix
ConfigOptions ||--|{ PrefixSuffixOptions : has_suffix
ConfigOptions ||--|{ IgnorePatterns : has_ignorePatterns
DeprecatedInV3 {
string classLength "Removed"
string classPrefix "Replaced by prefix.selectors"
string classSuffix "Replaced by suffix.selectors"
string_RegExp_array classIgnore "Replaced by ignorePatterns.selectors"
string simplify_seedable_mode "Removed from 'mode' options"
string generatorSeed_minus_one "'-1' for random seed replaced by 'undefined'"
boolean enableJsAst_false_default "Default changed from false to true"
}
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
WalkthroughThis update introduces a major version (v3) with breaking changes, including new configuration options, migration guides, and extensive refactoring of CSS obfuscation logic to use external libraries. The release process is automated with semantic-release, and Prettier is replaced by Biome for code formatting and linting. Deprecated options are marked, and documentation is updated accordingly. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI
participant Config
participant CSSHandler
participant Utils
participant ReleaseWorkflow
User->>CLI: Run obfuscateCli()
CLI->>Config: Load and normalize options
Config->>CSSHandler: obfuscateCssFiles(options)
CSSHandler->>Utils: Load conversion tables, filter files
CSSHandler->>CSSHandler: Obfuscate CSS using css-seasoning/lightningcss-wasm
CSSHandler->>Utils: Save conversion tables
CLI->>Utils: replaceJsonKeysInFiles(conversionTables, ...)
Utils->>Utils: Obfuscate HTML/JS files using conversion tables
CLI->>User: Done
User->>ReleaseWorkflow: Push or PR to main/beta
ReleaseWorkflow->>ReleaseWorkflow: Checkout, install, build, patch semantic-release, run release/dry-run
Possibly related PRs
Suggested labels
Poem
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 12
🧹 Nitpick comments (7)
biome.json (2)
1-7
: VCS configuration requires clarification.The VCS is disabled (
"enabled": false
) but still specifies Git settings. This configuration might be confusing.Consider either:
- Enabling VCS if you want Git integration, or
- Removing the unnecessary
clientKind
anduseIgnoreFile
properties when VCS is disabled."vcs": { - "enabled": false, - "clientKind": "git", - "useIgnoreFile": false + "enabled": false },
29-65
: Comprehensive and well-balanced linting rules.The linting configuration is thoughtful with appropriate severity levels:
Strengths:
- Good mix of error, warning, and info levels
- Focuses on code quality without being overly restrictive
- Enables TypeScript-specific best practices
noExplicitAny
as error promotes better typingMinor considerations:
noDebugger
as "info" might be too lenient for production codenoUnusedVariables
as "info" might miss important cleanup opportunitiesConsider upgrading these rules for better code quality:
"correctness": { - "noUnusedVariables": "info", + "noUnusedVariables": "warn", "useArrayLiterals": "error" }, "suspicious": { - "noDebugger": "info", + "noDebugger": "warn",src/__tests__/js-ast.test.ts (1)
1484-1485
: Consider adding a comment about the test data formatThe very long line of minified code could benefit from a comment explaining that it represents real-world minified/bundled JavaScript code to ensure the obfuscation handles such cases correctly.
it("should handle basic real world example 1", () => { + // Testing with real-world minified Next.js bundle code const code = `(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[185],{6773:function(e,t,n){Promise.resolve().then(n.bind(n,1845)),Promise.resolve().then(n.bind(n,7388)),Promise.resolve().then(n.bind(n,6016)),Promise.resolve().then(n.bind(n,1120)),Promise.resolve().then(n.bind(n,1255)),Promise.resolve().then(n.t.bind(n,5935,23)),Promise.resolve().then(n.t.bind(n,3710,23)),Promise.resolve().then(n.t.bind(n,3385,23)),Promise.resolve().then(n.bind(n,6212)),Promise.resolve().then(n.bind(n,1267)),Promise.resolve().then(n.bind(n,5322)),Promise.resolve().then(n.bind(n,9149))},6212:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return d}});var r=n(3827),i=n(703),l=n(8792),a={src:"/_next/static/media/some.svg",height:42,width:42,blurWidth:0,blurHeight:0},s=n(4090),o=n(7907);function d(){(0,o.usePathname)();let[e,t]=(0,s.useState)(!1),n=(0,s.useCallback)((e,t)=>{if(e.id===t)return!0;for(let r=0;r<e.children.length;r++)if(n(e.children[r],t))return!0;return!1},[]);return(0,s.useEffect)(()=>{t(n(document.body,"NotFoundPage"))},[n]),e?null:(0,r.jsx)(r.Fragment,{children:(0,r.jsx)("header",{className:"h-[4.4dvw] size-full bg-transparent px-[1.41dvw] flex items-center",children:(0,r.jsx)(l.default,{href:"/",className:"flex items-center flex-shrink-0",children:(0,r.jsx)(i.default,{className:"w-auto h-[1.88dvw]",src:a,alt:"Logo"})})})})}},1267:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return o}});var r=n(3827),i=n(8792),l=n(7907),a=n(4090),s=n(6314);function o(){let e=(0,l.usePathname)(),[t,n]=(0,a.useState)(!1),o=(0,a.useCallback)((e,t)=>{if(e.id===t)return!0;for(let n=0;n<e.children.length;n++)if(o(e.children[n],t))return!0;return!1},[]);(0,a.useEffect)(()=>{n(o(document.body,"NotFoundPage"))},[o]);let d=[{href:"/",label:"Home"},{href:"/tag1",label:"tag1"},{href:"/tag2",label:"tag2"},{href:"/tag3",label:"tag3"},{href:"/tag4",label:"tag4"},],[u,c]=(0,a.useState)(()=>{let t=d.find(t=>t.href===e);return t?t.label:"label"}),[h,f]=(0,a.useState)(()=>{if(e.startsWith("/tag1"))return"tag1";{let t=d.find(t=>t.href===e);return t?t.label:"label"}});return(0,a.useEffect)(()=>{e.startsWith("/tag1")&&f("tag1")},[e]),(0,a.useEffect)(()=>{e.startsWith("/tag1/")&&(f("tag1"),c("tag1"))},[e]),t?null:(0,r.jsx)(r.Fragment,{children:(0,r.jsx)("div",{className:"z-0 w-[11dvw] h-dvh [&_.side-box]:absolute [&_.side-box]:left-[0.94dvw] [&_.side-box]:top-0 [&_.side-box]:-z-10 [&_.side-box]:h-full [&_.side-box]:w-[calc(100%-0.94dvw)] [&_.side-box]:rounded-tl-[0.94dvw] [&_.side-box]:rounded-bl-[0.94dvw] [&_.side-box]:bg-gradient-to-r [&_.side-box]:from-[#2858ff] [&_.side-box]:to-85%",children:(0,r.jsx)("div",{className:" flex flex-col items-start size-full *:relative *:w-full *:font-bold [&_a]:flex [&_a]:items-center [&_a]:w-full [&_a]:pe-[2.82dvw] [&_a]:h-[2.82dvw] [&_a]:transition-[padding_color] [&_a]:ease-bounce [&_a]:duration-300 [&_#side-box-line]:absolute [&_#side-box-line]:left-0 [&_#side-box-line]:top-0 [&_#side-box-line]:-z-10 [&_#side-box-line]:h-full [&_#side-box-line]:w-[0.235dvw] [&_#side-box-line]:transition-opacity [&_#side-box-line]:duration-0 [&_#side-box-line]:rounded-tr-full [&_#side-box-line]:rounded-br-full [&_#side-box-line]:bg-[#2858ff] ",children:d.map(t=>(0,r.jsx)(s.E.div,{onHoverStart:()=>c(t.label),onHoverEnd:()=>c(h),onClick:()=>f(t.label),children:(0,r.jsxs)(i.default,{href:t.href,className:t.href===e||e.startsWith("/tag1")&&"/tag1"===t.href?"text-white ps-[2.115dvw]":"text-white/50 ps-[1.41dvw]",children:[t.href===e||e.startsWith("/tag1/")&&"/tag1"===t.href?(0,r.jsx)(s.E.div,{transition:{type:"spring",duration:.65,mass:.5},layoutId:"sideBox",className:"side-box"}):null,t.label,t.label===u||e.startsWith("/tag1/")&&"/tag1/"===t.href?(0,r.jsx)(s.E.div,{transition:{type:"spring",duration:.8},layoutId:"sideBoxLine",id:"side-box-line"}):null,]})},t.href))})})})}},9149:function(e,t,n){"use strict";n.r(t);var r=n(4404),i=n(4090),l=n(7717);let a=e=>{let{color:t,height:n,crawl:r,crawlSpeed:a,initialPosition:s,easing:o,speed:d,shadow:u,template:c,zIndex:h=99999999,delay:f}=e,$=null!=t?t:"#29d";return(u||void 0===u)&&(u||"box-shadow:0 0 10px ".concat($,",0 0 5px ").concat($)),i.useEffect(()=>{let e;function t(){clearTimeout(e),e=setTimeout(l.start,null!=f?f:200)}function n(){clearTimeout(e),l.done()}l.configure({trickle:null==r||r,trickleSpeed:null!=a?a:200,minimum:null!=s?s:.55+.2*Math.random(),easing:null!=o?o:"ease-out",speed:null!=d?d:180,template:null!=c?c:'<div class="bar" role="bar"><div class="peg"></div></div>'});var i=document.querySelectorAll("html");function u(e){try{let r=e.target,l=function(e){for(;e&&"a"!==e.tagName.toLowerCase();)e=e.parentElement;return e}(r),a=null==l?void 0:l.href;if(a){var s;let o=window.location.href,d="_blank"===l.target,u=a.startsWith("blob:"),c=function(e,t){let n=new URL(e),r=new URL(t);if(n.hostname===r.hostname&&n.pathname===r.pathname&&n.search===r.search){let i=n.hash,l=r.hash;return i!==l&&n.href.replace(i,"")===r.href.replace(l,"")}return!1}(o,a),h;a===o||c||d||u||e.ctrlKey?(t(),n(),[].forEach.call(i,function(e){e.classList.remove("nprogress-busy")})):(t(),h=(s=window.history).pushState,s.pushState=function(){return n(),[].forEach.call(i,function(e){e.classList.remove("nprogress-busy")}),h.apply(s,arguments)})}}catch(f){t(),n()}}return document.addEventListener("click",u),()=>{document.removeEventListener("click",u)}},[r,a,f,o,s,d,c]),null};t.default=a,a.propTypes={color:r.string,height:r.number,crawl:r.bool,crawlSpeed:r.number,initialPosition:r.number,easing:r.string,speed:r.number,delay:r.number,template:r.string,shadow:r.oneOfType([r.string,r.bool]),zIndex:r.number}},5322:function(e,t,n){"use strict";n.r(t);var r=n(3827),i=n(4090);t.default=()=>{let e=(0,i.useRef)(null);return(0,i.useEffect)(()=>{let t=e.current,n=null==t?void 0:t.getContext("2d"),r={x:.5*window.innerWidth,y:.5*window.innerHeight},i={pointsNumber:8,widthFactor:4,spring:.35,friction:.48},l=Array(i.pointsNumber);for(let a=0;a<i.pointsNumber;a++)l[a]={x:r.x,y:r.y,dx:0,dy:0};let s=(e,t)=>{r.x=e,r.y=t},o=()=>{e.current&&(e.current.width=window.innerWidth,e.current.height=window.innerHeight)},d=e=>{if(n&&(n.strokeStyle="#e2ecfc"),t&&(null==n||n.clearRect(0,0,t.width,t.height)),l.forEach((e,t)=>{let n=0===t?r:l[t-1],a=0===t?.4*i.spring:i.spring;e.dx+=(n.x-e.x)*a,e.dy+=(n.y-e.y)*a,e.dx*=i.friction,e.dy*=i.friction,e.x+=e.dx,e.y+=e.dy}),n){n.lineCap="round",n.beginPath(),n.moveTo(l[0].x,l[0].y);for(let a=1;a<l.length-1;a++){let s=.5*(l[a].x+l[a+1].x),o=.5*(l[a].y+l[a+1].y);n.quadraticCurveTo(l[a].x,l[a].y,s,o),n.lineWidth=i.widthFactor*(i.pointsNumber-a),n.stroke()}n.lineTo(l[l.length-1].x,l[l.length-1].y),n.stroke()}window.requestAnimationFrame(d)},u=()=>{o()},c=e=>{s(e.pageX,e.pageY)},h=e=>{s(e.pageX,e.pageY)},f=e=>{s(e.targetTouches[0].pageX,e.targetTouches[0].pageY)};return window.addEventListener("click",c),window.addEventListener("mousemove",h),window.addEventListener("touchmove",f),window.addEventListener("resize",u),o(),d(0),()=>{window.removeEventListener("click",c),window.removeEventListener("mousemove",h),window.removeEventListener("touchmove",f),window.removeEventListener("resize",u)}},[]),(0,r.jsx)("canvas",{ref:e})}},3385:function(){}},function(e){e.O(0,[314,250,134,336,971,69,744],function(){return e(e.s=6773)}),_N_E=e.O()},]);`; const expectedCode = `(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[185],{6773:function(e,t,n){Promise.resolve().then(n.bind(n,1845)),Promise.resolve().then(n.bind(n,7388)),Promise.resolve().then(n.bind(n,6016)),Promise.resolve().then(n.bind(n,1120)),Promise.resolve().then(n.bind(n,1255)),Promise.resolve().then(n.t.bind(n,5935,23)),Promise.resolve().then(n.t.bind(n,3710,23)),Promise.resolve().then(n.t.bind(n,3385,23)),Promise.resolve().then(n.bind(n,6212)),Promise.resolve().then(n.bind(n,1267)),Promise.resolve().then(n.bind(n,5322)),Promise.resolve().then(n.bind(n,9149))},6212:function(e,t,n){\"usestrict\";n.r(t),n.d(t,{default:function(){returnd}});varr=n(3827),i=n(703),l=n(8792),a={src:\"/_next/static/media/some.svg\",height:42,width:42,blurWidth:0,blurHeight:0},s=n(4090),o=n(7907);functiond(){(0,o.usePathname)();let[e,t]=(0,s.useState)(!1),n=(0,s.useCallback)((e,t)=>{if(e.id===t)return!0;for(letr=0;r<e.children.length;r++)if(n(e.children[r],t))return!0;return!1},[]);return(0,s.useEffect)(()=>{t(n(document.body,\"NotFoundPage\"))},[n]),e?null:(0,r.jsx)(r.Fragment,{children:(0,r.jsx)(\"header\",{className:\"{{obfuscated}}\",children:(0,r.jsx)(l.default,{href:\"/\",className:\"{{obfuscated}}\",children:(0,r.jsx)(i.default,{className:\"{{obfuscated}}\",src:a,alt:\"Logo\"})})})})}},1267:function(e,t,n){\"usestrict\";n.r(t),n.d(t,{default:function(){returno}});varr=n(3827),i=n(8792),l=n(7907),a=n(4090),s=n(6314);functiono(){lete=(0,l.usePathname)(),[t,n]=(0,a.useState)(!1),o=(0,a.useCallback)((e,t)=>{if(e.id===t)return!0;for(letn=0;n<e.children.length;n++)if(o(e.children[n],t))return!0;return!1},[]);(0,a.useEffect)(()=>{n(o(document.body,\"NotFoundPage\"))},[o]);letd=[{href:\"/\",label:\"Home\"},{href:\"/tag1\",label:\"tag1\"},{href:\"/tag2\",label:\"tag2\"},{href:\"/tag3\",label:\"tag3\"},{href:\"/tag4\",label:\"tag4\"}],[u,c]=(0,a.useState)(()=>{lett=d.find(t=>t.href===e);returnt?t.label:\"label\"}),[h,f]=(0,a.useState)(()=>{if(e.startsWith(\"/tag1\"))return\"tag1\";{lett=d.find(t=>t.href===e);returnt?t.label:\"label\"}});return(0,a.useEffect)(()=>{e.startsWith(\"/tag1\")&&f(\"tag1\")},[e]),(0,a.useEffect)(()=>{e.startsWith(\"/tag1/\")&&(f(\"tag1\"),c(\"tag1\"))},[e]),t?null:(0,r.jsx)(r.Fragment,{children:(0,r.jsx)(\"div\",{className:\"{{obfuscated}}\",children:(0,r.jsx)(\"div\",{className:\"{{obfuscated}}\",children:d.map(t=>(0,r.jsx)(s.E.div,{onHoverStart:()=>c(t.label),onHoverEnd:()=>c(h),onClick:()=>f(t.label),children:(0,r.jsxs)(i.default,{href:t.href,className:t.href===e||e.startsWith(\"/tag1\")&&\"/tag1\"===t.href?\"{{obfuscated}}\":\"{{obfuscated}}\",children:[t.href===e||e.startsWith(\"/tag1/\")&&\"/tag1\"===t.href?(0,r.jsx)(s.E.div,{transition:{type:\"spring\",duration:.65,mass:.5},layoutId:\"sideBox\",className:\"{{obfuscated}}\"}):null,t.label,t.label===u||e.startsWith(\"/tag1/\")&&\"/tag1/\"===t.href?(0,r.jsx)(s.E.div,{transition:{type:\"spring\",duration:.8},layoutId:\"sideBoxLine\",id:\"side-box-line\"}):null]})},t.href))})})})}},9149:function(e,t,n){\"usestrict\";n.r(t);varr=n(4404),i=n(4090),l=n(7717);leta=e=>{let{color:t,height:n,crawl:r,crawlSpeed:a,initialPosition:s,easing:o,speed:d,shadow:u,template:c,zIndex:h=99999999,delay:f}=e,$=null!=t?t:\"#29d\";return(u||void0===u)&&(u||\"box-shadow:0010px\".concat($,\",005px\").concat($)),i.useEffect(()=>{lete;functiont(){clearTimeout(e),e=setTimeout(l.start,null!=f?f:200)}functionn(){clearTimeout(e),l.done()}l.configure({trickle:null==r||r,trickleSpeed:null!=a?a:200,minimum:null!=s?s:.55+.2*Math.random(),easing:null!=o?o:\"ease-out\",speed:null!=d?d:180,template:null!=c?c:\"<divclass=\\\"bar\\\"role=\\\"bar\\\"><divclass=\\\"peg\\\"></div></div>\"});vari=document.querySelectorAll(\"html\");functionu(e){try{letr=e.target,l=function(e){for(;e&&\"a\"!==e.tagName.toLowerCase();)e=e.parentElement;returne}(r),a=null==l?void0:l.href;if(a){vars;leto=window.location.href,d=\"_blank\"===l.target,u=a.startsWith(\"blob:\"),c=function(e,t){letn=newURL(e),r=newURL(t);if(n.hostname===r.hostname&&n.pathname===r.pathname&&n.search===r.search){leti=n.hash,l=r.hash;returni!==l&&n.href.replace(i,\"\")===r.href.replace(l,\"\")}return!1}(o,a),h;a===o||c||d||u||e.ctrlKey?(t(),n(),[].forEach.call(i,function(e){e.classList.remove(\"nprogress-busy\")})):(t(),h=(s=window.history).pushState,s.pushState=function(){returnn(),[].forEach.call(i,function(e){e.classList.remove(\"nprogress-busy\")}),h.apply(s,arguments)})}}catch(f){t(),n()}}returndocument.addEventListener(\"click\",u),()=>{document.removeEventListener(\"click\",u)}},[r,a,f,o,s,d,c]),null};t.default=a,a.propTypes={color:r.string,height:r.number,crawl:r.bool,crawlSpeed:r.number,initialPosition:r.number,easing:r.string,speed:r.number,delay:r.number,template:r.string,shadow:r.oneOfType([r.string,r.bool]),zIndex:r.number}},5322:function(e,t,n){\"usestrict\";n.r(t);varr=n(3827),i=n(4090);t.default=()=>{lete=(0,i.useRef)(null);return(0,i.useEffect)(()=>{lett=e.current,n=null==t?void0:t.getContext(\"2d\"),r={x:.5*window.innerWidth,y:.5*window.innerHeight},i={pointsNumber:8,widthFactor:4,spring:.35,friction:.48},l=Array(i.pointsNumber);for(leta=0;a<i.pointsNumber;a++)l[a]={x:r.x,y:r.y,dx:0,dy:0};lets=(e,t)=>{r.x=e,r.y=t},o=()=>{e.current&&(e.current.width=window.innerWidth,e.current.height=window.innerHeight)},d=e=>{if(n&&(n.strokeStyle=\"#e2ecfc\"),t&&(null==n||n.clearRect(0,0,t.width,t.height)),l.forEach((e,t)=>{letn=0===t?r:l[t-1],a=0===t?.4*i.spring:i.spring;e.dx+=(n.x-e.x)*a,e.dy+=(n.y-e.y)*a,e.dx*=i.friction,e.dy*=i.friction,e.x+=e.dx,e.y+=e.dy}),n){n.lineCap=\"round\",n.beginPath(),n.moveTo(l[0].x,l[0].y);for(leta=1;a<l.length-1;a++){lets=.5*(l[a].x+l[a+1].x),o=.5*(l[a].y+l[a+1].y);n.quadraticCurveTo(l[a].x,l[a].y,s,o),n.lineWidth=i.widthFactor*(i.pointsNumber-a),n.stroke()}n.lineTo(l[l.length-1].x,l[l.length-1].y),n.stroke()}window.requestAnimationFrame(d)},u=()=>{o()},c=e=>{s(e.pageX,e.pageY)},h=e=>{s(e.pageX,e.pageY)},f=e=>{s(e.targetTouches[0].pageX,e.targetTouches[0].pageY)};returnwindow.addEventListener(\"click\",c),window.addEventListener(\"mousemove\",h),window.addEventListener(\"touchmove\",f),window.addEventListener(\"resize\",u),o(),d(0),()=>{window.removeEventListener(\"click\",c),window.removeEventListener(\"mousemove\",h),window.removeEventListener(\"touchmove\",f),window.removeEventListener(\"resize\",u)}},[]),(0,r.jsx)(\"canvas\",{ref:e})}},3385:function(){}},function(e){e.O(0,[314,250,134,336,971,69,744],function(){returne(e.s=6773)}),_N_E=e.O()}]);`;
src/handlers/css.ts (1)
15-16
: Complete or remove the incomplete TODO comment.The TODO comment "html failed with ." is incomplete and doesn't provide enough context about what failed or what needs to be done.
Either complete the TODO comment with proper context or remove it:
-// TODO: html failed with . +// TODO: Handle HTML files that fail with specific selectors containing dotssrc/utils.ts (3)
284-286
: Remove commented out code.Dead code should be removed rather than left as comments to maintain code cleanliness.
- // cssPaths.forEach(async (cssPath) => { - // await obfuscateCss(classConversion, cssPath, removeOriginalCss, !enableObfuscateMarkerClasses); - // });
289-356
: Consider using a local variable instead of reassigning the parameter.The static analysis tool correctly identifies that reassigning the
fileContent
parameter can be confusing. While the current implementation works, using a local variable would improve code clarity.Consider refactoring to use a local variable:
export const obfuscateKeys = ( selectorConversion: SelectorConversion, fileContent: string, contentIgnoreRegexes: RegExp[] = [], ) => { //ref: https://github.com/n4j1Br4ch1D/postcss-obfuscator/blob/main/utils.js const usedKeys = new Set<string>(); + let modifiedContent = fileContent; Object.keys(selectorConversion).forEach((key) => { - const fileContentOriginal = fileContent; + const fileContentOriginal = modifiedContent; // let keyUse = escapeRegExp(key.slice(1).replace(/\\/g, "")); let keyUse = cssUnescape(key).slice(1); keyUse = escapeRegExp(keyUse.replace(/\\/g, "")); // escape the key //? sample: "text-sm w-full\n text-right\n p-2 flex gap-2 hover:bg-gray-100 dark:hover:bg-red-700 text-right" const exactMatchRegex = new RegExp( `([\\s"'\\\`]|^)(${keyUse})(?=$|[\\s"'\\\`]|\\\\n|\\\\",|\\\\"})`, "g", ); // match exact wording & avoid ` ' "" // exactMatchRegex = new RegExp(`([\\s"'\\\`]|^)(${keyUse})(?=$|[\\s"'\\\`])`, 'g'); // match exact wording & avoid ` ' "" const replacement = `$1${selectorConversion[key].slice(1).replace(/\\/g, "").slice(1)}`; - const matches = fileContent.match(exactMatchRegex); + const matches = modifiedContent.match(exactMatchRegex); const originalObscuredContentPairs = matches?.map((match) => { return { originalContent: match, obscuredContent: match.replace(exactMatchRegex, replacement), }; }); - fileContent = fileContent.replace(exactMatchRegex, replacement); // capture preceding space + modifiedContent = modifiedContent.replace(exactMatchRegex, replacement); // capture preceding spaceThen update all other occurrences of
fileContent
tomodifiedContent
within the function and returnmodifiedContent
instead.🧰 Tools
🪛 Biome (1.9.4)
[error] 320-320: Reassigning a function parameter is confusing.
The parameter is declared here:
Use a local variable instead.
(lint/style/noParameterAssign)
[error] 338-338: Reassigning a function parameter is confusing.
The parameter is declared here:
Use a local variable instead.
(lint/style/noParameterAssign)
117-117
: Fix typo: "registery" should be "registry".There's a consistent typo throughout the code where "registery" should be "registry".
-export const usedKeyRegistery = new Set<string>(); +export const usedKeyRegistry = new Set<string>();-export const addKeysToRegistery = (usedKeys: Set<string> | string[]) => { +export const addKeysToRegistry = (usedKeys: Set<string> | string[]) => { usedKeys.forEach((key) => { - usedKeyRegistery.add(key); + usedKeyRegistry.add(key); }); };Also update all references to this variable and function throughout the codebase.
Also applies to: 495-495
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (27)
.github/workflows/auto_test.yml
(2 hunks).github/workflows/release.yml
(1 hunks).prettierignore
(0 hunks).prettierrc
(0 hunks)README.md
(15 hunks)bin/cli.mjs
(1 hunks)biome.json
(1 hunks)docs/upgrade-to-v3.md
(1 hunks)package.json
(3 hunks)release.config.cjs
(1 hunks)src/__tests__/html.test.ts
(1 hunks)src/__tests__/js-ast.test.ts
(63 hunks)src/__tests__/js.test.ts
(1 hunks)src/__tests__/utils.test.ts
(1 hunks)src/config.ts
(1 hunks)src/handlers/css.test.ts
(0 hunks)src/handlers/css.ts
(1 hunks)src/handlers/html.test.ts
(0 hunks)src/handlers/html.ts
(1 hunks)src/handlers/js-ast.ts
(16 hunks)src/handlers/js.test.ts
(0 hunks)src/handlers/js.ts
(2 hunks)src/index.ts
(3 hunks)src/types.ts
(1 hunks)src/utils.test.ts
(0 hunks)src/utils.ts
(13 hunks)tsconfig.json
(2 hunks)
💤 Files with no reviewable changes (6)
- .prettierignore
- .prettierrc
- src/handlers/js.test.ts
- src/utils.test.ts
- src/handlers/html.test.ts
- src/handlers/css.test.ts
🧰 Additional context used
🧬 Code Graph Analysis (11)
src/__tests__/js.test.ts (1)
src/handlers/js.ts (1)
searchForwardComponent
(13-25)
src/__tests__/utils.test.ts (1)
src/utils.ts (3)
findContentBetweenMarker
(451-493)getFilenameFromPath
(368-371)duplicationCheck
(572-575)
src/__tests__/js-ast.test.ts (1)
src/handlers/js-ast.ts (1)
searchStringLiterals
(80-358)
src/__tests__/html.test.ts (2)
src/types.ts (1)
SelectorConversion
(9-9)src/handlers/html.ts (1)
obfuscateHtmlClassNames
(9-154)
src/handlers/html.ts (3)
src/types.ts (1)
SelectorConversion
(9-9)src/utils.ts (1)
obfuscateKeys
(289-356)src/handlers/js.ts (1)
obfuscateJs
(138-196)
src/index.ts (3)
src/types.ts (1)
Options
(11-55)src/utils.ts (5)
setLogLevel
(94-96)log
(57-92)findAllFilesWithExt
(511-553)getFilenameFromPath
(368-371)replaceJsonKeysInFiles
(119-287)src/handlers/css.ts (1)
obfuscateCssFiles
(142-220)
src/types.ts (1)
src/index.ts (1)
OptionalOptions
(139-139)
src/handlers/js.ts (3)
src/utils.ts (7)
findClosestSymbolPosition
(400-449)findContentBetweenMarker
(451-493)obfuscateKeys
(289-356)addKeysToRegistery
(495-499)log
(57-92)replaceFirstMatch
(555-565)normalizePath
(387-389)src/types.ts (1)
SelectorConversion
(9-9)src/handlers/js-ast.ts (1)
obfuscateJsWithAst
(17-71)
src/handlers/js-ast.ts (2)
src/types.ts (1)
SelectorConversion
(9-9)src/utils.ts (1)
obfuscateKeys
(289-356)
src/config.ts (1)
src/types.ts (2)
Options
(11-55)OptionalOptions
(57-82)
src/utils.ts (3)
src/types.ts (2)
LogLevel
(7-7)SelectorConversion
(9-9)src/handlers/html.ts (1)
obfuscateHtmlClassNames
(9-154)src/handlers/js.ts (1)
obfuscateJs
(138-196)
🪛 Biome (1.9.4)
src/__tests__/js-ast.test.ts
[error] 51-51: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 82-82: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 126-126: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 156-156: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 192-192: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 225-225: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 264-264: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 305-305: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 348-348: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 387-387: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 426-426: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 473-473: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 522-522: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 561-561: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 592-592: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 621-621: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 658-658: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 695-695: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 730-730: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 771-771: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 812-812: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 857-857: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 902-902: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 935-935: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 970-970: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 1003-1003: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 1040-1040: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 1077-1077: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 1118-1118: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 1173-1173: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 1218-1218: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 1257-1257: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 1294-1294: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 1339-1339: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 1390-1390: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 1427-1427: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
[error] 1458-1458: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
src/utils.ts
[error] 338-338: Reassigning a function parameter is confusing.
The parameter is declared here:
Use a local variable instead.
(lint/style/noParameterAssign)
🪛 LanguageTool
README.md
[style] ~32-~32: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... TailwindCSS 4. - Support nested CSS. - Support CSS idents obfuscation. #### 📌 Config...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
[grammar] ~37-~37: Did you mean the adverb “no longer”?
Context: ...d by default. - Default generatorSeed
not longer fixed to -1
, but a random string. - `...
(NOT_LONGER)
[grammar] ~38-~38: Did you mean the adverb “no longer”?
Context: ...m string. - simplify-seedable
mode is not longer supported. Use random
mode instead. -...
(NOT_LONGER)
[grammar] ~40-~40: Did you mean the adverb “no longer”?
Context: ...ead. - Deprecated classLength
option, not longer supported. - Added ignorePatterns
opt...
(NOT_LONGER)
[grammar] ~42-~42: Did you mean the adverb “no longer”?
Context: ...ts that match the regexes or strings. - Not longer preserve TailwindCSS dark mode class na...
(NOT_LONGER)
[misspelling] ~56-~56: Use “an” instead of ‘a’ if the following word starts with a vowel sound, e.g. ‘an article’, ‘an hour’.
Context: ...More configuration options - Now become a independent solution (no need to patch ...
(EN_A_VS_AN)
[misspelling] ~323-~323: Use “an” instead of ‘a’ if the following word starts with a vowel sound, e.g. ‘an article’, ‘an hour’.
Context: ...e original CSS from CSS files if it has a obfuscated version. (NOT recommended ...
(EN_A_VS_AN)
🪛 markdownlint-cli2 (0.17.2)
README.md
20-20: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3
(MD001, heading-increment)
24-24: Blank line inside blockquote
null
(MD028, no-blanks-blockquote)
170-170: Heading levels should only increment by one level at a time
Expected: h4; Actual: h5
(MD001, heading-increment)
188-188: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
318-318: No empty links
null
(MD042, no-empty-links)
319-319: No empty links
null
(MD042, no-empty-links)
323-323: Table pipe style
Expected: leading_and_trailing; Actual: leading_only; Missing trailing pipe
(MD055, table-pipe-style)
🔇 Additional comments (54)
bin/cli.mjs (1)
5-5
:❌ Incorrect review comment
Verify async handling for obfuscateCli() call.
According to the AI summary,
obfuscateCli()
is now an async function in the updated codebase. If this is correct, the function call should be awaited to ensure the CLI process doesn't exit before the async operation completes.
🏁 Script executed:
#!/bin/bash # Description: Check if obfuscateCli is an async function that should be awaited # Expected: Find async function definition and determine if CLI call needs await # Search for obfuscateCli function definition ast-grep --pattern 'export const obfuscateCli = async $_' ast-grep --pattern 'export async function obfuscateCli($_) { $$$ }' ast-grep --pattern 'const obfuscateCli = async $_' # Also check for any existing await patterns in CLI usage rg -A 3 -B 3 'obfuscateCli\(\)'Length of output: 331
#!/bin/bash # Search for the obfuscateCli definition in source and dist directories rg -n 'obfuscateCli' -C 5 src rg -n 'obfuscateCli' -C 5 dist/index.js
#!/bin/bash # Search for async definition of obfuscateCli across the repo rg -n "async function obfuscateCli" -C2 . rg -n "obfuscateCli\s*=\s*async" -C2 . rg -n "export.*obfuscateCli" -C2 dist/index.js rg -n "async.*obfuscateCli" -C2 src
obfuscateCli() remains synchronous – no await required
We searched the entire codebase for an
async
definition ofobfuscateCli
(includingdist/index.js
) and found none. The CLI call in bin/cli.mjs is still a normal, synchronous invocation.
- bin/cli.mjs (line 5): keep
obfuscateCli();
as is.Likely an incorrect or invalid review comment.
tsconfig.json (2)
16-16
: Good addition for consistent line endings.Adding the
newLine: "LF"
option ensures consistent line endings across different operating systems.
27-27
: Appropriate exclusion of release configuration.Correctly excluding
release.config.cjs
from TypeScript compilation since it's a CommonJS configuration file that doesn't need to be processed by the TypeScript compiler..github/workflows/auto_test.yml (2)
7-9
: Good formatting consistency improvement.Removing extra spaces in the branch arrays improves YAML formatting consistency.
24-24
: Good practice updating to latest action version.Updating from
actions/checkout@v3
to@v4
keeps the workflow current with the latest GitHub Actions version.release.config.cjs (1)
1-17
: Well-configured semantic-release setup with one question.The semantic-release configuration follows best practices with proper branch setup, standard plugins, and appropriate commit message formatting.
Verify if GitHub plugin should be enabled.
The
@semantic-release/github
plugin is commented out. This plugin is commonly used to create GitHub releases. Please confirm if this is intentional or if it should be enabled for this project.package.json (5)
3-3
: Major version bump is appropriate for the breaking changes.The version update to
3.0.0-beta.5
correctly reflects the breaking changes introduced in this release, including deprecated configuration options and architectural changes.
13-13
: Good fix for JSON syntax error.The correction of the
pub@beta
script syntax resolves a potential JSON parsing issue.
14-15
: Well-structured automation scripts.The addition of
semantic-release
andlint
scripts supports the new automated release workflow and code quality enforcement with Biome.
38-49
:✅ Verification successful
Verify semantic-release and Biome integration.
The migration from Prettier to Biome and addition of semantic-release automation looks good. Ensure the Biome configuration is properly set up in the repository.
🏁 Script executed:
#!/bin/bash # Check if Biome configuration exists if [ -f "biome.json" ]; then echo "Biome configuration found:" cat biome.json else echo "Warning: biome.json not found" fi # Check if semantic-release configuration exists if [ -f "release.config.cjs" ]; then echo "Semantic-release configuration found:" cat release.config.cjs else echo "Warning: release.config.cjs not found" fi # Check if prettier files were removed if [ -f ".prettierrc" ] || [ -f ".prettierignore" ]; then echo "Warning: Prettier configuration files still exist" ls -la .prettier* else echo "Prettier configuration files properly removed" fiLength of output: 2565
Semantic-release and Biome integration verified
- ✅
biome.json
is present and correctly configured for formatting, linting, and import organization.- ✅
release.config.cjs
exists with defined branches and plugins.- ✅ Prettier configuration files (
.prettierrc
/.prettierignore
) have been removed.Everything is set up correctly—no further changes needed.
31-34
:✅ Verification successful
Verify dependency versions for security and compatibility.
The new dependencies
css-seasoning
andlightningcss-wasm
represent a significant architectural change. Please ensure these versions are secure and stable.
🏁 Script executed:
#!/bin/bash # Check for security advisories on new dependencies echo "Checking css-seasoning security advisories..." npm audit --package-lock-only --audit-level moderate 2>/dev/null | grep -A 10 "css-seasoning" || echo "No issues found for css-seasoning" echo "Checking lightningcss-wasm security advisories..." npm audit --package-lock-only --audit-level moderate 2>/dev/null | grep -A 10 "lightningcss-wasm" || echo "No issues found for lightningcss-wasm" # Check latest versions echo "Latest css-seasoning version:" npm view css-seasoning version echo "Latest lightningcss-wasm version:" npm view lightningcss-wasm versionLength of output: 768
Dependency audit passed – no security concerns
- css-seasoning@1.9.0 is the latest release and has no known advisories.
- lightningcss-wasm@^1.29.3 has no known advisories; its latest minor version is 1.30.1 (no breaking changes).
No further action required.
src/__tests__/js.test.ts (1)
1-128
: Excellent comprehensive test suite.This test file provides thorough coverage of the
searchForwardComponent
function with well-structured tests that follow the Arrange-Act-Assert pattern. The test cases effectively cover:
- ✅ Valid component extraction from
o.jsx()
calls- ✅ Proper handling of quoted strings (which should be ignored)
- ✅ Multiple component matches in single content
- ✅ Edge cases like empty content and special characters
- ✅ Complex scenarios with line breaks and nested calls
The tests align perfectly with the regex pattern
(?<=\.jsx\()[^,|"|']+
from the implementation, ensuring robust validation of the component extraction logic..github/workflows/release.yml (2)
16-20
: Appropriate permissions for release workflow.The permissions are correctly scoped for the release tasks: contents and issues/PR writing for release operations, and id-token for npm provenance.
37-48
: Good conditional logic for release vs dry-run.The conditional execution properly handles different scenarios - full release on push and dry-run on pull requests. Token usage follows security best practices.
src/config.ts (2)
10-37
: Excellent deprecation strategy with clear migration path.The deprecation comments are comprehensive and provide clear guidance for migration to the new structure. This approach maintains backward compatibility while guiding users toward the new API.
25-42
: Well-structured configuration reorganization.The new nested structure for
prefix
/suffix
andignorePatterns
is more logical and provides better organization for selectors vs idents. This aligns well with the CSS transformation engine rebuild mentioned in the PR objectives.docs/upgrade-to-v3.md (1)
1-23
: Excellent migration documentation with comprehensive coverage.This upgrade guide provides clear and detailed information about breaking changes in v3.0.0. The structured table format makes it easy for users to understand the migration path from v2.x to v3.x.
Key strengths:
- Clear categorization of configuration changes
- Helpful notes explaining the reasoning behind changes
- Proper marking of removed (⛔) and deprecated features
- Coverage of new features like CSS ident obfuscation
The documentation effectively addresses the major changes mentioned in the PR objectives including TailwindCSS 4 support and nested CSS handling.
src/__tests__/html.test.ts (11)
1-9
: Well-structured test imports and setup.The imports are clean and appropriately typed, correctly importing the
SelectorConversion
type and the function under test.
11-23
: Basic functionality test is well-implemented.This test correctly verifies the core obfuscation functionality with proper assertions for both the obfuscated content and used keys tracking.
25-43
: Excellent test for marker class scoping functionality.This test properly validates the obfuscate marker class feature, ensuring that obfuscation only occurs within the scope of elements containing the marker class.
45-66
: Good negative test case for marker class scoping.This test correctly verifies that classes outside the marker class scope are not obfuscated, which is crucial for the scoping functionality.
68-88
: Comprehensive script tag handling test.This test covers the important functionality of obfuscating class names within JavaScript code inside script tags. The test data with JSON-like structure is realistic and tests the JavaScript obfuscation integration.
90-101
: Good coverage of void tag handling.This test ensures that self-closing/void tags are handled correctly, which is important for HTML parsing accuracy.
103-116
: Appropriate test for HTML comment preservation.This test verifies that HTML comments are preserved while still obfuscating surrounding content.
118-129
: Good edge case coverage for empty and classless HTML.These tests properly handle edge cases where HTML has no classes or is completely empty, ensuring the function behaves gracefully.
Also applies to: 131-142
144-159
: Excellent test for multiple classes per element.This test verifies that multiple classes on a single element are all obfuscated correctly, which is a common use case.
161-178
: Good test for nested HTML structures.This test covers complex nested HTML with multiple classes across different levels, ensuring the obfuscation works recursively.
237-253
: Excellent test for HTML entity handling.This test addresses a specific issue (#57) related to HTML entities in attributes, ensuring proper escaping and unescaping behavior. The reference to the GitHub issue provides valuable context.
src/__tests__/utils.test.ts (3)
1-11
: Clean imports and test organization.The imports are properly structured and the test file is well-organized with clear section headers.
47-127
: Comprehensive test coverage for getFilenameFromPath.Excellent test suite with thorough edge case coverage including:
- Unix and Windows paths
- Files with/without extensions
- Special characters and spaces
- Empty strings and directory-only paths
- Hidden files (dot files)
The test coverage is exemplary and handles all realistic scenarios.
133-221
: Thorough test coverage for duplicationCheck.Comprehensive test suite covering:
- Arrays with and without duplicates
- Empty arrays and single-element arrays
- Case sensitivity
- Non-adjacent duplicates
- Similar but unique strings
The test coverage is complete and well-thought-out.
biome.json (3)
8-18
: Appropriate file ignore patterns.The ignored directories are standard for a Node.js/TypeScript project and include all common build artifacts and dependencies.
19-28
: Good formatter configuration.The formatter settings are sensible:
- 80-character line width (good for readability)
- 2-space indentation (consistent with modern practices)
- LF line endings (cross-platform friendly)
- Import organization enabled (helps maintain clean code)
66-73
: Appropriate JavaScript formatting settings.The JavaScript-specific formatter settings are consistent with modern practices:
- Double quotes for strings
- Always use semicolons
- Trailing commas (helps with git diffs)
src/__tests__/js-ast.test.ts (5)
2-6
: LGTM!Good improvement to use explicit type imports for TypeScript types.
8-10
: LGTM!Good conversion to arrow function syntax for consistency.
51-54
: Consistent formatting improvementGood addition of semicolons to callback return statements for consistent code style.
Also applies to: 82-84, 126-128, 156-158, 192-194, 225-227, 264-266, 305-307, 348-350, 387-389, 426-428, 473-475, 522-524, 561-563, 592-594, 621-623, 658-660, 695-697, 730-732, 771-773, 812-814, 857-859, 902-904, 935-937, 970-972, 1003-1005, 1040-1042, 1077-1079, 1118-1120, 1173-1175, 1218-1220, 1257-1259, 1294-1296, 1339-1341, 1390-1392, 1427-1429, 1458-1461
🧰 Tools
🪛 Biome (1.9.4)
[error] 51-51: Forbidden non-null assertion.
(lint/style/noNonNullAssertion)
1395-1401
: LGTM!Clear formatting of the expected array with each element on its own line improves readability.
1463-1467
: Helpful documentation addedGood addition of comments to clarify the distinction between raw and cooked values in template literals.
src/handlers/js-ast.ts (5)
1-5
: LGTM!Good reorganization of imports with explicit type imports.
17-22
: LGTM!Good conversion to arrow function syntax for consistency across the codebase.
41-52
: Good improvement in variable handlingUsing a new
newStr
variable instead of modifying the originalstr
parameter is a better practice that avoids potential side effects and makes the code more predictable.
80-86
: Improved function signatureGood improvements:
- Converted to arrow function for consistency
- Refined callback return type from
void | string
toundefined | string
which is more semantically correct
115-127
: Helpful comments added throughoutGood addition of descriptive comments for each AST node type handler, making the code more maintainable and easier to understand.
Also applies to: 137-143, 157-176, 176-190, 190-204, 204-212, 212-218, 218-226, 226-234, 292-310, 310-324, 324-347
README.md (1)
20-27
: LGTM!Clear announcement of version 3 with appropriate warnings about breaking changes and migration guide reference.
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
20-20: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3(MD001, heading-increment)
24-24: Blank line inside blockquote
null(MD028, no-blanks-blockquote)
src/index.ts (5)
1-14
: LGTM!Good improvements:
- Using explicit
node:
prefix for Node.js built-in modules- Separating type imports
- Clear module organization
16-16
: LGTM!Appropriate conversion to async function to handle the new asynchronous CSS obfuscation.
41-54
: Good backward compatibility handlingWell-implemented backward compatibility for the deprecated
classIgnore
option, with a TODO comment for future removal.
72-84
: Good addition of conversion table persistenceThe explicit saving of conversion tables to a JSON file is a good improvement for debugging and analysis purposes.
104-137
: LGTM!Good conversion to async function to properly await the obfuscation process.
src/handlers/html.ts (1)
118-119
: Good improvement: UsingcssUnescape
for proper selector handlingThe use of
cssUnescape
before slicing ensures that escaped CSS selectors are properly handled. This is a necessary improvement for correctness.src/types.ts (1)
1-86
: Well-structured type definitions with clear deprecation noticesThe type definitions have been modernized with:
- Clear deprecation notices for legacy fields
- Proper integration with external CSS processing library types
- Improved structure with nested
prefix
andsuffix
options- Appropriate migration path indicated in deprecation comments
src/handlers/js.ts (1)
13-196
: Clean refactoring to arrow functions with improved code styleThe refactoring successfully:
- Converts functions to arrow functions for consistency
- Maintains all existing functionality
- Improves code by using a local
newContent
variable instead of modifying the input- Preserves proper error handling with AST fallback
src/utils.ts (1)
17-47
: Well-implemented path filtering function!The
shouldIncludePath
function provides a clean, unified approach to path filtering with proper normalization and priority handling (blacklist > whitelist).
- name: Edit First Release Version | ||
run: sed -i 's/\( FIRST_RELEASE\) = "1.0.0"/\1 = "3.0.0"/' node_modules/semantic-release/lib/definitions/constants.js | ||
|
||
- name: Edit First Prerelease Version | ||
run: sed -i 's/\( FIRSTPRERELEASE\) = "1"/\1 = "3"/' node_modules/semantic-release/lib/definitions/constants.js |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider a more robust approach for version configuration.
The sed commands modifying semantic-release internal constants are fragile and could break with package updates. Consider using semantic-release configuration options instead.
A more maintainable approach would be to configure this in release.config.cjs
:
module.exports = {
branches: [
'main',
{ name: 'beta', prerelease: true }
],
plugins: [
'@semantic-release/commit-analyzer',
'@semantic-release/release-notes-generator',
['@semantic-release/npm', {
tarballDir: 'dist'
}],
['@semantic-release/github', {
assets: 'dist/*.tgz'
}]
],
tagFormat: 'v${version}',
preset: 'angular'
};
Then remove the sed commands from the workflow.
🤖 Prompt for AI Agents
In .github/workflows/release.yml around lines 31 to 35, the sed commands
directly modify semantic-release internal constants, which is fragile and prone
to break with updates. Instead, remove these sed commands and configure the
versioning and prerelease behavior properly in a release.config.cjs file by
specifying branches, plugins, tagFormat, and preset options as per
semantic-release documentation. This approach is more maintainable and less
error-prone.
/** | ||
* Experimental feature | ||
*/ | ||
enableJsAst: true, // Whether to obfuscate JS files using abstract syntax tree parser. (Experimental feature) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify stability of enableJsAst feature before enabling by default.
Changing enableJsAst
from false
to true
by default could impact users since it was marked as experimental. Ensure this feature is stable enough for production use.
🏁 Script executed:
#!/bin/bash
# Check for any TODO, FIXME, or experimental comments related to JST AST
echo "Checking for experimental or unstable code in JS AST handling:"
rg -A 5 -B 5 "(TODO|FIXME|experimental|unstable|beta)" src/handlers/js-ast.ts src/handlers/js.ts
# Check test coverage for JS AST functionality
echo "Checking JS AST test coverage:"
fd "js.*test" src/ | head -10
Length of output: 390
Update annotation for enableJsAst
default change
We’ve confirmed there are no lingering “experimental” markers in the implementation and that dedicated tests (js-ast.test.ts
, js.test.ts
) cover this feature. Since enableJsAst
is now on by default, please:
- In src/config.ts (line 60), remove or update the “(Experimental feature)” comment to reflect its production readiness.
- Add a note in your CHANGELOG.md (or README) explaining that
enableJsAst
is now enabled by default.
This will keep the docs in sync with the code and avoid confusing users.
🤖 Prompt for AI Agents
In src/config.ts at line 60, update the comment for enableJsAst by removing or
revising the "(Experimental feature)" note to indicate it is now stable and
enabled by default. Additionally, add a corresponding entry in CHANGELOG.md or
README to document that enableJsAst is enabled by default, ensuring users are
informed of this change.
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: undefined, // The seed for the random generator. "undefined" means use random seed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify the impact of changing generatorSeed default.
The change from "-1"
to undefined
for random seed generation is a breaking change that could affect reproducibility of obfuscated output.
🏁 Script executed:
#!/bin/bash
# Check how generatorSeed is used in the codebase
echo "Searching for generatorSeed usage:"
rg -A 3 -B 3 "generatorSeed" --type ts --type js
# Check if there are any migration notes about this change
echo "Checking for migration documentation:"
fd -e md . | xargs rg -l "generatorSeed\|generator.*seed" || echo "No migration docs found mentioning generatorSeed"
Length of output: 3626
I don’t see a definition for stringToNumber
in the grep results—let’s locate it to confirm how "-1"
was handled versus undefined
. This will show whether -1
triggered a reproducible seed or was treated as “use random,” and what stringToNumber(undefined)
yields.
🏁 Script executed:
#!/bin/bash
echo "Finding stringToNumber definition and usages..."
rg -A5 -B5 "function stringToNumber" --type ts || \
rg -A5 -B5 "stringToNumber" --type ts
Length of output: 1685
Here’s where the handler defaults now inject a random/timestamp seed when generatorSeed
is undefined:
This will confirm the exact lines in src/handlers/css.ts
where the change to undefined
in src/config.ts
becomes a nondeterministic seed.
🏁 Script executed:
#!/bin/bash
echo "Location of Math.random default:"
rg -n "generatorSeed = Math.random" src/handlers/css.ts
echo "Location of Date-based default:"
rg -n "generatorSeed = new Date" src/handlers/css.ts
Length of output: 415
Document a reproducible default or restore the fixed seed
Changing generatorSeed
from the string "-1"
(which yielded a constant seed via stringToNumber("-1")
) to undefined
now falls back to nondeterministic generators in both CSS handlers—one using Math.random()
and the other Date.now()
. This will break any previously reproducible obfuscation outputs.
Please address this by either reinstating a fixed default seed or explicitly documenting the breaking change and providing a migration path.
• src/config.ts:55
- generatorSeed: "-1", // was a constant, reproducible seed
+ generatorSeed: undefined, // now uses Math.random()/Date.now(), non-reproducible
• src/handlers/css.ts:36
generatorSeed = Math.random().toString().slice(2, 10), // random 8-digit seed
• src/handlers/css.ts:151
generatorSeed = new Date().getTime().toString(), // timestamp seed
– Either restore a deterministic default (e.g. "-1"
) or add clear migration/docs warning that omitting generatorSeed
will produce non-reproducible output.
🤖 Prompt for AI Agents
In src/config.ts at line 55, the default value of generatorSeed was changed from
the string "-1" to undefined, causing the seed to become nondeterministic and
breaking reproducibility. To fix this, either revert the default back to the
fixed string "-1" to maintain deterministic output or, if keeping undefined as
default, add clear documentation and migration notes warning users that omitting
generatorSeed now results in non-reproducible obfuscation outputs. Also verify
and update related code in src/handlers/css.ts at lines 36 and 151 to align with
this decision.
describe("findContentBetweenMarker", () => { | ||
it("should return the correct content between markers", () => { | ||
const content = "123{{4}5{67}8}901{2345678}9"; | ||
const targetStr = "5"; | ||
const openSymbol = "{"; | ||
const closeSymbol = "}"; | ||
|
||
const expectedOutput = ["{4}5{67}8", "2345678"]; | ||
|
||
const result = findContentBetweenMarker( | ||
content, | ||
targetStr, | ||
openSymbol, | ||
closeSymbol, | ||
); | ||
expect(result).toEqual(expectedOutput); | ||
}); | ||
|
||
// it('should return the correct content between (if marker length > 1)', () => { | ||
// const content = '[Hello_0 [[Hello_1]]! Hello_2 [[Hello_3]]!]'; | ||
// const targetStr = 'He'; | ||
// const openSymbol = '[['; | ||
// const closeSymbol = ']]'; | ||
|
||
// const expectedOutput = ['Hello_1', 'Hello_3']; | ||
|
||
// const result = findContentBetweenMarker(content, targetStr, openSymbol, closeSymbol); | ||
// expect(result).toEqual(expectedOutput); | ||
// }); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Good test for findContentBetweenMarker but missing edge cases.
The test correctly validates the core functionality, but there are some areas for improvement:
- Commented out test: The commented test for multi-character markers should be uncommented if the function supports it, or removed if not supported.
- Missing edge cases: Consider adding tests for:
- Same open and close markers (should throw error per the function implementation)
- No target string found
- Unmatched markers
- Empty content
+ it("should throw error when open and close markers are the same", () => {
+ expect(() => {
+ findContentBetweenMarker("test", "t", "{", "{");
+ }).toThrow("openMarker and closeMarker can not be the same");
+ });
+
+ it("should return empty array when target string not found", () => {
+ const result = findContentBetweenMarker("abc", "x", "{", "}");
+ expect(result).toEqual([]);
+ });
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
describe("findContentBetweenMarker", () => { | |
it("should return the correct content between markers", () => { | |
const content = "123{{4}5{67}8}901{2345678}9"; | |
const targetStr = "5"; | |
const openSymbol = "{"; | |
const closeSymbol = "}"; | |
const expectedOutput = ["{4}5{67}8", "2345678"]; | |
const result = findContentBetweenMarker( | |
content, | |
targetStr, | |
openSymbol, | |
closeSymbol, | |
); | |
expect(result).toEqual(expectedOutput); | |
}); | |
// it('should return the correct content between (if marker length > 1)', () => { | |
// const content = '[Hello_0 [[Hello_1]]! Hello_2 [[Hello_3]]!]'; | |
// const targetStr = 'He'; | |
// const openSymbol = '[['; | |
// const closeSymbol = ']]'; | |
// const expectedOutput = ['Hello_1', 'Hello_3']; | |
// const result = findContentBetweenMarker(content, targetStr, openSymbol, closeSymbol); | |
// expect(result).toEqual(expectedOutput); | |
// }); | |
}); | |
describe("findContentBetweenMarker", () => { | |
it("should return the correct content between markers", () => { | |
const content = "123{{4}5{67}8}901{2345678}9"; | |
const targetStr = "5"; | |
const openSymbol = "{"; | |
const closeSymbol = "}"; | |
const expectedOutput = ["{4}5{67}8", "2345678"]; | |
const result = findContentBetweenMarker( | |
content, | |
targetStr, | |
openSymbol, | |
closeSymbol, | |
); | |
expect(result).toEqual(expectedOutput); | |
}); | |
// it('should return the correct content between (if marker length > 1)', () => { | |
// const content = '[Hello_0 [[Hello_1]]! Hello_2 [[Hello_3]]!]'; | |
// const targetStr = 'He'; | |
// const openSymbol = '[['; | |
// const closeSymbol = ']]'; | |
// | |
// const expectedOutput = ['Hello_1', 'Hello_3']; | |
// | |
// const result = findContentBetweenMarker(content, targetStr, openSymbol, closeSymbol); | |
// expect(result).toEqual(expectedOutput); | |
// }); | |
it("should throw error when open and close markers are the same", () => { | |
expect(() => { | |
findContentBetweenMarker("test", "t", "{", "{"); | |
}).toThrow("openMarker and closeMarker can not be the same"); | |
}); | |
it("should return empty array when target string not found", () => { | |
const result = findContentBetweenMarker("abc", "x", "{", "}"); | |
expect(result).toEqual([]); | |
}); | |
}); |
🤖 Prompt for AI Agents
In src/__tests__/utils.test.ts between lines 12 and 41, the existing test for
findContentBetweenMarker covers basic functionality but lacks edge case
coverage. Uncomment the test for multi-character markers if the function
supports it, or remove it if not. Add new tests for edge cases including when
open and close markers are the same (expecting an error), when the target string
is not found, when there are unmatched markers in the content, and when the
content is empty. These additions will ensure more robust validation of the
function's behavior.
- Default `generatorSeed` not longer fixed to `-1`, but a random string. | ||
- `simplify-seedable` mode is not longer supported. Use `random` mode instead. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix grammatical errors: "not longer" → "no longer"
Multiple instances of incorrect grammar that should be fixed.
- - Default `generatorSeed` not longer fixed to `-1`, but a random string.
- - `simplify-seedable` mode is not longer supported. Use `random` mode instead.
+ - Default `generatorSeed` no longer fixed to `-1`, but a random string.
+ - `simplify-seedable` mode is no longer supported. Use `random` mode instead.
- - Deprecated `classLength` option, not longer supported.
+ - Deprecated `classLength` option, no longer supported.
- - Not longer preserve TailwindCSS dark mode class names (ie `.dark`). Add the dark mode class name to the `ignorePatterns.selectors` option to preserve it.
+ - No longer preserve TailwindCSS dark mode class names (ie `.dark`). Add the dark mode class name to the `ignorePatterns.selectors` option to preserve it.
Also applies to: 40-40, 42-42
🧰 Tools
🪛 LanguageTool
[grammar] ~37-~37: Did you mean the adverb “no longer”?
Context: ...d by default. - Default generatorSeed
not longer fixed to -1
, but a random string. - `...
(NOT_LONGER)
[grammar] ~38-~38: Did you mean the adverb “no longer”?
Context: ...m string. - simplify-seedable
mode is not longer supported. Use random
mode instead. -...
(NOT_LONGER)
🤖 Prompt for AI Agents
In README.md at lines 37-38, 40, and 42, correct the grammatical error "not
longer" to "no longer" in the sentences describing the default `generatorSeed`
and the `simplify-seedable` mode. Review these lines and replace all instances
of "not longer" with "no longer" to fix the grammar.
refreshClassConversionJson: false, // recommended set to true if not in production | ||
allowExtensions: [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"], | ||
}; | ||
|
||
``` | ||
##### Partially obfuscate | ||
|
||
##### Partially obfuscate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix heading level hierarchy
Another heading level issue that should be fixed for consistency.
- ##### Partially obfuscate
+ #### Partially obfuscate
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
##### Partially obfuscate | |
#### Partially obfuscate |
🤖 Prompt for AI Agents
In README.md at line 183, the heading "Partially obfuscate" has an incorrect
heading level that disrupts the hierarchy. Adjust the markdown heading level to
ensure it follows the previous headings consistently, maintaining proper nesting
and structure.
options.classConversionJsonFolderPath, | ||
"conversion.json", | ||
); | ||
console.log({ jsonPath }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove debug console.log statement
This appears to be debug code that should be removed before production.
- console.log({ jsonPath });
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
console.log({ jsonPath }); |
🤖 Prompt for AI Agents
In src/index.ts at line 77, remove the debug console.log statement that outputs
the jsonPath variable, as it is not needed in production code and should be
cleaned up.
const { obfuscatedContent, usedKeys: _usedKeys } = obfuscateKeys( | ||
selectorConversion, | ||
attribs.class, | ||
[], | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential issue: contentIgnoreRegexes
not applied to HTML class obfuscation
The function receives contentIgnoreRegexes
as a parameter but passes an empty array to obfuscateKeys
when obfuscating HTML class attributes. This means content ignore patterns won't be applied to class names in HTML attributes, which could be inconsistent with how JavaScript content is handled (where contentIgnoreRegexes
is properly passed on line 124).
Consider applying the content ignore patterns consistently:
const { obfuscatedContent, usedKeys: _usedKeys } = obfuscateKeys(
selectorConversion,
attribs.class,
- [],
+ contentIgnoreRegexes,
);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const { obfuscatedContent, usedKeys: _usedKeys } = obfuscateKeys( | |
selectorConversion, | |
attribs.class, | |
[], | |
); | |
const { obfuscatedContent, usedKeys: _usedKeys } = obfuscateKeys( | |
selectorConversion, | |
attribs.class, | |
contentIgnoreRegexes, | |
); |
🤖 Prompt for AI Agents
In src/handlers/html.ts around lines 72 to 76, the call to obfuscateKeys for
HTML class attributes incorrectly passes an empty array instead of the
contentIgnoreRegexes parameter, causing ignore patterns not to be applied. Fix
this by replacing the empty array argument with the contentIgnoreRegexes
parameter to ensure consistent application of ignore patterns during class name
obfuscation.
} else { | ||
return item; | ||
} | ||
}); | ||
} | ||
cssObj.stylesheet.rules = recursive(cssObj.stylesheet.rules).flat(); | ||
return cssObj; | ||
} | ||
|
||
function renameCssSelector(oldSelector: string, newSelector: string, cssObj: any) { | ||
function recursive(rules: any[]): any[] { | ||
return rules.map((item: any) => { | ||
if (item.rules) { | ||
return { ...item, rules: recursive(item.rules) }; | ||
} else if (item.selectors) { | ||
// remove empty selectors | ||
item.selectors = item.selectors.filter((selector: any) => selector !== ""); | ||
|
||
let updatedSelectors = item.selectors.map((selector: any) => | ||
selector === oldSelector ? newSelector : selector | ||
); | ||
|
||
return { ...item, selectors: updatedSelectors }; | ||
} else { | ||
return item; | ||
} | ||
}); | ||
} | ||
|
||
cssObj.stylesheet.rules = recursive(cssObj.stylesheet.rules); | ||
return cssObj; | ||
} | ||
|
||
function obfuscateCss( | ||
selectorConversion: SelectorConversion, | ||
cssPath: string, | ||
replaceOriginalSelector: boolean = false, | ||
isFullObfuscation: boolean = false, | ||
outCssPath?: string, | ||
) { | ||
if (!outCssPath) { | ||
outCssPath = cssPath; | ||
} else if (!fs.existsSync(path.dirname(outCssPath))) { | ||
fs.mkdirSync(path.dirname(outCssPath)); | ||
} | ||
|
||
let cssContent = fs.readFileSync(cssPath, "utf-8"); | ||
|
||
let cssObj = css.parse(cssContent); | ||
const cssRulesCount = cssObj.stylesheet.rules.length; | ||
|
||
if (isFullObfuscation) { | ||
Object.keys(selectorConversion).forEach((key) => { | ||
usedKeyRegistery.add(key); | ||
}); | ||
} else { | ||
// join all selectors start with ":" (eg. ":is") | ||
Object.keys(selectorConversion).forEach((key) => { | ||
if (key.startsWith(":")) { | ||
usedKeyRegistery.add(key); | ||
} | ||
}); | ||
|
||
// join all selectors with action selectors | ||
const actionSelectors = getAllSelector(cssObj).filter((selector) => selector.match(findActionSelectorsRegex)); | ||
actionSelectors.forEach((actionSelector) => { | ||
usedKeyRegistery.add(actionSelector); | ||
}); | ||
|
||
// join all Tailwind CSS [child] selectors (eg. ".\[\&_\.side-box\]\:absolute .side-box") | ||
const tailwindCssChildSelectors = getAllSelector(cssObj).filter((selector) => selector.startsWith(".\\[")); | ||
tailwindCssChildSelectors.forEach((tailwindCssChildSelector) => { | ||
usedKeyRegistery.add(tailwindCssChildSelector); | ||
}); | ||
|
||
// join all child selectors (eg. ">*") | ||
const universalSelectors = getAllSelector(cssObj).filter((selector) => selector.includes(">")); | ||
universalSelectors.forEach((universalSelector) => { | ||
usedKeyRegistery.add(universalSelector); | ||
}); | ||
} | ||
|
||
// modify css rules | ||
usedKeyRegistery.forEach((key) => { | ||
const originalSelectorName = key; | ||
const obfuscatedSelectorName = selectorConversion[key]; | ||
if (obfuscatedSelectorName) { | ||
if (replaceOriginalSelector) { | ||
cssObj = renameCssSelector(originalSelectorName, selectorConversion[key], cssObj); | ||
} else { | ||
cssObj = copyCssData(originalSelectorName, selectorConversion[key], cssObj); | ||
} | ||
} | ||
Object.entries(newConversionTables.idents).forEach(([key, value]) => { | ||
if (!tables.idents[key]) { | ||
// If it doesn't exist, create a new entry | ||
tables.idents[key] = value; | ||
} | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace forEach with for...of loop to properly await async operations.
Using forEach
with an async callback doesn't wait for the promises to resolve. This means the function might return before all CSS files are processed, leading to incomplete conversion tables.
Replace the forEach loop with a for...of loop:
- cssPaths.forEach(async (cssPath) => {
- const { conversionTables: newConversionTables } = await obfuscateCss({
+ for (const cssPath of cssPaths) {
+ const { conversionTables: newConversionTables } = obfuscateCss({
cssPath: cssPath,
conversionTables: conversionTables,
prefix,
suffix,
mode,
ignorePatterns,
generatorSeed,
removeOriginalCss,
});
// Merge the conversion tables
Object.entries(newConversionTables.selectors).forEach(([key, value]) => {
if (!tables.selectors[key]) {
// If it doesn't exist, create a new entry
tables.selectors[key] = value;
}
});
Object.entries(newConversionTables.idents).forEach(([key, value]) => {
if (!tables.idents[key]) {
// If it doesn't exist, create a new entry
tables.idents[key] = value;
}
});
- });
+ }
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/handlers/css.ts between lines 189 and 215, the async operations inside
the forEach loop are not awaited, causing potential incomplete processing of CSS
files. Replace the forEach loop with a for...of loop to properly await each
asynchronous call to obfuscateCss, ensuring all CSS files are processed
sequentially and the conversion tables are fully merged before proceeding.
let transformerMode: TransformProps["mode"] = | ||
mode === "simplify" ? "minimal" : "hash"; | ||
if (!transformerMode) { | ||
// @ts-expect-error - "simplify-seedable" is deprecated but for backward compatibility | ||
if (mode === "simplify-seedable") { | ||
log( | ||
"warn", | ||
"CSS obfuscation", | ||
"The 'simplify-seedable' mode is deprecated, please use 'random' or 'simplify' instead. Now will fall back to 'random' mode.", | ||
); | ||
transformerMode = "hash"; | ||
} | ||
log("error", "CSS obfuscation", `Invalid mode: ${mode}`); | ||
throw new Error(`Invalid mode: ${mode}`); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix logical error in deprecated mode handling.
The code logs a warning about falling back to 'random' mode for the deprecated 'simplify-seedable' mode, but then continues to throw an error. The error should only be thrown for truly invalid modes.
Apply this fix to handle the deprecated mode correctly:
let transformerMode: TransformProps["mode"] =
mode === "simplify" ? "minimal" : "hash";
if (!transformerMode) {
// @ts-expect-error - "simplify-seedable" is deprecated but for backward compatibility
if (mode === "simplify-seedable") {
log(
"warn",
"CSS obfuscation",
"The 'simplify-seedable' mode is deprecated, please use 'random' or 'simplify' instead. Now will fall back to 'random' mode.",
);
transformerMode = "hash";
- }
+ } else {
log("error", "CSS obfuscation", `Invalid mode: ${mode}`);
throw new Error(`Invalid mode: ${mode}`);
+ }
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
let transformerMode: TransformProps["mode"] = | |
mode === "simplify" ? "minimal" : "hash"; | |
if (!transformerMode) { | |
// @ts-expect-error - "simplify-seedable" is deprecated but for backward compatibility | |
if (mode === "simplify-seedable") { | |
log( | |
"warn", | |
"CSS obfuscation", | |
"The 'simplify-seedable' mode is deprecated, please use 'random' or 'simplify' instead. Now will fall back to 'random' mode.", | |
); | |
transformerMode = "hash"; | |
} | |
log("error", "CSS obfuscation", `Invalid mode: ${mode}`); | |
throw new Error(`Invalid mode: ${mode}`); | |
} | |
let transformerMode: TransformProps["mode"] = | |
mode === "simplify" ? "minimal" : "hash"; | |
if (!transformerMode) { | |
// @ts-expect-error - "simplify-seedable" is deprecated but for backward compatibility | |
if (mode === "simplify-seedable") { | |
log( | |
"warn", | |
"CSS obfuscation", | |
"The 'simplify-seedable' mode is deprecated, please use 'random' or 'simplify' instead. Now will fall back to 'random' mode.", | |
); | |
transformerMode = "hash"; | |
} else { | |
log("error", "CSS obfuscation", `Invalid mode: ${mode}`); | |
throw new Error(`Invalid mode: ${mode}`); | |
} | |
} |
🤖 Prompt for AI Agents
In src/handlers/css.ts around lines 60 to 74, the code incorrectly throws an
error after logging a warning for the deprecated 'simplify-seedable' mode, which
should instead be handled as a valid fallback. Modify the logic so that when
mode is 'simplify-seedable', it logs the warning and sets transformerMode to
'hash' without throwing an error. Only throw an error if the mode is neither
valid nor the deprecated 'simplify-seedable'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @soranoo - I've reviewed your changes - here's some feedback:
- There is a lot of commented-out legacy code remaining in the CSS and HTML handlers—please remove unused code blocks to keep the codebase clean and maintainable.
- The
replaceJsonKeysInFiles
utility has grown very large and deeply nested—consider extracting parts of its logic into smaller helper functions or modules to improve readability. - The conversion-table JSON is written both in the CSS handler and again in
index.ts
—you might centralize that responsibility to avoid duplicate file-writing logic.
Here's what I looked at during the review
- 🟡 General issues: 5 issues found
- 🟢 Security: all looks good
- 🟢 Testing: all looks good
- 🟢 Documentation: all looks good
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
// exactMatchRegex = new RegExp(`([\\s"'\\\`]|^)(${keyUse})(?=$|[\\s"'\\\`])`, 'g'); // match exact wording & avoid ` ' "" | ||
|
||
const replacement = `$1` + selectorConversion[key].slice(1).replace(/\\/g, ""); | ||
const replacement = `$1${selectorConversion[key].slice(1).replace(/\\/g, "").slice(1)}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): Extra .slice(1) drops the first character of the replacement value
The second .slice(1) likely removes a needed character from the replacement. Removing it should preserve the intended class name.
idents: {}, | ||
}; | ||
|
||
cssPaths.forEach(async (cssPath) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): Async callback in forEach isn’t awaited
Use a for...of loop or Promise.all to ensure all async operations complete before returning.
Suggested implementation:
for (const cssPath of cssPaths) {
const { conversionTables: newConversionTables } = await obfuscateCss({
cssPath: cssPath,
conversionTables: conversionTables,
prefix,
suffix,
mode,
ignorePatterns,
generatorSeed,
removeOriginalCss,
});
If the containing function is not already async
, you must add the async
keyword to its declaration.
options.classConversionJsonFolderPath, | ||
"conversion.json", | ||
); | ||
console.log({ jsonPath }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: Remove leftover debug log
if (options.classIgnore) { | ||
if (ignorePatterns && !Array.isArray(ignorePatterns)) { | ||
ignorePatterns.selectors?.push(...options.classIgnore); | ||
} else { | ||
ignorePatterns = { | ||
selectors: [...(ignorePatterns || []), ...options.classIgnore], | ||
idents: [...(ignorePatterns || [])], | ||
}; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Deprecated classIgnore is always defined, consider checking its length
Since options.classIgnore defaults to an empty array, check options.classIgnore.length > 0 before merging to avoid unnecessary operations.
if (options.classIgnore) { | |
if (ignorePatterns && !Array.isArray(ignorePatterns)) { | |
ignorePatterns.selectors?.push(...options.classIgnore); | |
} else { | |
ignorePatterns = { | |
selectors: [...(ignorePatterns || []), ...options.classIgnore], | |
idents: [...(ignorePatterns || [])], | |
}; | |
} | |
} | |
if (options.classIgnore && options.classIgnore.length > 0) { | |
if (ignorePatterns && !Array.isArray(ignorePatterns)) { | |
ignorePatterns.selectors?.push(...options.classIgnore); | |
} else { | |
ignorePatterns = { | |
selectors: [...(ignorePatterns || []), ...options.classIgnore], | |
idents: [...(ignorePatterns || [])], | |
}; | |
} | |
} |
* @param blackListedFolderPaths - Paths to exclude (higher priority than whitelist) | ||
* @returns - True if the path should be included, false otherwise | ||
*/ | ||
const shouldIncludePath = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): Whitelist/blacklist matching using includes may match partial segments
Using String.includes may result in unintended matches within folder names. Use path boundaries or regex anchors to match entire folder names only.
Suggested implementation:
// Check if the path is blacklisted (higher priority)
const isBlacklisted = blackListedFolderPaths.some((excludePath) => {
if (typeof excludePath === "string") {
// Match only whole path segments
const pattern = new RegExp(`(^|/)${escapeRegExp(excludePath)}(/|$)`);
return pattern.test(normalizedPath);
}
return excludePath.test(normalizedPath);
// Check if the path is whitelisted (if any whitelist is provided)
if (whiteListedFolderPaths.length > 0) {
const isWhitelisted = whiteListedFolderPaths.some((includePath) => {
if (typeof includePath === "string") {
// Match only whole path segments
const pattern = new RegExp(`(^|/)${escapeRegExp(includePath)}(/|$)`);
return pattern.test(normalizedPath);
}
return includePath.test(normalizedPath);
});
return isWhitelisted;
}
/**
* Escapes special characters for use in a RegExp constructor
*/
function escapeRegExp(str: string): string {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
|
||
// Reconstruct ignore patterns for backward compatibility | ||
// TODO: Remove in the next major version | ||
let ignorePatterns = options.ignorePatterns; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Prefer object destructuring when accessing and using properties. (use-object-destructuring
)
let ignorePatterns = options.ignorePatterns; | |
let {ignorePatterns} = options; |
Explanation
Object destructuring can often remove an unnecessary temporary reference, as well as making your code more succinct.From the Airbnb Javascript Style Guide
const argv = yargs.option("config", { | ||
alias: "c", | ||
type: "string", | ||
description: "Path to the config file" | ||
description: "Path to the config file", | ||
}).argv; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Prefer object destructuring when accessing and using properties. (use-object-destructuring
)
const argv = yargs.option("config", { | |
alias: "c", | |
type: "string", | |
description: "Path to the config file" | |
description: "Path to the config file", | |
}).argv; | |
const {argv} = yargs.option("config", { | |
alias: "c", | |
type: "string", | |
description: "Path to the config file", | |
}); | |
Explanation
Object destructuring can often remove an unnecessary temporary reference, as well as making your code more succinct.From the Airbnb Javascript Style Guide
const isWhitelisted = | ||
whiteListedFolderPaths.length === 0 || | ||
whiteListedFolderPaths.some((includePath) => { | ||
if (typeof includePath === "string") { | ||
return normalizedPath.includes(includePath); | ||
} | ||
return includePath.test(normalizedPath); | ||
}); | ||
|
||
return isWhitelisted; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable
)
const isWhitelisted = | |
whiteListedFolderPaths.length === 0 || | |
whiteListedFolderPaths.some((includePath) => { | |
if (typeof includePath === "string") { | |
return normalizedPath.includes(includePath); | |
} | |
return includePath.test(normalizedPath); | |
}); | |
return isWhitelisted; | |
return whiteListedFolderPaths.length === 0 || | |
whiteListedFolderPaths.some((includePath) => { | |
if (typeof includePath === "string") { | |
return normalizedPath.includes(includePath); | |
} | |
return includePath.test(normalizedPath); | |
}); | |
Explanation
Something that we often see in people's code is assigning to a result variableand then immediately returning it.
Returning the result directly shortens the code and removes an unnecessary
variable, reducing the mental load of reading the function.
Where intermediate variables can be useful is if they then get used as a
parameter or a condition, and the name can act like a comment on what the
variable represents. In the case where you're returning it from a function, the
function name is there to tell you what the result is, so the variable name
is unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (9)
README.md (9)
17-17
: Maintain Heading Hierarchy
The heading### 🎉 Version 3 has NOW been released
jumps from an h1 title without an h2 in between, which violates heading level hierarchy. Consider changing###
to##
to ensure proper nesting.🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
17-17: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3(MD001, heading-increment)
27-29
: Enhance Bullet List Variation
All three bullets start with “Support”. For improved readability, consider rephrasing one or more items, e.g.:
- “Add support for TailwindCSS 4”
- “Introduce nested CSS processing”
- “Enable CSS idents obfuscation”
🧰 Tools
🪛 LanguageTool
[style] ~29-~29: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... TailwindCSS 4. - Support nested CSS. - Support CSS idents obfuscation. #### 📌 Config...(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
34-34
: Fix Grammar: “not longer” → “no longer” and add “the”
Replace with:- Default `generatorSeed` not longer fixed to `-1`, but a random string. + The default `generatorSeed` is no longer fixed to `-1`, but uses a random string.🧰 Tools
🪛 LanguageTool
[uncategorized] ~34-~34: You might be missing the article “the” here.
Context: ...stoption is now enabled by default. - Default
generatorSeednot longer fixed to
-1...(AI_EN_LECTOR_MISSING_DETERMINER_THE)
[grammar] ~34-~34: Did you mean the adverb “no longer”?
Context: ...d by default. - DefaultgeneratorSeed
not longer fixed to-1
, but a random string. - `...(NOT_LONGER)
35-35
: Fix Grammar: “not longer” → “no longer”
Replace with:- `simplify-seedable` mode is not longer supported. Use `random` mode instead. + `simplify-seedable` mode is no longer supported. Use the `random` mode instead.🧰 Tools
🪛 LanguageTool
[grammar] ~35-~35: Did you mean the adverb “no longer”?
Context: ...m string. -simplify-seedable
mode is not longer supported. Userandom
mode instead. -...(NOT_LONGER)
37-37
: Fix Grammar: “not longer” → “no longer”
Replace with:- Deprecated `classLength` option, not longer supported. + Deprecated `classLength` option, no longer supported.🧰 Tools
🪛 LanguageTool
[grammar] ~37-~37: Did you mean the adverb “no longer”?
Context: ...ead. - DeprecatedclassLength
option, not longer supported. - AddedignorePatterns
opt...(NOT_LONGER)
39-39
: Fix Grammar: “Not longer preserve” → “No longer preserve”
Replace with:- Not longer preserve TailwindCSS dark mode class names (ie `.dark`). Add the dark mode... + No longer preserve TailwindCSS dark mode class names (i.e., `.dark`). Add the dark mode...🧰 Tools
🪛 LanguageTool
[grammar] ~39-~39: Did you mean the adverb “no longer”?
Context: ...ts that match the regexes or strings. - Not longer preserve TailwindCSS dark mode class na...(NOT_LONGER)
167-167
: Fix Heading Hierarchy
The heading##### Obfuscate all files
should use####
instead of#####
to maintain a single-level increment under its parent section.🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
167-167: Heading levels should only increment by one level at a time
Expected: h4; Actual: h5(MD001, heading-increment)
180-180
: Fix Heading Hierarchy
The heading##### Partially obfuscate
should be####
to ensure proper nesting and consistency with prior headings.
320-320
: Fix Table Formatting: missing trailing pipe
Add a trailing|
to close the table row:-|removeOriginalCss|boolean|false|Delete original CSS...| +|removeOriginalCss|boolean|false|Delete original CSS... (*NOT recommended* using in partial obfuscation)|🧰 Tools
🪛 LanguageTool
[misspelling] ~320-~320: Use “an” instead of ‘a’ if the following word starts with a vowel sound, e.g. ‘an article’, ‘an hour’.
Context: ...e original CSS from CSS files if it has a obfuscated version. (NOT recommended ...(EN_A_VS_AN)
🪛 markdownlint-cli2 (0.17.2)
320-320: Table pipe style
Expected: leading_and_trailing; Actual: leading_only; Missing trailing pipe(MD055, table-pipe-style)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
demos/next14-app-router/package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (3)
README.md
(15 hunks)demos/next14-app-router/next-css-obfuscator.config.cjs
(1 hunks)demos/next14-app-router/package.json
(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- demos/next14-app-router/package.json
- demos/next14-app-router/next-css-obfuscator.config.cjs
🧰 Additional context used
🪛 LanguageTool
README.md
[style] ~29-~29: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... TailwindCSS 4. - Support nested CSS. - Support CSS idents obfuscation. #### 📌 Config...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
[uncategorized] ~34-~34: You might be missing the article “the” here.
Context: ...stoption is now enabled by default. - Default
generatorSeednot longer fixed to
-1...
(AI_EN_LECTOR_MISSING_DETERMINER_THE)
[grammar] ~34-~34: Did you mean the adverb “no longer”?
Context: ...d by default. - Default generatorSeed
not longer fixed to -1
, but a random string. - `...
(NOT_LONGER)
[grammar] ~35-~35: Did you mean the adverb “no longer”?
Context: ...m string. - simplify-seedable
mode is not longer supported. Use random
mode instead. -...
(NOT_LONGER)
[grammar] ~37-~37: Did you mean the adverb “no longer”?
Context: ...ead. - Deprecated classLength
option, not longer supported. - Added ignorePatterns
opt...
(NOT_LONGER)
[grammar] ~39-~39: Did you mean the adverb “no longer”?
Context: ...ts that match the regexes or strings. - Not longer preserve TailwindCSS dark mode class na...
(NOT_LONGER)
[misspelling] ~53-~53: Use “an” instead of ‘a’ if the following word starts with a vowel sound, e.g. ‘an article’, ‘an hour’.
Context: ...More configuration options - Now become a independent solution (no need to patch ...
(EN_A_VS_AN)
[uncategorized] ~201-~201: The preposition “to” seems more likely in this position.
Context: ...ver work as expected, tweak the options with your own needs. 2. Add the following c...
(AI_EN_LECTOR_REPLACEMENT_PREPOSITION)
[misspelling] ~320-~320: Use “an” instead of ‘a’ if the following word starts with a vowel sound, e.g. ‘an article’, ‘an hour’.
Context: ...e original CSS from CSS files if it has a obfuscated version. (NOT recommended ...
(EN_A_VS_AN)
[uncategorized] ~328-~328: You might be missing the article “an” here.
Context: ...rue|Whether to obfuscate JS files using abstract syntax tree parser.
`contentIgn...
(AI_EN_LECTOR_MISSING_DETERMINER_AN)
🪛 markdownlint-cli2 (0.17.2)
README.md
17-17: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3
(MD001, heading-increment)
21-21: Blank line inside blockquote
null
(MD028, no-blanks-blockquote)
167-167: Heading levels should only increment by one level at a time
Expected: h4; Actual: h5
(MD001, heading-increment)
185-185: Code block style
Expected: fenced; Actual: indented
(MD046, code-block-style)
315-315: No empty links
null
(MD042, no-empty-links)
316-316: No empty links
null
(MD042, no-empty-links)
320-320: Table pipe style
Expected: leading_and_trailing; Actual: leading_only; Missing trailing pipe
(MD055, table-pipe-style)
🔇 Additional comments (20)
README.md (20)
15-16
: Formatting Update: Section Separator
The horizontal rule has been updated to visually separate the v3 announcement.
19-20
: Approve Blockquote Notices
The[!IMPORTANT]
callout clearly highlights the breaking changes and essential migration guide link.
22-23
: Approve Upgrade Tip
The[!TIP]
callout provides a succinct reminder to avoid unnecessary upgrades.
25-25
: Approve Feature Changes Heading
This new section clearly outlines the primary feature updates introduced in v3.0.
31-31
: Approve Configuration Changes Heading
This heading appropriately introduces the list of breaking configuration updates.
33-33
: Approve Default JS AST Option
EnablingenableJsAst
by default aligns documentation with the updated code behavior.
36-36
: Approve Deprecation of Regex Options
Switching towhiteListedFolderPaths
andblackListedFolderPaths
consolidates and clarifies path filtering options.
38-38
: Approve Addition of ignorePatterns Option
IntroducingignorePatterns
provides a flexible way to exclude selectors and idents.
40-40
: Approve Merging classIgnore
MergingclassIgnore
intoignorePatterns.selectors
simplifies and unifies the API.
41-41
: Approve Renaming Prefix/Suffix Options
RenamingclassPrefix
/classSuffix
toprefix
/suffix
improves consistency and clarity.
173-173
: Approve Mode Example
The code example correctly reflects the supportedrandom
andsimplify
modes consistent with v3 documentation.
183-184
: Approve Caution Callout
The[!CAUTION]
callout effectively warns users about partial obfuscation instability.
189-190
: Approve Mode Clarification
The example remains consistent with the two supported modes after deprecatingsimplify-seedable
.
201-202
: Approve Partial Obfuscation Tip
The suggestion to tweak options for individual needs is clear and useful.🧰 Tools
🪛 LanguageTool
[uncategorized] ~201-~201: The preposition “to” seems more likely in this position.
Context: ...ver work as expected, tweak the options with your own needs. 2. Add the following c...(AI_EN_LECTOR_REPLACEMENT_PREPOSITION)
273-273
: Skip: Informal Section Intro
This line is purely narrative (“If you are interested in my setting…”). No action needed.
281-281
: Skip: Example Configuration Snippet
This code example aligns with the documentation and requires no changes.
308-310
: Approve Table Updates for Prefix/Suffix
The table now accurately documents the new nestedprefix
andsuffix
options for selectors and idents.
315-316
: Approve Whitelist/Blacklist Path Options
The addition ofwhiteListedFolderPaths
andblackListedFolderPaths
entries aligns with the updated filter API.🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
315-315: No empty links
null(MD042, no-empty-links)
316-316: No empty links
null(MD042, no-empty-links)
321-321
: ApprovegeneratorSeed
Table Entry
The documentation now clearly states that an undefinedgeneratorSeed
produces a random seed.
328-328
: ApproveenableJsAst
Default
Marking this experimental feature as enabled by default accurately reflects the updated code behavior.🧰 Tools
🪛 LanguageTool
[uncategorized] ~328-~328: You might be missing the article “an” here.
Context: ...rue|Whether to obfuscate JS files using abstract syntax tree parser.
`contentIgn...(AI_EN_LECTOR_MISSING_DETERMINER_AN)
native CSS nesting
#20Summary by Sourcery
Upgrade to version 3.0.0 with a complete overhaul of the CSS transformation engine, adding nested CSS and TailwindCSS 4 support and refining the configuration API.
New Features:
Enhancements:
Build:
CI:
Documentation:
Tests:
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Documentation
Chores
Refactor
Tests