diff --git a/.gitignore b/.gitignore
index 7864794..cd445dd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
node_modules
coverage
.vscode
+.idea
.env
.dccache
-dist
\ No newline at end of file
diff --git a/.tool-versions b/.tool-versions
new file mode 100644
index 0000000..9c08e2b
--- /dev/null
+++ b/.tool-versions
@@ -0,0 +1 @@
+nodejs 20.12.0
diff --git a/dist/config.d.ts b/dist/config.d.ts
new file mode 100644
index 0000000..6302811
--- /dev/null
+++ b/dist/config.d.ts
@@ -0,0 +1,7 @@
+import { type Options, type OptionalOptions } from "./types";
+declare class Config {
+ private options;
+ constructor(options?: OptionalOptions);
+ get(): Options;
+}
+export default Config;
diff --git a/dist/config.js b/dist/config.js
new file mode 100644
index 0000000..4da935e
--- /dev/null
+++ b/dist/config.js
@@ -0,0 +1,39 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const defaultOptions = {
+ enable: true,
+ mode: "random",
+ buildFolderPath: ".next",
+ classConversionJsonFolderPath: "./css-obfuscator",
+ refreshClassConversionJson: false,
+ classLength: 5,
+ classPrefix: "",
+ classSuffix: "",
+ classIgnore: [],
+ allowExtensions: [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"],
+ contentIgnoreRegexes: [
+ /\.jsxs\)\("\w+"/g,
+ ],
+ whiteListedFolderPaths: [],
+ blackListedFolderPaths: ["./.next/cache"],
+ enableMarkers: false,
+ markers: ["next-css-obfuscation"],
+ removeMarkersAfterObfuscated: true,
+ removeOriginalCss: false,
+ generatorSeed: "-1",
+ enableJsAst: false,
+ logLevel: "info",
+};
+class Config {
+ constructor(options) {
+ if (!options) {
+ this.options = defaultOptions;
+ return;
+ }
+ this.options = Object.assign(Object.assign({}, defaultOptions), options);
+ }
+ get() {
+ return this.options;
+ }
+}
+exports.default = Config;
diff --git a/dist/handlers/css.d.ts b/dist/handlers/css.d.ts
new file mode 100644
index 0000000..5aa3027
--- /dev/null
+++ b/dist/handlers/css.d.ts
@@ -0,0 +1,20 @@
+import { obfuscateMode, SelectorConversion } from "../types";
+declare function extractClassFromSelector(selector: string, replacementClassNames?: (string | undefined)[]): {
+ selector: string;
+ extractedClasses: string[];
+};
+declare function createSelectorConversionJson({ selectorConversionJsonFolderPath, buildFolderPath, mode, classNameLength, classPrefix, classSuffix, classIgnore, enableObfuscateMarkerClasses, generatorSeed, }: {
+ selectorConversionJsonFolderPath: string;
+ buildFolderPath: string;
+ mode?: obfuscateMode;
+ classNameLength?: number;
+ classPrefix?: string;
+ classSuffix?: string;
+ classIgnore?: (string | RegExp)[];
+ enableObfuscateMarkerClasses?: boolean;
+ generatorSeed?: string;
+}): void;
+declare function copyCssData(targetSelector: string, newSelectorName: string, cssObj: any): any;
+declare function renameCssSelector(oldSelector: string, newSelector: string, cssObj: any): any;
+declare function obfuscateCss(selectorConversion: SelectorConversion, cssPath: string, replaceOriginalSelector?: boolean, isFullObfuscation?: boolean, outCssPath?: string): void;
+export { copyCssData, renameCssSelector, createSelectorConversionJson, obfuscateCss, extractClassFromSelector, };
diff --git a/dist/handlers/css.js b/dist/handlers/css.js
new file mode 100644
index 0000000..dd4259d
--- /dev/null
+++ b/dist/handlers/css.js
@@ -0,0 +1,277 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.extractClassFromSelector = exports.obfuscateCss = exports.createSelectorConversionJson = exports.renameCssSelector = exports.copyCssData = void 0;
+const tslib_1 = require("tslib");
+const path_1 = tslib_1.__importDefault(require("path"));
+const fs_1 = tslib_1.__importDefault(require("fs"));
+const css_1 = tslib_1.__importDefault(require("css"));
+const recoverable_random_1 = tslib_1.__importDefault(require("recoverable-random"));
+const utils_1 = require("../utils");
+let randomStringGeneraterStateCode = undefined;
+let currentAlphabetPoistion = 1;
+function createNewClassName(mode, className, classPrefix = "", classSuffix = "", classNameLength = 5, seed = Math.random().toString()) {
+ let newClassName = className;
+ let { rngStateCode, randomString } = { rngStateCode: "", randomString: "" };
+ switch (mode) {
+ case "random":
+ ({ rngStateCode, randomString } = (0, utils_1.getRandomString)(classNameLength, seed, undefined, className));
+ break;
+ case "simplify-seedable":
+ ({ rngStateCode, randomString } = (0, utils_1.seedableSimplifyString)(className, seed, seed + recoverable_random_1.default.stringToSeed(className).toString()));
+ break;
+ case "simplify":
+ randomString = (0, utils_1.simplifyString)(currentAlphabetPoistion);
+ currentAlphabetPoistion++;
+ break;
+ default:
+ break;
+ }
+ newClassName = randomString;
+ randomStringGeneraterStateCode = rngStateCode;
+ if (classPrefix) {
+ newClassName = `${classPrefix}${newClassName}`;
+ }
+ if (classSuffix) {
+ newClassName = `${newClassName}${classSuffix}`;
+ }
+ return newClassName;
+}
+const findActionSelectorsRegex = /(?)/g;
+function extractClassFromSelector(selector, replacementClassNames) {
+ const extractClassRegex = /(?<=[.:!]|(?\/]|(?:\\\[(?:[^\[\]\s])*\\\]))+)(?![\w\-]*\()/g;
+ const vendorPseudoClassRegexes = [
+ /::?-moz-[\w-]+/g,
+ /::?-ms-[\w-]+/g,
+ /::?-webkit-[\w-]+/g,
+ /::?-o-[\w-]+/g,
+ ];
+ selector = selector.replace(findActionSelectorsRegex, (match) => {
+ return (0, utils_1.createKey)(match);
+ });
+ vendorPseudoClassRegexes.forEach((regex, i) => {
+ selector = selector.replace(regex, (match) => {
+ return (0, utils_1.createKey)(match);
+ });
+ });
+ let classes = selector.match(extractClassRegex);
+ if (replacementClassNames !== undefined) {
+ selector = selector.replace(extractClassRegex, (originalClassName) => {
+ return replacementClassNames.shift() || originalClassName;
+ });
+ }
+ selector = (0, utils_1.decodeKey)(selector);
+ return {
+ selector: selector,
+ extractedClasses: classes || []
+ };
+}
+exports.extractClassFromSelector = extractClassFromSelector;
+function getAllSelector(cssObj) {
+ const selectors = [];
+ function recursive(rules) {
+ for (const item of rules) {
+ if (item.rules) {
+ recursive(item.rules);
+ }
+ else if (item.selectors) {
+ item.selectors = item.selectors.filter((selector) => selector !== "");
+ selectors.push(...item.selectors);
+ }
+ }
+ return null;
+ }
+ recursive(cssObj.stylesheet.rules);
+ return selectors;
+}
+function createSelectorConversionJson({ selectorConversionJsonFolderPath, buildFolderPath, mode = "random", classNameLength = 5, classPrefix = "", classSuffix = "", classIgnore = [], enableObfuscateMarkerClasses = false, generatorSeed = Math.random().toString().slice(2, 10), }) {
+ if (!fs_1.default.existsSync(selectorConversionJsonFolderPath)) {
+ fs_1.default.mkdirSync(selectorConversionJsonFolderPath);
+ }
+ const selectorConversion = (0, utils_1.loadAndMergeJsonFiles)(selectorConversionJsonFolderPath);
+ if (enableObfuscateMarkerClasses) {
+ selectorConversion[".dark"] = ".dark";
+ }
+ const cssPaths = (0, utils_1.findAllFilesWithExt)(".css", buildFolderPath);
+ const selectors = [];
+ cssPaths.forEach((cssPath) => {
+ const cssContent = fs_1.default.readFileSync(cssPath, "utf-8");
+ const cssObj = css_1.default.parse(cssContent);
+ selectors.push(...getAllSelector(cssObj));
+ });
+ const uniqueSelectors = [...new Set(selectors)];
+ const allowClassStartWith = [".", "#", ":is(", ":where(", ":not(",
+ ":matches(", ":nth-child(", ":nth-last-child(",
+ ":nth-of-type(", ":nth-last-of-type(", ":first-child(",
+ ":last-child(", ":first-of-type(", ":last-of-type(",
+ ":only-child(", ":only-of-type(", ":empty(", ":link(",
+ ":visited(", ":active(", ":hover(", ":focus(", ":target(",
+ ":lang(", ":enabled(", ":disabled(", ":checked(", ":default(",
+ ":indeterminate(", ":root(", ":before(",
+ ":after(", ":first-letter(", ":first-line(", ":selection(",
+ ":read-only(", ":read-write(", ":fullscreen(", ":optional(",
+ ":required(", ":valid(", ":invalid(", ":in-range(", ":out-of-range(",
+ ":placeholder-shown("
+ ];
+ const selectorClassPair = {};
+ for (let i = 0; i < uniqueSelectors.length; i++) {
+ const originalSelector = uniqueSelectors[i];
+ const { extractedClasses } = extractClassFromSelector(originalSelector) || [];
+ selectorClassPair[originalSelector] = extractedClasses;
+ }
+ const sortedSelectorClassPair = Object.entries(selectorClassPair)
+ .sort((a, b) => a[1].length - b[1].length)
+ .filter((pair) => pair[1].length > 0);
+ for (let i = 0; i < sortedSelectorClassPair.length; i++) {
+ const [originalSelector, selectorClasses] = sortedSelectorClassPair[i];
+ if (selectorClasses.length == 0) {
+ continue;
+ }
+ let selector = originalSelector;
+ let classes = selectorClasses;
+ if (classes && allowClassStartWith.some((start) => selector.startsWith(start))) {
+ classes = classes.map((className) => {
+ if (classIgnore.some(regex => {
+ if (typeof regex === "string") {
+ return className === regex;
+ }
+ return new RegExp(regex).test(className);
+ })) {
+ return className;
+ }
+ let obfuscatedSelector = selectorConversion[`.${className}`];
+ if (!obfuscatedSelector) {
+ const obfuscatedClass = createNewClassName(mode, className, classPrefix, classSuffix, classNameLength, generatorSeed);
+ obfuscatedSelector = `.${obfuscatedClass}`;
+ selectorConversion[`.${className}`] = obfuscatedSelector;
+ }
+ return obfuscatedSelector.slice(1);
+ });
+ const { selector: obfuscatedSelector } = extractClassFromSelector(originalSelector, classes);
+ selectorConversion[originalSelector] = obfuscatedSelector;
+ }
+ }
+ const jsonPath = path_1.default.join(process.cwd(), selectorConversionJsonFolderPath, "conversion.json");
+ fs_1.default.writeFileSync(jsonPath, JSON.stringify(selectorConversion, null, 2));
+ if ((0, utils_1.duplicationCheck)(Object.keys(selectorConversion))) {
+ if (mode == "random") {
+ (0, utils_1.log)("error", "Obfuscation", "Duplicated class names found in the conversion JSON, try to increase the class name length / open an issue on GitHub https://github.com/soranoo/next-css-obfuscator/issues");
+ }
+ else {
+ (0, utils_1.log)("error", "Obfuscation", "Duplicated class names found in the conversion JSON, please open an issue on GitHub https://github.com/soranoo/next-css-obfuscator/issues");
+ }
+ }
+}
+exports.createSelectorConversionJson = createSelectorConversionJson;
+function copyCssData(targetSelector, newSelectorName, cssObj) {
+ function recursive(rules) {
+ return rules.map((item) => {
+ if (item.rules) {
+ let newRules = recursive(item.rules);
+ if (Array.isArray(newRules)) {
+ newRules = newRules.flat();
+ }
+ return Object.assign(Object.assign({}, item), { rules: newRules });
+ }
+ else if (item.selectors) {
+ item.selectors = item.selectors.filter((selector) => selector !== "");
+ if (item.selectors.includes(targetSelector)) {
+ const newRule = JSON.parse(JSON.stringify(item));
+ newRule.selectors = [newSelectorName];
+ return [item, newRule];
+ }
+ else {
+ return item;
+ }
+ }
+ else {
+ return item;
+ }
+ });
+ }
+ cssObj.stylesheet.rules = recursive(cssObj.stylesheet.rules).flat();
+ return cssObj;
+}
+exports.copyCssData = copyCssData;
+function renameCssSelector(oldSelector, newSelector, cssObj) {
+ function recursive(rules) {
+ return rules.map((item) => {
+ if (item.rules) {
+ return Object.assign(Object.assign({}, item), { rules: recursive(item.rules) });
+ }
+ else if (item.selectors) {
+ item.selectors = item.selectors.filter((selector) => selector !== "");
+ let updatedSelectors = item.selectors.map((selector) => selector === oldSelector ? newSelector : selector);
+ return Object.assign(Object.assign({}, item), { selectors: updatedSelectors });
+ }
+ else {
+ return item;
+ }
+ });
+ }
+ cssObj.stylesheet.rules = recursive(cssObj.stylesheet.rules);
+ return cssObj;
+}
+exports.renameCssSelector = renameCssSelector;
+function obfuscateCss(selectorConversion, cssPath, replaceOriginalSelector = false, isFullObfuscation = false, outCssPath) {
+ if (!outCssPath) {
+ outCssPath = cssPath;
+ }
+ else if (!fs_1.default.existsSync(path_1.default.dirname(outCssPath))) {
+ fs_1.default.mkdirSync(path_1.default.dirname(outCssPath));
+ }
+ let cssContent = fs_1.default.readFileSync(cssPath, "utf-8");
+ let cssObj = css_1.default.parse(cssContent);
+ const cssRulesCount = cssObj.stylesheet.rules.length;
+ if (isFullObfuscation) {
+ Object.keys(selectorConversion).forEach((key) => {
+ utils_1.usedKeyRegistery.add(key);
+ });
+ }
+ else {
+ Object.keys(selectorConversion).forEach((key) => {
+ if (key.startsWith(":")) {
+ utils_1.usedKeyRegistery.add(key);
+ }
+ });
+ const actionSelectors = getAllSelector(cssObj).filter((selector) => selector.match(findActionSelectorsRegex));
+ actionSelectors.forEach((actionSelector) => {
+ utils_1.usedKeyRegistery.add(actionSelector);
+ });
+ const tailwindCssChildSelectors = getAllSelector(cssObj).filter((selector) => selector.startsWith(".\\["));
+ tailwindCssChildSelectors.forEach((tailwindCssChildSelector) => {
+ utils_1.usedKeyRegistery.add(tailwindCssChildSelector);
+ });
+ const universalSelectors = getAllSelector(cssObj).filter((selector) => selector.includes(">"));
+ universalSelectors.forEach((universalSelector) => {
+ utils_1.usedKeyRegistery.add(universalSelector);
+ });
+ }
+ utils_1.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);
+ }
+ }
+ });
+ if (replaceOriginalSelector) {
+ (0, utils_1.log)("info", "CSS rules:", `Modified ${utils_1.usedKeyRegistery.size} CSS rules to ${(0, utils_1.getFilenameFromPath)(cssPath)}`);
+ }
+ else {
+ (0, utils_1.log)("info", "CSS rules:", `Added ${cssObj.stylesheet.rules.length - cssRulesCount} new CSS rules to ${(0, utils_1.getFilenameFromPath)(cssPath)}`);
+ }
+ const cssOptions = {
+ compress: true,
+ };
+ const cssObfuscatedContent = css_1.default.stringify(cssObj, cssOptions);
+ const sizeBefore = Buffer.byteLength(cssContent, "utf8");
+ fs_1.default.writeFileSync(outCssPath, cssObfuscatedContent);
+ const sizeAfter = Buffer.byteLength(cssObfuscatedContent, "utf8");
+ const percentChange = Math.round(((sizeAfter) / sizeBefore) * 100);
+ (0, utils_1.log)("success", "CSS obfuscated:", `Size from ${sizeBefore} to ${sizeAfter} bytes (${percentChange}%) in ${(0, utils_1.getFilenameFromPath)(cssPath)}`);
+}
+exports.obfuscateCss = obfuscateCss;
diff --git a/dist/handlers/css.test.d.ts b/dist/handlers/css.test.d.ts
new file mode 100644
index 0000000..cb0ff5c
--- /dev/null
+++ b/dist/handlers/css.test.d.ts
@@ -0,0 +1 @@
+export {};
diff --git a/dist/handlers/css.test.js b/dist/handlers/css.test.js
new file mode 100644
index 0000000..6b6d972
--- /dev/null
+++ b/dist/handlers/css.test.js
@@ -0,0 +1,305 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const tslib_1 = require("tslib");
+const vitest_1 = require("vitest");
+const css_1 = tslib_1.__importDefault(require("css"));
+const css_2 = require("./css");
+const testCss = `
+.s0-1 {
+ background: #181810;
+ color: #181811;
+}
+
+@media (min-width: 640px)
+{
+ .s1-1
+ {
+ background: #181812;
+ color: #181813;
+ }
+
+ @media (min-width: 768px)
+ {
+ .s2-1, .s2-1-1 {
+ background: #181814;
+ color: #181815;
+ },
+ .s2-1, .s2-1-1 {
+ background: #181814;
+ color: #181815;
+ },
+ .s2-2, .s2-2-2 {
+ background: #181816;
+ color: #181817;
+ },
+ .s2-3 {
+ background: #181818;
+ color: #181819;
+ }
+ }
+
+ .s1-2
+ {
+ background: #181820;
+ color: #181821;
+ }
+}
+
+.s0-2 {
+ background: #181822;
+ color: #181823;
+}
+`;
+(0, vitest_1.describe)("renameCssSelector", () => {
+ (0, vitest_1.it)("should rename the CSS selector (single selector, no nested rule)", () => {
+ const cssObj = css_1.default.parse(testCss);
+ const oldSelector = ".s1-1";
+ const newSelector = ".s1-1-new";
+ const expectedOutput = [{ "type": "rule", "selectors": [".s0-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181810", "position": { "start": { "line": 3, "column": 5 }, "end": { "line": 3, "column": 24 } } }, { "type": "declaration", "property": "color", "value": "#181811", "position": { "start": { "line": 4, "column": 5 }, "end": { "line": 4, "column": 19 } } }], "position": { "start": { "line": 2, "column": 1 }, "end": { "line": 5, "column": 2 } } }, { "type": "media", "media": "(min-width: 640px)", "rules": [{ "type": "rule", "selectors": [".s1-1-new"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181812", "position": { "start": { "line": 11, "column": 9 }, "end": { "line": 11, "column": 28 } } }, { "type": "declaration", "property": "color", "value": "#181813", "position": { "start": { "line": 12, "column": 9 }, "end": { "line": 12, "column": 23 } } }], "position": { "start": { "line": 9, "column": 5 }, "end": { "line": 13, "column": 6 } } }, { "type": "media", "media": "(min-width: 768px)", "rules": [{ "type": "rule", "selectors": [".s2-1", ".s2-1-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181814", "position": { "start": { "line": 18, "column": 13 }, "end": { "line": 18, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181815", "position": { "start": { "line": 19, "column": 13 }, "end": { "line": 19, "column": 27 } } }], "position": { "start": { "line": 17, "column": 9 }, "end": { "line": 20, "column": 10 } } }, { "type": "rule", "selectors": [".s2-1", ".s2-1-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181814", "position": { "start": { "line": 22, "column": 13 }, "end": { "line": 22, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181815", "position": { "start": { "line": 23, "column": 13 }, "end": { "line": 23, "column": 27 } } }], "position": { "start": { "line": 20, "column": 10 }, "end": { "line": 24, "column": 10 } } }, { "type": "rule", "selectors": [".s2-2", ".s2-2-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181816", "position": { "start": { "line": 26, "column": 13 }, "end": { "line": 26, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181817", "position": { "start": { "line": 27, "column": 13 }, "end": { "line": 27, "column": 27 } } }], "position": { "start": { "line": 24, "column": 10 }, "end": { "line": 28, "column": 10 } } }, { "type": "rule", "selectors": [".s2-3"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181818", "position": { "start": { "line": 30, "column": 13 }, "end": { "line": 30, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181819", "position": { "start": { "line": 31, "column": 13 }, "end": { "line": 31, "column": 27 } } }], "position": { "start": { "line": 28, "column": 10 }, "end": { "line": 32, "column": 10 } } }], "position": { "start": { "line": 15, "column": 5 }, "end": { "line": 33, "column": 6 } } }, { "type": "rule", "selectors": [".s1-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181820", "position": { "start": { "line": 37, "column": 9 }, "end": { "line": 37, "column": 28 } } }, { "type": "declaration", "property": "color", "value": "#181821", "position": { "start": { "line": 38, "column": 9 }, "end": { "line": 38, "column": 23 } } }], "position": { "start": { "line": 35, "column": 5 }, "end": { "line": 39, "column": 6 } } }], "position": { "start": { "line": 7, "column": 1 }, "end": { "line": 40, "column": 2 } } }, { "type": "rule", "selectors": [".s0-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181822", "position": { "start": { "line": 43, "column": 5 }, "end": { "line": 43, "column": 24 } } }, { "type": "declaration", "property": "color", "value": "#181823", "position": { "start": { "line": 44, "column": 5 }, "end": { "line": 44, "column": 19 } } }], "position": { "start": { "line": 42, "column": 1 }, "end": { "line": 45, "column": 2 } } }];
+ const result = (0, css_2.renameCssSelector)(oldSelector, newSelector, cssObj);
+ (0, vitest_1.expect)(result.stylesheet.rules).toEqual(expectedOutput);
+ });
+ (0, vitest_1.it)("should rename the CSS selector (multiple nested media queries)", () => {
+ const cssObj = css_1.default.parse(testCss);
+ const oldSelector = ".s2-2";
+ const newSelector = ".s2-2-new";
+ const expectedOutput = [{ "type": "rule", "selectors": [".s0-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181810", "position": { "start": { "line": 3, "column": 5 }, "end": { "line": 3, "column": 24 } } }, { "type": "declaration", "property": "color", "value": "#181811", "position": { "start": { "line": 4, "column": 5 }, "end": { "line": 4, "column": 19 } } }], "position": { "start": { "line": 2, "column": 1 }, "end": { "line": 5, "column": 2 } } }, { "type": "media", "media": "(min-width: 640px)", "rules": [{ "type": "rule", "selectors": [".s1-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181812", "position": { "start": { "line": 11, "column": 9 }, "end": { "line": 11, "column": 28 } } }, { "type": "declaration", "property": "color", "value": "#181813", "position": { "start": { "line": 12, "column": 9 }, "end": { "line": 12, "column": 23 } } }], "position": { "start": { "line": 9, "column": 5 }, "end": { "line": 13, "column": 6 } } }, { "type": "media", "media": "(min-width: 768px)", "rules": [{ "type": "rule", "selectors": [".s2-1", ".s2-1-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181814", "position": { "start": { "line": 18, "column": 13 }, "end": { "line": 18, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181815", "position": { "start": { "line": 19, "column": 13 }, "end": { "line": 19, "column": 27 } } }], "position": { "start": { "line": 17, "column": 9 }, "end": { "line": 20, "column": 10 } } }, { "type": "rule", "selectors": [".s2-1", ".s2-1-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181814", "position": { "start": { "line": 22, "column": 13 }, "end": { "line": 22, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181815", "position": { "start": { "line": 23, "column": 13 }, "end": { "line": 23, "column": 27 } } }], "position": { "start": { "line": 20, "column": 10 }, "end": { "line": 24, "column": 10 } } }, { "type": "rule", "selectors": [".s2-2-new", ".s2-2-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181816", "position": { "start": { "line": 26, "column": 13 }, "end": { "line": 26, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181817", "position": { "start": { "line": 27, "column": 13 }, "end": { "line": 27, "column": 27 } } }], "position": { "start": { "line": 24, "column": 10 }, "end": { "line": 28, "column": 10 } } }, { "type": "rule", "selectors": [".s2-3"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181818", "position": { "start": { "line": 30, "column": 13 }, "end": { "line": 30, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181819", "position": { "start": { "line": 31, "column": 13 }, "end": { "line": 31, "column": 27 } } }], "position": { "start": { "line": 28, "column": 10 }, "end": { "line": 32, "column": 10 } } }], "position": { "start": { "line": 15, "column": 5 }, "end": { "line": 33, "column": 6 } } }, { "type": "rule", "selectors": [".s1-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181820", "position": { "start": { "line": 37, "column": 9 }, "end": { "line": 37, "column": 28 } } }, { "type": "declaration", "property": "color", "value": "#181821", "position": { "start": { "line": 38, "column": 9 }, "end": { "line": 38, "column": 23 } } }], "position": { "start": { "line": 35, "column": 5 }, "end": { "line": 39, "column": 6 } } }], "position": { "start": { "line": 7, "column": 1 }, "end": { "line": 40, "column": 2 } } }, { "type": "rule", "selectors": [".s0-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181822", "position": { "start": { "line": 43, "column": 5 }, "end": { "line": 43, "column": 24 } } }, { "type": "declaration", "property": "color", "value": "#181823", "position": { "start": { "line": 44, "column": 5 }, "end": { "line": 44, "column": 19 } } }], "position": { "start": { "line": 42, "column": 1 }, "end": { "line": 45, "column": 2 } } }];
+ const result = (0, css_2.renameCssSelector)(oldSelector, newSelector, cssObj);
+ (0, vitest_1.expect)(result.stylesheet.rules).toEqual(expectedOutput);
+ });
+});
+(0, vitest_1.describe)("copyCssData", () => {
+ (0, vitest_1.it)("should copy the CSS data (single selector, no nested rule)", () => {
+ const cssObj = css_1.default.parse(testCss);
+ const targetSelector = ".s0-2";
+ const newSelectorName = ".s0-2-new";
+ const expectedOutput = [{ "type": "rule", "selectors": [".s0-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181810", "position": { "start": { "line": 3, "column": 5 }, "end": { "line": 3, "column": 24 } } }, { "type": "declaration", "property": "color", "value": "#181811", "position": { "start": { "line": 4, "column": 5 }, "end": { "line": 4, "column": 19 } } }], "position": { "start": { "line": 2, "column": 1 }, "end": { "line": 5, "column": 2 } } }, { "type": "media", "media": "(min-width: 640px)", "rules": [{ "type": "rule", "selectors": [".s1-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181812", "position": { "start": { "line": 11, "column": 9 }, "end": { "line": 11, "column": 28 } } }, { "type": "declaration", "property": "color", "value": "#181813", "position": { "start": { "line": 12, "column": 9 }, "end": { "line": 12, "column": 23 } } }], "position": { "start": { "line": 9, "column": 5 }, "end": { "line": 13, "column": 6 } } }, { "type": "media", "media": "(min-width: 768px)", "rules": [{ "type": "rule", "selectors": [".s2-1", ".s2-1-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181814", "position": { "start": { "line": 18, "column": 13 }, "end": { "line": 18, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181815", "position": { "start": { "line": 19, "column": 13 }, "end": { "line": 19, "column": 27 } } }], "position": { "start": { "line": 17, "column": 9 }, "end": { "line": 20, "column": 10 } } }, { "type": "rule", "selectors": [".s2-1", ".s2-1-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181814", "position": { "start": { "line": 22, "column": 13 }, "end": { "line": 22, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181815", "position": { "start": { "line": 23, "column": 13 }, "end": { "line": 23, "column": 27 } } }], "position": { "start": { "line": 20, "column": 10 }, "end": { "line": 24, "column": 10 } } }, { "type": "rule", "selectors": [".s2-2", ".s2-2-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181816", "position": { "start": { "line": 26, "column": 13 }, "end": { "line": 26, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181817", "position": { "start": { "line": 27, "column": 13 }, "end": { "line": 27, "column": 27 } } }], "position": { "start": { "line": 24, "column": 10 }, "end": { "line": 28, "column": 10 } } }, { "type": "rule", "selectors": [".s2-3"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181818", "position": { "start": { "line": 30, "column": 13 }, "end": { "line": 30, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181819", "position": { "start": { "line": 31, "column": 13 }, "end": { "line": 31, "column": 27 } } }], "position": { "start": { "line": 28, "column": 10 }, "end": { "line": 32, "column": 10 } } }], "position": { "start": { "line": 15, "column": 5 }, "end": { "line": 33, "column": 6 } } }, { "type": "rule", "selectors": [".s1-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181820", "position": { "start": { "line": 37, "column": 9 }, "end": { "line": 37, "column": 28 } } }, { "type": "declaration", "property": "color", "value": "#181821", "position": { "start": { "line": 38, "column": 9 }, "end": { "line": 38, "column": 23 } } }], "position": { "start": { "line": 35, "column": 5 }, "end": { "line": 39, "column": 6 } } }], "position": { "start": { "line": 7, "column": 1 }, "end": { "line": 40, "column": 2 } } }, { "type": "rule", "selectors": [".s0-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181822", "position": { "start": { "line": 43, "column": 5 }, "end": { "line": 43, "column": 24 } } }, { "type": "declaration", "property": "color", "value": "#181823", "position": { "start": { "line": 44, "column": 5 }, "end": { "line": 44, "column": 19 } } }], "position": { "start": { "line": 42, "column": 1 }, "end": { "line": 45, "column": 2 } } }, { "type": "rule", "selectors": [".s0-2-new"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181822", "position": { "start": { "line": 43, "column": 5 }, "end": { "line": 43, "column": 24 } } }, { "type": "declaration", "property": "color", "value": "#181823", "position": { "start": { "line": 44, "column": 5 }, "end": { "line": 44, "column": 19 } } }], "position": { "start": { "line": 42, "column": 1 }, "end": { "line": 45, "column": 2 } } }];
+ const result = (0, css_2.copyCssData)(targetSelector, newSelectorName, cssObj);
+ (0, vitest_1.expect)(result.stylesheet.rules).toEqual(expectedOutput);
+ });
+ (0, vitest_1.it)("should copy the CSS data (multiple nested rules)", () => {
+ const cssObj = css_1.default.parse(testCss);
+ const targetSelector = ".s2-3";
+ const newSelectorName = ".s2-3-new";
+ const expectedOutput = [{ "type": "rule", "selectors": [".s0-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181810", "position": { "start": { "line": 3, "column": 5 }, "end": { "line": 3, "column": 24 } } }, { "type": "declaration", "property": "color", "value": "#181811", "position": { "start": { "line": 4, "column": 5 }, "end": { "line": 4, "column": 19 } } }], "position": { "start": { "line": 2, "column": 1 }, "end": { "line": 5, "column": 2 } } }, { "type": "media", "media": "(min-width: 640px)", "rules": [{ "type": "rule", "selectors": [".s1-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181812", "position": { "start": { "line": 11, "column": 9 }, "end": { "line": 11, "column": 28 } } }, { "type": "declaration", "property": "color", "value": "#181813", "position": { "start": { "line": 12, "column": 9 }, "end": { "line": 12, "column": 23 } } }], "position": { "start": { "line": 9, "column": 5 }, "end": { "line": 13, "column": 6 } } }, { "type": "media", "media": "(min-width: 768px)", "rules": [{ "type": "rule", "selectors": [".s2-1", ".s2-1-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181814", "position": { "start": { "line": 18, "column": 13 }, "end": { "line": 18, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181815", "position": { "start": { "line": 19, "column": 13 }, "end": { "line": 19, "column": 27 } } }], "position": { "start": { "line": 17, "column": 9 }, "end": { "line": 20, "column": 10 } } }, { "type": "rule", "selectors": [".s2-1", ".s2-1-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181814", "position": { "start": { "line": 22, "column": 13 }, "end": { "line": 22, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181815", "position": { "start": { "line": 23, "column": 13 }, "end": { "line": 23, "column": 27 } } }], "position": { "start": { "line": 20, "column": 10 }, "end": { "line": 24, "column": 10 } } }, { "type": "rule", "selectors": [".s2-2", ".s2-2-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181816", "position": { "start": { "line": 26, "column": 13 }, "end": { "line": 26, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181817", "position": { "start": { "line": 27, "column": 13 }, "end": { "line": 27, "column": 27 } } }], "position": { "start": { "line": 24, "column": 10 }, "end": { "line": 28, "column": 10 } } }, { "type": "rule", "selectors": [".s2-3"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181818", "position": { "start": { "line": 30, "column": 13 }, "end": { "line": 30, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181819", "position": { "start": { "line": 31, "column": 13 }, "end": { "line": 31, "column": 27 } } }], "position": { "start": { "line": 28, "column": 10 }, "end": { "line": 32, "column": 10 } } }, { "type": "rule", "selectors": [".s2-3-new"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181818", "position": { "start": { "line": 30, "column": 13 }, "end": { "line": 30, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181819", "position": { "start": { "line": 31, "column": 13 }, "end": { "line": 31, "column": 27 } } }], "position": { "start": { "line": 28, "column": 10 }, "end": { "line": 32, "column": 10 } } }], "position": { "start": { "line": 15, "column": 5 }, "end": { "line": 33, "column": 6 } } }, { "type": "rule", "selectors": [".s1-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181820", "position": { "start": { "line": 37, "column": 9 }, "end": { "line": 37, "column": 28 } } }, { "type": "declaration", "property": "color", "value": "#181821", "position": { "start": { "line": 38, "column": 9 }, "end": { "line": 38, "column": 23 } } }], "position": { "start": { "line": 35, "column": 5 }, "end": { "line": 39, "column": 6 } } }], "position": { "start": { "line": 7, "column": 1 }, "end": { "line": 40, "column": 2 } } }, { "type": "rule", "selectors": [".s0-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181822", "position": { "start": { "line": 43, "column": 5 }, "end": { "line": 43, "column": 24 } } }, { "type": "declaration", "property": "color", "value": "#181823", "position": { "start": { "line": 44, "column": 5 }, "end": { "line": 44, "column": 19 } } }], "position": { "start": { "line": 42, "column": 1 }, "end": { "line": 45, "column": 2 } } }];
+ const result = (0, css_2.copyCssData)(targetSelector, newSelectorName, cssObj);
+ (0, vitest_1.expect)(result.stylesheet.rules).toEqual(expectedOutput);
+ });
+ (0, vitest_1.it)("should copy the CSS data (multiple selector in same rule)", () => {
+ const cssObj = css_1.default.parse(testCss);
+ const targetSelector = ".s2-2-2";
+ const newSelectorName = ".s2-2-2-new";
+ const expectedOutput = [{ "type": "rule", "selectors": [".s0-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181810", "position": { "start": { "line": 3, "column": 5 }, "end": { "line": 3, "column": 24 } } }, { "type": "declaration", "property": "color", "value": "#181811", "position": { "start": { "line": 4, "column": 5 }, "end": { "line": 4, "column": 19 } } }], "position": { "start": { "line": 2, "column": 1 }, "end": { "line": 5, "column": 2 } } }, { "type": "media", "media": "(min-width: 640px)", "rules": [{ "type": "rule", "selectors": [".s1-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181812", "position": { "start": { "line": 11, "column": 9 }, "end": { "line": 11, "column": 28 } } }, { "type": "declaration", "property": "color", "value": "#181813", "position": { "start": { "line": 12, "column": 9 }, "end": { "line": 12, "column": 23 } } }], "position": { "start": { "line": 9, "column": 5 }, "end": { "line": 13, "column": 6 } } }, { "type": "media", "media": "(min-width: 768px)", "rules": [{ "type": "rule", "selectors": [".s2-1", ".s2-1-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181814", "position": { "start": { "line": 18, "column": 13 }, "end": { "line": 18, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181815", "position": { "start": { "line": 19, "column": 13 }, "end": { "line": 19, "column": 27 } } }], "position": { "start": { "line": 17, "column": 9 }, "end": { "line": 20, "column": 10 } } }, { "type": "rule", "selectors": [".s2-1", ".s2-1-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181814", "position": { "start": { "line": 22, "column": 13 }, "end": { "line": 22, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181815", "position": { "start": { "line": 23, "column": 13 }, "end": { "line": 23, "column": 27 } } }], "position": { "start": { "line": 20, "column": 10 }, "end": { "line": 24, "column": 10 } } }, { "type": "rule", "selectors": [".s2-2", ".s2-2-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181816", "position": { "start": { "line": 26, "column": 13 }, "end": { "line": 26, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181817", "position": { "start": { "line": 27, "column": 13 }, "end": { "line": 27, "column": 27 } } }], "position": { "start": { "line": 24, "column": 10 }, "end": { "line": 28, "column": 10 } } }, { "type": "rule", "selectors": [".s2-2-2-new"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181816", "position": { "start": { "line": 26, "column": 13 }, "end": { "line": 26, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181817", "position": { "start": { "line": 27, "column": 13 }, "end": { "line": 27, "column": 27 } } }], "position": { "start": { "line": 24, "column": 10 }, "end": { "line": 28, "column": 10 } } }, { "type": "rule", "selectors": [".s2-3"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181818", "position": { "start": { "line": 30, "column": 13 }, "end": { "line": 30, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181819", "position": { "start": { "line": 31, "column": 13 }, "end": { "line": 31, "column": 27 } } }], "position": { "start": { "line": 28, "column": 10 }, "end": { "line": 32, "column": 10 } } }], "position": { "start": { "line": 15, "column": 5 }, "end": { "line": 33, "column": 6 } } }, { "type": "rule", "selectors": [".s1-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181820", "position": { "start": { "line": 37, "column": 9 }, "end": { "line": 37, "column": 28 } } }, { "type": "declaration", "property": "color", "value": "#181821", "position": { "start": { "line": 38, "column": 9 }, "end": { "line": 38, "column": 23 } } }], "position": { "start": { "line": 35, "column": 5 }, "end": { "line": 39, "column": 6 } } }], "position": { "start": { "line": 7, "column": 1 }, "end": { "line": 40, "column": 2 } } }, { "type": "rule", "selectors": [".s0-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181822", "position": { "start": { "line": 43, "column": 5 }, "end": { "line": 43, "column": 24 } } }, { "type": "declaration", "property": "color", "value": "#181823", "position": { "start": { "line": 44, "column": 5 }, "end": { "line": 44, "column": 19 } } }], "position": { "start": { "line": 42, "column": 1 }, "end": { "line": 45, "column": 2 } } }];
+ const result = (0, css_2.copyCssData)(targetSelector, newSelectorName, cssObj);
+ (0, vitest_1.expect)(result.stylesheet.rules).toEqual(expectedOutput);
+ });
+ (0, vitest_1.it)("should copy the CSS data (same selector with different declarations)", () => {
+ const cssObj = css_1.default.parse(testCss);
+ const targetSelector = ".s2-1";
+ const newSelectorName = ".s2-1-new";
+ const expectedOutput = [{ "type": "rule", "selectors": [".s0-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181810", "position": { "start": { "line": 3, "column": 5 }, "end": { "line": 3, "column": 24 } } }, { "type": "declaration", "property": "color", "value": "#181811", "position": { "start": { "line": 4, "column": 5 }, "end": { "line": 4, "column": 19 } } }], "position": { "start": { "line": 2, "column": 1 }, "end": { "line": 5, "column": 2 } } }, { "type": "media", "media": "(min-width: 640px)", "rules": [{ "type": "rule", "selectors": [".s1-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181812", "position": { "start": { "line": 11, "column": 9 }, "end": { "line": 11, "column": 28 } } }, { "type": "declaration", "property": "color", "value": "#181813", "position": { "start": { "line": 12, "column": 9 }, "end": { "line": 12, "column": 23 } } }], "position": { "start": { "line": 9, "column": 5 }, "end": { "line": 13, "column": 6 } } }, { "type": "media", "media": "(min-width: 768px)", "rules": [{ "type": "rule", "selectors": [".s2-1", ".s2-1-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181814", "position": { "start": { "line": 18, "column": 13 }, "end": { "line": 18, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181815", "position": { "start": { "line": 19, "column": 13 }, "end": { "line": 19, "column": 27 } } }], "position": { "start": { "line": 17, "column": 9 }, "end": { "line": 20, "column": 10 } } }, { "type": "rule", "selectors": [".s2-1-new"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181814", "position": { "start": { "line": 18, "column": 13 }, "end": { "line": 18, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181815", "position": { "start": { "line": 19, "column": 13 }, "end": { "line": 19, "column": 27 } } }], "position": { "start": { "line": 17, "column": 9 }, "end": { "line": 20, "column": 10 } } }, { "type": "rule", "selectors": [".s2-1", ".s2-1-1"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181814", "position": { "start": { "line": 22, "column": 13 }, "end": { "line": 22, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181815", "position": { "start": { "line": 23, "column": 13 }, "end": { "line": 23, "column": 27 } } }], "position": { "start": { "line": 20, "column": 10 }, "end": { "line": 24, "column": 10 } } }, { "type": "rule", "selectors": [".s2-1-new"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181814", "position": { "start": { "line": 22, "column": 13 }, "end": { "line": 22, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181815", "position": { "start": { "line": 23, "column": 13 }, "end": { "line": 23, "column": 27 } } }], "position": { "start": { "line": 20, "column": 10 }, "end": { "line": 24, "column": 10 } } }, { "type": "rule", "selectors": [".s2-2", ".s2-2-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181816", "position": { "start": { "line": 26, "column": 13 }, "end": { "line": 26, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181817", "position": { "start": { "line": 27, "column": 13 }, "end": { "line": 27, "column": 27 } } }], "position": { "start": { "line": 24, "column": 10 }, "end": { "line": 28, "column": 10 } } }, { "type": "rule", "selectors": [".s2-3"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181818", "position": { "start": { "line": 30, "column": 13 }, "end": { "line": 30, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181819", "position": { "start": { "line": 31, "column": 13 }, "end": { "line": 31, "column": 27 } } }], "position": { "start": { "line": 28, "column": 10 }, "end": { "line": 32, "column": 10 } } }], "position": { "start": { "line": 15, "column": 5 }, "end": { "line": 33, "column": 6 } } }, { "type": "rule", "selectors": [".s1-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181820", "position": { "start": { "line": 37, "column": 9 }, "end": { "line": 37, "column": 28 } } }, { "type": "declaration", "property": "color", "value": "#181821", "position": { "start": { "line": 38, "column": 9 }, "end": { "line": 38, "column": 23 } } }], "position": { "start": { "line": 35, "column": 5 }, "end": { "line": 39, "column": 6 } } }], "position": { "start": { "line": 7, "column": 1 }, "end": { "line": 40, "column": 2 } } }, { "type": "rule", "selectors": [".s0-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181822", "position": { "start": { "line": 43, "column": 5 }, "end": { "line": 43, "column": 24 } } }, { "type": "declaration", "property": "color", "value": "#181823", "position": { "start": { "line": 44, "column": 5 }, "end": { "line": 44, "column": 19 } } }], "position": { "start": { "line": 42, "column": 1 }, "end": { "line": 45, "column": 2 } } }];
+ const result = (0, css_2.copyCssData)(targetSelector, newSelectorName, cssObj);
+ (0, vitest_1.expect)(result.stylesheet.rules).toEqual(expectedOutput);
+ });
+});
+(0, vitest_1.describe)("extractClassFromSelector", () => {
+ (0, vitest_1.it)("should extract single class from simple selector", () => {
+ const sample = ".example htmlTag";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["example"]
+ });
+ });
+ (0, vitest_1.test)("should extract multiple classes from complex selector", () => {
+ const sample = ":is(.some-class .some-class\\:bg-dark::-moz-placeholder)[data-active=\'true\']";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["some-class", "some-class\\:bg-dark"]
+ });
+ });
+ (0, vitest_1.test)("should handle selector with no classes", () => {
+ const sample = "div";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: []
+ });
+ });
+ (0, vitest_1.test)("should handle selector with action pseudo-classes and not extract them", () => {
+ const sample = ".btn:hover .btn-active::after";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["btn", "btn-active"]
+ });
+ });
+ (0, vitest_1.test)("should handle selector with vendor pseudo-classes and not extract them", () => {
+ const sample = ".btn-moz:-moz-focusring .btn-ms::-ms-placeholder .btn-webkit::-webkit-placeholder .btn-o::-o-placeholder";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["btn-moz", "btn-ms", "btn-webkit", "btn-o"]
+ });
+ });
+ (0, vitest_1.test)("should handle selector with escaped characters", () => {
+ const sample = ".escaped\\:class:action";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["escaped\\:class"]
+ });
+ });
+ (0, vitest_1.test)("should handle selector with multiple classes separated by spaces", () => {
+ const sample = ".class1 .class2 .class3";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["class1", "class2", "class3"]
+ });
+ });
+ (0, vitest_1.test)("should handle selector with multiple classes separated by commas", () => {
+ const sample = ".class1, .class2, .class3";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["class1", "class2", "class3"]
+ });
+ });
+ (0, vitest_1.test)("should handle selector with a combination of classes and ids", () => {
+ const sample = ".class1 #id .class2";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["class1", "class2"]
+ });
+ });
+ (0, vitest_1.test)("should handle [attribute] selector", () => {
+ const sample = ".class1[data-attr=\"value\"] .class2[data-attr='value']";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["class1", "class2"]
+ });
+ });
+ (0, vitest_1.test)("should handle action pseudo-class selector correctly", () => {
+ const sample = ".class1\\:hover\\:class2:after .class3\\:hover\\:class4:after:hover :is(.class5 .class6\\:hover\\:class7:hover:after) :is(.hover\\:class8\\:class9):after>:last-child";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["class1\\:hover\\:class2", "class3\\:hover\\:class4", "class5", "class6\\:hover\\:class7", "hover\\:class8\\:class9"]
+ });
+ });
+ (0, vitest_1.test)("should ignore [attribute] selector that not in the same scope as class", () => {
+ const sample = ":is(.class1 .class2\\:class3\\:\\!class4)[aria-selected=\"true\"]";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["class1", "class2\\:class3\\:\\!class4"]
+ });
+ });
+ (0, vitest_1.test)("should return null for invalid input types", () => {
+ (0, vitest_1.expect)(() => (0, css_2.extractClassFromSelector)(null)).toThrow(TypeError);
+ (0, vitest_1.expect)(() => (0, css_2.extractClassFromSelector)(undefined)).toThrow(TypeError);
+ (0, vitest_1.expect)(() => (0, css_2.extractClassFromSelector)(123)).toThrow(TypeError);
+ });
+ (0, vitest_1.test)("should handle Tailwind CSS important selector '!'", () => {
+ const sample = ".\\!my-0 .some-class\\:\\!bg-white";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["\\!my-0", "some-class\\:\\!bg-white"]
+ });
+ });
+ (0, vitest_1.test)("should handle Tailwind CSS selector with start with '-'", () => {
+ const sample = ".-class-1";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["-class-1"]
+ });
+ });
+ (0, vitest_1.test)("should handle Tailwind CSS selector with '.' at the number", () => {
+ const sample = ".class-0\\.5 .class-1\\.125";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["class-0\\.5", "class-1\\.125"]
+ });
+ });
+ (0, vitest_1.test)("should handle Tailwind CSS selector with '/' at the number", () => {
+ const sample = ".class-1\\/2";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["class-1\\/2"]
+ });
+ });
+ (0, vitest_1.test)("should handle Tailwind CSS selector with '%' at the number", () => {
+ const sample = ".\\[\\.class1\\]\\:to-85\\%";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["\\[\\.class1\\]\\:to-85\\%"]
+ });
+ });
+ (0, vitest_1.test)("should handle Tailwind CSS universal selector", () => {
+ const sample = ".\\*\\:class1 .class2\\*\\:class3 .class4\\*:.class5";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["\\*\\:class1", "class2\\*\\:class3", "class4\\*", "class5"]
+ });
+ });
+ (0, vitest_1.test)("should handle Tailwind CSS [custom parameter] selector", () => {
+ const sample = ".class1\\[100\\] .class2-\\[200\\]";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["class1\\[100\\]", "class2-\\[200\\]"]
+ });
+ });
+ (0, vitest_1.test)("should handle Tailwind CSS [custom parameter] selector with escaped characters", () => {
+ const sample = ".class1\\[1em\\] .class2-\\[2em\\] .class3\\[3\\%\\] .class4-\\[4\\%\\]";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["class1\\[1em\\]", "class2-\\[2em\\]", "class3\\[3\\%\\]", "class4-\\[4\\%\\]"]
+ });
+ });
+ (0, vitest_1.test)("should handle complex Tailwind CSS [custom parameter] selector", () => {
+ const sample = ".w-\\[calc\\(10\\%\\+5px\\)\\]";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["w-\\[calc\\(10\\%\\+5px\\)\\]"]
+ });
+ });
+ (0, vitest_1.test)("should ignore Tailwind CSS [custom parameter] selector that not in the same scope as class", () => {
+ const sample = ":is(.class1)[100]";
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: ["class1"]
+ });
+ });
+ (0, vitest_1.test)("should handle Tailwind CSS [custom selector] child elements selector", () => {
+ const sample = `
+ .\\[\\&\\>\\class1\\]:after
+ .\\[\\&_class2\\]:hover
+ .\\[\\&_\\.class3\\]\\:class4
+ .\\[\\&_\\#id1\\]::moz-placeholder
+ .\\[\\&_\\#id2\\]\\:class5.\\[\\&_\\#id\\]\\:class6
+ `;
+ const result = (0, css_2.extractClassFromSelector)(sample);
+ (0, vitest_1.expect)(result).toEqual({
+ selector: sample,
+ extractedClasses: [
+ "\\[\\&\\>\\class1\\]", "\\[\\&_class2\\]",
+ "\\[\\&_\\.class3\\]\\:class4", "\\[\\&_\\#id1\\]",
+ "\\[\\&_\\#id2\\]\\:class5", "\\[\\&_\\#id\\]\\:class6"
+ ]
+ });
+ });
+});
diff --git a/dist/handlers/html.d.ts b/dist/handlers/html.d.ts
new file mode 100644
index 0000000..1370fa4
--- /dev/null
+++ b/dist/handlers/html.d.ts
@@ -0,0 +1,13 @@
+import { SelectorConversion } from "../types";
+declare function findHtmlTagContents(content: string, targetTag: string, targetClass?: string | null): string[];
+declare function findHtmlTagContentsByClass(content: string, targetClass: string): string[];
+declare function obfuscateHtmlClassNames({ html, selectorConversion, obfuscateMarkerClass, contentIgnoreRegexes, }: {
+ html: string;
+ selectorConversion: SelectorConversion;
+ obfuscateMarkerClass?: string;
+ contentIgnoreRegexes?: RegExp[];
+}): {
+ obfuscatedContent: string;
+ usedKeys: string[];
+};
+export { findHtmlTagContents, findHtmlTagContentsByClass, obfuscateHtmlClassNames, };
diff --git a/dist/handlers/html.js b/dist/handlers/html.js
new file mode 100644
index 0000000..60cf143
--- /dev/null
+++ b/dist/handlers/html.js
@@ -0,0 +1,183 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.obfuscateHtmlClassNames = exports.findHtmlTagContentsByClass = exports.findHtmlTagContents = void 0;
+const tslib_1 = require("tslib");
+const htmlparser2 = tslib_1.__importStar(require("htmlparser2"));
+const utils_1 = require("../utils");
+const js_1 = require("./js");
+function findHtmlTagContentsRecursive(content, targetTag, targetClass = null, foundTagContents = [], deep = 0, maxDeep = -1) {
+ let contentAfterTag = content;
+ const startTagWithClassRegexStr = targetClass ?
+ `(<\\w+?\\s+?class\\s*=\\s*['\"][^'\"]*?\\b${targetClass}\\b)`
+ : "";
+ const startTagRegexStr = `(<${targetTag}[\\s|>])`;
+ const endTagRegexStr = `(<\/${targetTag}>)`;
+ const clearContentBeforeStartTagRegex = new RegExp(`${startTagWithClassRegexStr ? startTagWithClassRegexStr + ".*|" + startTagRegexStr : startTagRegexStr + ".*"}`, "i");
+ const contentAfterStartTagMatch = contentAfterTag.match(clearContentBeforeStartTagRegex);
+ if (contentAfterStartTagMatch) {
+ contentAfterTag = contentAfterStartTagMatch[0];
+ }
+ let endTagCont = 0;
+ const endTagContRegex = new RegExp(endTagRegexStr, "gi");
+ const endTagContMatch = contentAfterTag.match(endTagContRegex);
+ if (endTagContMatch) {
+ endTagCont = endTagContMatch.length;
+ }
+ let closeTagPoition = 0;
+ const tagPatternRegex = new RegExp(`${startTagWithClassRegexStr ? startTagWithClassRegexStr + "|" + startTagRegexStr : startTagRegexStr}|${endTagRegexStr}`, "gi");
+ const tagPatternMatch = contentAfterTag.match(tagPatternRegex);
+ if (tagPatternMatch) {
+ let tagCount = 0;
+ let markedPosition = false;
+ for (let i = 0; i < tagPatternMatch.length; i++) {
+ if (tagPatternMatch[i].startsWith("")) {
+ if (!markedPosition) {
+ closeTagPoition = endTagCont - tagCount;
+ markedPosition = true;
+ }
+ tagCount--;
+ }
+ else {
+ tagCount++;
+ }
+ if (tagCount == 0) {
+ break;
+ }
+ }
+ ;
+ }
+ const tagEndRegex = new RegExp(`(.*)${endTagRegexStr}`, "i");
+ for (let i = 0; i < closeTagPoition; i++) {
+ const tagCloseMatch = contentAfterTag.match(tagEndRegex);
+ if (tagCloseMatch) {
+ contentAfterTag = tagCloseMatch[1];
+ }
+ }
+ const clearContentAfterCloseTagRegex = new RegExp(`.*${endTagRegexStr}`, "i");
+ const clearContentAfterCloseTagMatch = contentAfterTag.match(clearContentAfterCloseTagRegex);
+ if (clearContentAfterCloseTagMatch) {
+ contentAfterTag = clearContentAfterCloseTagMatch[0];
+ foundTagContents.push(contentAfterTag);
+ }
+ const remainingHtmlRegex = new RegExp(contentAfterTag.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + "(.*)", "i");
+ const remainingHtmlMatch = content.match(remainingHtmlRegex);
+ if (remainingHtmlMatch) {
+ const remainingHtml = remainingHtmlMatch[1];
+ const remainingHtmlTagRegex = new RegExp(`(<\\w+?>)`, "i");
+ const remainingHtmlTagMatch = remainingHtml.match(remainingHtmlTagRegex);
+ if (remainingHtmlTagMatch) {
+ if (maxDeep === -1 || deep < maxDeep) {
+ return findHtmlTagContentsRecursive(remainingHtml, targetTag, targetClass, foundTagContents, deep + 1, maxDeep);
+ }
+ else {
+ (0, utils_1.log)("warn", "HTML search:", "Max deep reached, recursive break");
+ return foundTagContents;
+ }
+ }
+ }
+ return foundTagContents;
+}
+function findHtmlTagContents(content, targetTag, targetClass = null) {
+ return findHtmlTagContentsRecursive(content, targetTag, targetClass);
+}
+exports.findHtmlTagContents = findHtmlTagContents;
+function findHtmlTagContentsByClass(content, targetClass) {
+ const regex = new RegExp(`(<(\\w+)\\s+class\\s*=\\s*['\"][^'\"]*?\\b${targetClass}\\b)`, "i");
+ const match = content.match(regex);
+ if (match) {
+ const tag = match[2];
+ return findHtmlTagContents(content, tag, targetClass);
+ }
+ else {
+ return [];
+ }
+}
+exports.findHtmlTagContentsByClass = findHtmlTagContentsByClass;
+function obfuscateHtmlClassNames({ html, selectorConversion, obfuscateMarkerClass = "", contentIgnoreRegexes = [], }) {
+ const voidTags = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"];
+ let modifiedHtml = "";
+ let insideObsClassScope = false;
+ let ObsClassScopeTagCount = 0;
+ let ObsClassScopeTag = "";
+ let scriptContent = "";
+ let isScriptTag = false;
+ const usedKeys = [];
+ const parser = new htmlparser2.Parser({
+ onprocessinginstruction(name, data) {
+ modifiedHtml += `<${data}>`;
+ },
+ onopentag(tagName, attribs) {
+ if (tagName === "script") {
+ isScriptTag = true;
+ scriptContent = "";
+ }
+ if (attribs.class) {
+ if (!insideObsClassScope && obfuscateMarkerClass && attribs.class.includes(obfuscateMarkerClass)) {
+ insideObsClassScope = true;
+ ObsClassScopeTag = tagName;
+ }
+ if (insideObsClassScope || !obfuscateMarkerClass) {
+ const { obfuscatedContent, usedKeys: _usedKeys } = (0, utils_1.obfuscateKeys)(selectorConversion, attribs.class, [], true);
+ usedKeys.push(..._usedKeys);
+ attribs.class = obfuscatedContent;
+ }
+ }
+ if (insideObsClassScope && tagName === ObsClassScopeTag) {
+ ObsClassScopeTagCount++;
+ }
+ modifiedHtml += `<${tagName}`;
+ for (const key in attribs) {
+ modifiedHtml += ` ${key}="${attribs[key]}"`;
+ }
+ if (voidTags.includes(tagName)) {
+ modifiedHtml += " />";
+ }
+ else {
+ modifiedHtml += ">";
+ }
+ },
+ oncomment(comment) {
+ modifiedHtml += ``;
+ },
+ ontext(text) {
+ if (isScriptTag) {
+ scriptContent += text;
+ }
+ else {
+ modifiedHtml += text;
+ }
+ },
+ onclosetag(tagname) {
+ if (voidTags.includes(tagname)) {
+ return;
+ }
+ if (tagname === "script" && isScriptTag) {
+ isScriptTag = false;
+ let obfuscatedScriptContent = scriptContent;
+ Object.keys(selectorConversion).forEach((key) => {
+ const className = key.slice(1);
+ const obfuscatedJs = (0, js_1.obfuscateJs)(obfuscatedScriptContent, className, { [key]: selectorConversion[key] }, "{a HTML file path}", contentIgnoreRegexes);
+ if (obfuscatedJs !== obfuscatedScriptContent) {
+ obfuscatedScriptContent = obfuscatedJs;
+ usedKeys.push(key);
+ }
+ });
+ modifiedHtml += `${obfuscatedScriptContent}`;
+ }
+ modifiedHtml += `${tagname}>`;
+ if (insideObsClassScope && tagname === ObsClassScopeTag) {
+ ObsClassScopeTagCount--;
+ }
+ if (ObsClassScopeTagCount === 0) {
+ insideObsClassScope = false;
+ }
+ }
+ }, { decodeEntities: true });
+ parser.write(html);
+ parser.end();
+ return {
+ obfuscatedContent: modifiedHtml,
+ usedKeys: Array.from(new Set(usedKeys))
+ };
+}
+exports.obfuscateHtmlClassNames = obfuscateHtmlClassNames;
diff --git a/dist/handlers/html.test.d.ts b/dist/handlers/html.test.d.ts
new file mode 100644
index 0000000..cb0ff5c
--- /dev/null
+++ b/dist/handlers/html.test.d.ts
@@ -0,0 +1 @@
+export {};
diff --git a/dist/handlers/html.test.js b/dist/handlers/html.test.js
new file mode 100644
index 0000000..67ec097
--- /dev/null
+++ b/dist/handlers/html.test.js
@@ -0,0 +1,116 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const vitest_1 = require("vitest");
+const html_1 = require("./html");
+(0, vitest_1.describe)("findHtmlTagContentsByClass", () => {
+ const content = `
0123456
`;
+ (0, vitest_1.it)("should return the correct content within the tag that with a given class", () => {
+ const targetClass = "test1";
+ const expectedOutput = [''];
+ const result = (0, html_1.findHtmlTagContentsByClass)(content, targetClass);
+ (0, vitest_1.expect)(result).toEqual(expectedOutput);
+ });
+ (0, vitest_1.it)("should return empty array if no content found", () => {
+ const targetClass = "test5";
+ const expectedOutput = [];
+ const result = (0, html_1.findHtmlTagContentsByClass)(content, targetClass);
+ (0, vitest_1.expect)(result).toEqual(expectedOutput);
+ });
+});
+(0, vitest_1.describe)("obfuscateHtmlClassNames", () => {
+ (0, vitest_1.it)("should obfuscate class names correctly", () => {
+ const html = ``;
+ const selectorConversion = { ".foo": ".a" };
+ const result = (0, html_1.obfuscateHtmlClassNames)({ html, selectorConversion });
+ (0, vitest_1.expect)(result.obfuscatedContent).toEqual(``);
+ (0, vitest_1.expect)(result.usedKeys).to.deep.equal([".foo"]);
+ });
+ (0, vitest_1.it)("should handle nested tags with obfuscate class", () => {
+ const html = `
`;
+ const selectorConversion = { ".foo": ".a" };
+ const keyClass = "key";
+ const result = (0, html_1.obfuscateHtmlClassNames)({ html, selectorConversion, obfuscateMarkerClass: keyClass });
+ (0, vitest_1.expect)(result.obfuscatedContent).toEqual(`
`);
+ (0, vitest_1.expect)(result.usedKeys).to.deep.equal([".foo"]);
+ });
+ (0, vitest_1.it)("should not obfuscate class names outside of obfuscate class scope", () => {
+ const html = `
`;
+ const selectorConversion = { ".foo": ".a", ".bar": ".b" };
+ const keyClass = "key";
+ const result = (0, html_1.obfuscateHtmlClassNames)({ html, selectorConversion, obfuscateMarkerClass: keyClass });
+ (0, vitest_1.expect)(result.obfuscatedContent).toEqual(`
`);
+ (0, vitest_1.expect)(result.usedKeys).to.deep.equal([]);
+ });
+ (0, vitest_1.it)("should handle script tags", () => {
+ const html = ``;
+ const selectorConversion = { ".fol": ".a", ".foo": ".b" };
+ const result = (0, html_1.obfuscateHtmlClassNames)({ html, selectorConversion, obfuscateMarkerClass: "" });
+ (0, vitest_1.expect)(result.obfuscatedContent).toEqual(``);
+ (0, vitest_1.expect)(result.usedKeys).to.deep.equal([".fol", ".foo"]);
+ });
+ (0, vitest_1.it)("should handle void tags", () => {
+ const html = `
`;
+ const selectorConversion = { ".foo": ".a" };
+ const result = (0, html_1.obfuscateHtmlClassNames)({ html, selectorConversion });
+ (0, vitest_1.expect)(result.obfuscatedContent).toEqual(`
`);
+ (0, vitest_1.expect)(result.usedKeys).to.deep.equal([".foo"]);
+ });
+ (0, vitest_1.it)("should handle comments", () => {
+ const html = ``;
+ const selectorConversion = { ".foo": ".a" };
+ const result = (0, html_1.obfuscateHtmlClassNames)({ html, selectorConversion });
+ (0, vitest_1.expect)(result.obfuscatedContent).toEqual(``);
+ (0, vitest_1.expect)(result.usedKeys).to.deep.equal([".foo"]);
+ });
+ (0, vitest_1.it)("should handle HTML without classes", () => {
+ const html = "";
+ const selectorConversion = {};
+ const result = (0, html_1.obfuscateHtmlClassNames)({ html, selectorConversion });
+ (0, vitest_1.expect)(result.obfuscatedContent).toEqual("");
+ (0, vitest_1.expect)(result.usedKeys).to.deep.equal([]);
+ });
+ (0, vitest_1.it)("should handle empty HTML", () => {
+ const html = "";
+ const selectorConversion = { ".foo": ".a" };
+ const result = (0, html_1.obfuscateHtmlClassNames)({ html, selectorConversion });
+ (0, vitest_1.expect)(result.obfuscatedContent).toEqual("");
+ (0, vitest_1.expect)(result.usedKeys).to.deep.equal([]);
+ });
+ (0, vitest_1.it)("should handle HTML with multiple classes in one element", () => {
+ const html = ``;
+ const selectorConversion = { ".foo": ".a", ".bar": ".b", ".baz": ".c" };
+ const result = (0, html_1.obfuscateHtmlClassNames)({ html, selectorConversion });
+ (0, vitest_1.expect)(result.obfuscatedContent).toEqual(``);
+ (0, vitest_1.expect)(result.usedKeys).to.deep.equal([".foo", ".bar", ".baz"]);
+ });
+ (0, vitest_1.it)("should handle HTML with nested structures and multiple classes", () => {
+ const html = `
`;
+ const selectorConversion = { ".foo": ".a", ".bar": ".b", ".baz": ".c" };
+ const result = (0, html_1.obfuscateHtmlClassNames)({ html, selectorConversion });
+ (0, vitest_1.expect)(result.obfuscatedContent).toEqual(`
`);
+ (0, vitest_1.expect)(result.usedKeys).to.deep.equal([".foo", ".bar", ".baz"]);
+ });
+ (0, vitest_1.it)("should handle HTML with obfuscate marker class", () => {
+ const html = `
`;
+ const selectorConversion = { ".foo": ".a" };
+ const obfuscateMarkerClass = "key";
+ const result = (0, html_1.obfuscateHtmlClassNames)({ html, selectorConversion, obfuscateMarkerClass });
+ (0, vitest_1.expect)(result.obfuscatedContent).toEqual(`
`);
+ (0, vitest_1.expect)(result.usedKeys).to.deep.equal([".foo"]);
+ });
+ (0, vitest_1.it)("should handle HTML with multiple classes and obfuscate marker class", () => {
+ const html = ``;
+ const selectorConversion = { ".foo": ".a", ".bar": ".b", ".baz": ".c" };
+ const obfuscateMarkerClass = "key";
+ const result = (0, html_1.obfuscateHtmlClassNames)({ html, selectorConversion, obfuscateMarkerClass });
+ (0, vitest_1.expect)(result.obfuscatedContent).toEqual(``);
+ (0, vitest_1.expect)(result.usedKeys).to.deep.equal([".foo", ".bar", ".baz"]);
+ });
+ (0, vitest_1.it)("should handle HTML instruction", () => {
+ const html = ``;
+ const selectorConversion = { ".foo": ".a" };
+ const result = (0, html_1.obfuscateHtmlClassNames)({ html, selectorConversion });
+ (0, vitest_1.expect)(result.obfuscatedContent).toEqual(``);
+ (0, vitest_1.expect)(result.usedKeys).to.deep.equal([".foo"]);
+ });
+});
diff --git a/dist/handlers/js-ast.d.ts b/dist/handlers/js-ast.d.ts
new file mode 100644
index 0000000..0d84e51
--- /dev/null
+++ b/dist/handlers/js-ast.d.ts
@@ -0,0 +1,9 @@
+import { NodePath } from "@babel/traverse";
+import * as t from "@babel/types";
+import { type SelectorConversion } from "../types";
+declare function obfuscateJsWithAst(code: string, selectorConversion: SelectorConversion | undefined, startingKeys?: string[], stripUnnecessarySpace?: boolean): {
+ obfuscatedCode: string;
+ usedKeys: Set;
+};
+declare function searchStringLiterals(path: NodePath, callback: (str: string) => void | string, scannedNodes?: Set): NodePath | undefined;
+export { searchStringLiterals, obfuscateJsWithAst, };
diff --git a/dist/handlers/js-ast.js b/dist/handlers/js-ast.js
new file mode 100644
index 0000000..f8cdd1e
--- /dev/null
+++ b/dist/handlers/js-ast.js
@@ -0,0 +1,252 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.obfuscateJsWithAst = exports.searchStringLiterals = void 0;
+const tslib_1 = require("tslib");
+const parser = tslib_1.__importStar(require("@babel/parser"));
+const traverse_1 = tslib_1.__importDefault(require("@babel/traverse"));
+const t = tslib_1.__importStar(require("@babel/types"));
+const generator_1 = tslib_1.__importDefault(require("@babel/generator"));
+const utils_1 = require("../utils");
+function obfuscateJsWithAst(code, selectorConversion, startingKeys = [], stripUnnecessarySpace = true) {
+ const ast = parser.parse(code, { sourceType: "module", plugins: ["jsx"] });
+ const usedKeys = new Set();
+ (0, traverse_1.default)(ast, {
+ ObjectProperty(path) {
+ if (t.isIdentifier(path.node.key) && path.node.key.name === "className") {
+ searchStringLiterals(path.get("value"), (str) => {
+ if (startingKeys.length > 0 && !startingKeys.includes(str)) {
+ return str;
+ }
+ if (!selectorConversion) {
+ return "{{obfuscated}}";
+ }
+ str = stripUnnecessarySpace ? str.replace(/\s+/g, " ").trim() : str;
+ const { obfuscatedContent, usedKeys: obfuscateUsedKeys } = (0, utils_1.obfuscateKeys)(selectorConversion, str);
+ if (obfuscatedContent !== str) {
+ obfuscateUsedKeys.forEach(key => usedKeys.add(key));
+ return obfuscatedContent;
+ }
+ });
+ }
+ },
+ });
+ const options = {
+ compact: true,
+ concise: true,
+ retainLines: false,
+ comments: false,
+ minified: true,
+ };
+ const obfuscatedCode = (0, generator_1.default)(ast, options, code);
+ return {
+ obfuscatedCode: obfuscatedCode.code,
+ usedKeys: usedKeys
+ };
+}
+exports.obfuscateJsWithAst = obfuscateJsWithAst;
+function searchStringLiterals(path, callback, scannedNodes = new Set()) {
+ if (path.node && scannedNodes.has(path.node)) {
+ return;
+ }
+ scannedNodes.add(path.node);
+ if (t.isBlockStatement(path.node)) {
+ const body = path.get("body");
+ if (Array.isArray(body)) {
+ body.forEach(nodePath => {
+ switch (nodePath.node.type) {
+ case "ReturnStatement":
+ case "IfStatement":
+ case "SwitchStatement":
+ case "ExpressionStatement":
+ case "ForStatement":
+ case "WhileStatement":
+ case "TryStatement":
+ searchStringLiterals(nodePath, callback, scannedNodes);
+ break;
+ }
+ });
+ }
+ else {
+ searchStringLiterals(body, callback, scannedNodes);
+ }
+ }
+ else if (t.isReturnStatement(path.node)) {
+ const argument = path.get("argument");
+ if (argument && !Array.isArray(argument)) {
+ searchStringLiterals(argument, callback);
+ }
+ else if (Array.isArray(argument)) {
+ argument.forEach(arg => {
+ searchStringLiterals(arg, callback, scannedNodes);
+ });
+ }
+ }
+ else if (t.isBinaryExpression(path.node)) {
+ const left = path.get("left");
+ const right = path.get("right");
+ if (left && !Array.isArray(left)) {
+ searchStringLiterals(left, callback, scannedNodes);
+ }
+ if (right && !Array.isArray(right)) {
+ searchStringLiterals(right, callback, scannedNodes);
+ }
+ }
+ else if (t.isStringLiteral(path.node)) {
+ const replacement = callback(path.node.value);
+ if (replacement) {
+ path.replaceWith(t.stringLiteral(replacement));
+ }
+ }
+ else if (t.isCallExpression(path.node)) {
+ const callee = path.get("callee");
+ if (callee && !Array.isArray(callee)) {
+ searchStringLiterals(callee, callback, scannedNodes);
+ }
+ const args = path.get("arguments");
+ if (Array.isArray(args)) {
+ args.forEach(arg => {
+ if (t.isStringLiteral(arg.node)) {
+ const replacement = callback(arg.node.value);
+ if (replacement) {
+ arg.replaceWith(t.stringLiteral(replacement));
+ }
+ }
+ else {
+ searchStringLiterals(arg, callback, scannedNodes);
+ }
+ });
+ }
+ }
+ else if (t.isConditionalExpression(path.node)) {
+ const test = path.get("test");
+ const consequent = path.get("consequent");
+ const alternate = path.get("alternate");
+ if (test && !Array.isArray(test)) {
+ searchStringLiterals(test, callback, scannedNodes);
+ }
+ if (consequent && !Array.isArray(consequent)) {
+ searchStringLiterals(consequent, callback, scannedNodes);
+ }
+ if (alternate && !Array.isArray(alternate)) {
+ searchStringLiterals(alternate, callback, scannedNodes);
+ }
+ }
+ else if (t.isIfStatement(path.node)) {
+ const test = path.get("test");
+ const consequent = path.get("consequent");
+ const alternate = path.get("alternate");
+ if (test && !Array.isArray(test)) {
+ searchStringLiterals(test, callback, scannedNodes);
+ }
+ if (consequent && !Array.isArray(consequent)) {
+ searchStringLiterals(consequent, callback, scannedNodes);
+ }
+ if (alternate && !Array.isArray(alternate)) {
+ searchStringLiterals(alternate, callback, scannedNodes);
+ }
+ }
+ else if (t.isObjectExpression(path.node)) {
+ const properties = path.get("properties");
+ if (Array.isArray(properties)) {
+ properties.forEach(prop => {
+ searchStringLiterals(prop, callback, scannedNodes);
+ });
+ }
+ }
+ else if (t.isObjectProperty(path.node)) {
+ const value = path.get("value");
+ if (value && !Array.isArray(value)) {
+ searchStringLiterals(value, callback, scannedNodes);
+ }
+ }
+ else if (t.isArrayExpression(path.node)) {
+ const elements = path.get("elements");
+ if (Array.isArray(elements)) {
+ elements.forEach(element => {
+ searchStringLiterals(element, callback, scannedNodes);
+ });
+ }
+ }
+ else if (t.isSwitchStatement(path.node)) {
+ const cases = path.get("cases");
+ if (Array.isArray(cases)) {
+ cases.forEach(c => {
+ searchStringLiterals(c, callback, scannedNodes);
+ });
+ }
+ }
+ else if (t.isSwitchCase(path.node)) {
+ const consequent = path.get("consequent");
+ if (Array.isArray(consequent)) {
+ consequent.forEach(c => {
+ if (t.isReturnStatement(c.node)) {
+ searchStringLiterals(c, callback, scannedNodes);
+ }
+ });
+ }
+ }
+ else if (t.isFunctionDeclaration(path.node)) {
+ const body = path.get("body");
+ if (body && !Array.isArray(body)) {
+ searchStringLiterals(body, callback, scannedNodes);
+ }
+ }
+ else if (t.isForStatement(path.node)) {
+ const body = path.get("body");
+ if (body && !Array.isArray(body)) {
+ searchStringLiterals(body, callback, scannedNodes);
+ }
+ }
+ else if (t.isExpressionStatement(path.node)) {
+ const expression = path.get("expression");
+ if (expression && !Array.isArray(expression)) {
+ searchStringLiterals(expression, callback, scannedNodes);
+ }
+ }
+ else if (t.isAssignmentExpression(path.node)) {
+ const right = path.get("right");
+ if (right && !Array.isArray(right)) {
+ searchStringLiterals(right, callback, scannedNodes);
+ }
+ }
+ else if (t.isWhileStatement(path.node)) {
+ const body = path.get("body");
+ if (body && !Array.isArray(body)) {
+ searchStringLiterals(body, callback, scannedNodes);
+ }
+ }
+ else if (t.isSpreadElement(path.node)) {
+ const argument = path.get("argument");
+ if (argument && !Array.isArray(argument)) {
+ searchStringLiterals(argument, callback, scannedNodes);
+ }
+ }
+ else if (t.isArrowFunctionExpression(path.node)) {
+ const body = path.get("body");
+ if (body && !Array.isArray(body)) {
+ searchStringLiterals(body, callback, scannedNodes);
+ }
+ }
+ else if (t.isTryStatement(path.node)) {
+ const block = path.get("block");
+ const handler = path.get("handler");
+ if (block && !Array.isArray(block)) {
+ searchStringLiterals(block, callback, scannedNodes);
+ }
+ if (handler && !Array.isArray(handler)) {
+ const handlerBody = handler.get("body");
+ if (handlerBody && !Array.isArray(handlerBody)) {
+ searchStringLiterals(handlerBody, callback, scannedNodes);
+ }
+ }
+ }
+ else {
+ path.traverse({
+ Identifier(innerPath) {
+ searchStringLiterals(innerPath, callback, scannedNodes);
+ },
+ });
+ }
+ return path;
+}
+exports.searchStringLiterals = searchStringLiterals;
diff --git a/dist/handlers/js-ast.test.d.ts b/dist/handlers/js-ast.test.d.ts
new file mode 100644
index 0000000..cb0ff5c
--- /dev/null
+++ b/dist/handlers/js-ast.test.d.ts
@@ -0,0 +1 @@
+export {};
diff --git a/dist/handlers/js-ast.test.js b/dist/handlers/js-ast.test.js
new file mode 100644
index 0000000..6c1c546
--- /dev/null
+++ b/dist/handlers/js-ast.test.js
@@ -0,0 +1,1156 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const tslib_1 = require("tslib");
+const vitest_1 = require("vitest");
+const traverse_1 = tslib_1.__importDefault(require("@babel/traverse"));
+const parser = tslib_1.__importStar(require("@babel/parser"));
+const generator_1 = tslib_1.__importDefault(require("@babel/generator"));
+const js_ast_1 = require("./js-ast");
+function stripCode(code) {
+ return code.replace(/\s/g, "");
+}
+(0, vitest_1.describe)("searchStringLiterals", () => {
+ function findStartPointNode(ast) {
+ let startPointNode;
+ (0, traverse_1.default)(ast, {
+ FunctionDeclaration(path) {
+ var _a;
+ if (((_a = path.node.id) === null || _a === void 0 ? void 0 : _a.name) === "startPoint") {
+ startPointNode = path.get('body');
+ }
+ },
+ });
+ return startPointNode;
+ }
+ (0, vitest_1.it)("should handle string literals correctly", () => {
+ const code = `
+ const a = "test";
+
+ function startPoint() {
+ return "function";
+ }
+ `;
+ const expectedCode = `
+ const a = "test";
+
+ function startPoint() {
+ return "{{found}}";
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["function"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should not handle string literals in comments", () => {
+ const code = `
+ // const a = "test";
+
+ function startPoint() {
+ return "function";
+ }
+ `;
+ const expectedCode = `
+ // const a = "test";
+
+ function startPoint() {
+ return "{{found}}";
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["function"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle variable declarations correctly", () => {
+ const code = `
+ const a = "test";
+ let b = "test2";
+ var c = "test3";
+
+ function startPoint() {
+ const d = "test4";
+ let e = "test5";
+ var f = "test6";
+ return a + b + c + d + e + f;
+ }
+ `;
+ const expectedCode = `
+ const a = "{{found}}";
+ let b = "{{found}}";
+ var c = "{{found}}";
+
+ function startPoint() {
+ const d = "{{found}}";
+ let e = "{{found}}";
+ var f = "{{found}}";
+ return a + b + c + d + e + f;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["test", "test2", "test3", "test4", "test5", "test6"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle variable declarations with multiple variables correctly", () => {
+ const code = `
+ const a = "test", b = "test2";
+
+ function startPoint() {
+ return a + b;
+ }
+ `;
+ const expectedCode = `
+ const a = "{{found}}", b = "{{found}}";
+
+ function startPoint() {
+ return a + b;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["test", "test2"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle local variables correctly", () => {
+ const code = `
+ const globalVar = "out";
+
+ function startPoint() {
+ const localVar = "world";
+ return "hello" + localVar;
+ }
+ `;
+ const expectedCode = `
+ const globalVar = "out";
+
+ function startPoint() {
+ const localVar = "{{found}}";
+ return "{{found}}" + localVar;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["hello", "world"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle global variables correctly", () => {
+ const code = `
+ const globalVar = "world";
+
+ function startPoint() {
+ const localVar = "local";
+ return "hello" + globalVar;
+ }
+ `;
+ const expectedCode = `
+ const globalVar = "{{found}}";
+
+ function startPoint() {
+ const localVar = "local";
+ return "{{found}}" + globalVar;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["hello", "world"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle boolean expressions correctly", () => {
+ const code = `
+ const globalVar = "world";
+
+ function startPoint() {
+ const localBool = true;
+ const localVar = "again";
+ return "hello" + (localBool ? globalVar : localVar);
+ }
+ `;
+ const expectedCode = `
+ const globalVar = "{{found}}";
+
+ function startPoint() {
+ const localBool = true;
+ const localVar = "{{found}}";
+ return "{{found}}" + (localBool ? globalVar : localVar);
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["hello", "world", "again"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle if statements correctly", () => {
+ const code = `
+ const globalVar = "world";
+
+ function startPoint() {
+ const localBool = true;
+ const localVar = "again";
+ if (localBool) {
+ return "hello" + globalVar;
+ }
+ return "hello" + localVar;
+ }
+ `;
+ const expectedCode = `
+ const globalVar = "{{found}}";
+
+ function startPoint() {
+ const localBool = true;
+ const localVar = "{{found}}";
+ if (localBool) {
+ return "{{found}}" + globalVar;
+ }
+ return "{{found}}" + localVar;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["hello", "world", "hello", "again"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle function calls correctly", () => {
+ const code = `
+ const globalVar = "global";
+
+ function call() {
+ return "world";
+ }
+
+ function startPoint() {
+ return "hello" + globalVar + call();
+ }
+ `;
+ const expectedCode = `
+ const globalVar = "{{found}}";
+
+ function call() {
+ return "{{found}}";
+ }
+
+ function startPoint() {
+ return "{{found}}" + globalVar + call();
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["hello", "global", "world"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle function calls with arguments correctly", () => {
+ const code = `
+ const globalVar = "global";
+
+ function call(arg) {
+ return arg;
+ }
+
+ function startPoint() {
+ return "hello" + globalVar + call("world");
+ }
+ `;
+ const expectedCode = `
+ const globalVar = "{{found}}";
+
+ function call(arg) {
+ return arg;
+ }
+
+ function startPoint() {
+ return "{{found}}" + globalVar + call("{{found}}");
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["hello", "global", "world"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle function calls with multiple arguments correctly", () => {
+ const code = `
+ const globalVar = "global";
+
+ function call(arg, arg2) {
+ return arg + arg2;
+ }
+
+ function startPoint() {
+ return "hello" + globalVar + call("world", "again");
+ }
+ `;
+ const expectedCode = `
+ const globalVar = "{{found}}";
+
+ function call(arg, arg2) {
+ return arg + arg2;
+ }
+
+ function startPoint() {
+ return "{{found}}" + globalVar + call("{{found}}", "{{found}}");
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["hello", "global", "world", "again"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle function calls with nested function calls correctly", () => {
+ const code = `
+ const globalVar = "global";
+
+ function call(arg, arg2) {
+ return arg + arg2;
+ }
+
+ function call2(arg) {
+ return arg;
+ }
+
+ function startPoint() {
+ return "hello" + globalVar + call(call2("world"), "again");
+ }
+ `;
+ const expectedCode = `
+ const globalVar = "{{found}}";
+
+ function call(arg, arg2) {
+ return arg + arg2;
+ }
+
+ function call2(arg) {
+ return arg;
+ }
+
+ function startPoint() {
+ return "{{found}}" + globalVar + call(call2("{{found}}"), "{{found}}");
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["hello", "global", "world", "again"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle function calls with boolean expressions correctly", () => {
+ const code = `
+ const globalVar = "global";
+
+ function call(arg, arg2) {
+ return arg + arg2;
+ }
+
+ function call2(arg) {
+ return arg;
+ }
+
+ function startPoint() {
+ const bool = true;
+ return "hello" + globalVar + call(bool ? "world1" : call2("world2"), "again");
+ }
+ `;
+ const expectedCode = `
+ const globalVar = "{{found}}";
+
+ function call(arg, arg2) {
+ return arg + arg2;
+ }
+
+ function call2(arg) {
+ return arg;
+ }
+
+ function startPoint() {
+ const bool = true;
+ return "{{found}}" + globalVar + call(bool ? "{{found}}" : call2("{{found}}"), "{{found}}");
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["hello", "global", "world1", "world2", "again"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle arrow function calls correctly", () => {
+ const code = `
+ const globalVar = "global";
+
+ const call = (arg, arg2) => {
+ return arg + arg2;
+ };
+
+ function startPoint() {
+ return "hello" + globalVar + call("world", "again");
+ }
+ `;
+ const expectedCode = `
+ const globalVar = "{{found}}";
+
+ const call = (arg, arg2) => {
+ return arg + arg2;
+ };
+
+ function startPoint() {
+ return "{{found}}" + globalVar + call("{{found}}", "{{found}}");
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["hello", "global", "world", "again"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle document.{method} correctly", () => {
+ const code = `
+ function startPoint() {
+ return document.getElementById("element");
+ }
+ `;
+ const expectedCode = `
+ function startPoint() {
+ return document.getElementById("{{found}}");
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["element"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle document.{method} with variables correctly", () => {
+ const code = `
+ function startPoint() {
+ const id = "element";
+ return document.getElementById(id);
+ }
+ `;
+ const expectedCode = `
+ function startPoint() {
+ const id = "{{found}}";
+ return document.getElementById(id);
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["element"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle object expressions correctly", () => {
+ const code = `
+ function startPoint() {
+ const obj = {
+ key: "value"
+ };
+ return obj.key;
+ }
+ `;
+ const expectedCode = `
+ function startPoint() {
+ const obj = {
+ key: "{{found}}"
+ };
+ return obj.key;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["value"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle object expressions' operations correctly", () => {
+ const code = `
+ function startPoint() {
+ const obj = {
+ key: "value"
+ };
+ obj.key = "another";
+ obj["key2"] = "another2";
+ return obj.key;
+ }
+ `;
+ const expectedCode = `
+ function startPoint() {
+ const obj = {
+ key: "{{found}}"
+ };
+ obj.key = "{{found}}";
+ obj["key2"] = "{{found}}";
+ return obj.key;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["another", "another2", "value"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle object expressions with variables correctly", () => {
+ const code = `
+ function startPoint() {
+ const value = "value";
+ const obj = {
+ key: value
+ };
+ return obj.key;
+ }
+ `;
+ const expectedCode = `
+ function startPoint() {
+ const value = "{{found}}";
+ const obj = {
+ key: value
+ };
+ return obj.key;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["value"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle object expressions with function calls correctly", () => {
+ const code = `
+ function call() {
+ return "call";
+ }
+
+ function startPoint() {
+ const obj = {
+ key: call()
+ };
+ return obj.key;
+ }
+ `;
+ const expectedCode = `
+ function call() {
+ return "{{found}}";
+ }
+
+ function startPoint() {
+ const obj = {
+ key: call()
+ };
+ return obj.key;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["call"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle object expressions with function calls with arguments correctly", () => {
+ const code = `
+ function call(arg) {
+ return arg;
+ }
+
+ function startPoint() {
+ const obj = {
+ key: call("call")
+ };
+ return obj.key;
+ }
+ `;
+ const expectedCode = `
+ function call(arg) {
+ return arg;
+ }
+
+ function startPoint() {
+ const obj = {
+ key: call("{{found}}")
+ };
+ return obj.key;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["call"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle object expressions with function calls with boolean expressions correctly", () => {
+ const code = `
+ function call(arg) {
+ return arg;
+ }
+
+ function startPoint() {
+ const value = "value";
+ const bool = true;
+ const obj = {
+ key: call(bool ? value : "another")
+ };
+ return obj.key;
+ }
+ `;
+ const expectedCode = `
+ function call(arg) {
+ return arg;
+ }
+
+ function startPoint() {
+ const value = "{{found}}";
+ const bool = true;
+ const obj = {
+ key: call(bool ? value : "{{found}}")
+ };
+ return obj.key;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["value", "another"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle object expressions with a variable key correctly", () => {
+ const code = `
+ function call(arg) {
+ return arg;
+ }
+
+ function startPoint() {
+ const key = "value";
+ const bool = true;
+ const obj = {
+ key: call(bool ? key : "another")
+ };
+ return obj.key;
+ }
+ `;
+ const expectedCode = `
+ function call(arg) {
+ return arg;
+ }
+
+ function startPoint() {
+ const key = "{{found}}";
+ const bool = true;
+ const obj = {
+ key: call(bool ? key : "{{found}}")
+ };
+ return obj.key;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["value", "another", "{{found}}"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle array expressions correctly", () => {
+ const code = `
+ function startPoint() {
+ const arr = ["element_1", "element_2"];
+ return arr[0];
+ }
+ `;
+ const expectedCode = `
+ function startPoint() {
+ const arr = ["{{found}}", "{{found}}"];
+ return arr[0];
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["element_1", "element_2"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle array concatenation correctly and no duplicates node should be scaned", () => {
+ const code = `
+ function startPoint() {
+ const arr = ["element_1"];
+ const arr2 = ["element_2"];
+ const arr3 = arr.concat(arr2);
+ const arr4 = [...arr, ...arr3];
+ return arr4;
+ }
+ `;
+ const expectedCode = `
+ function startPoint() {
+ const arr = ["{{found}}"];
+ const arr2 = ["{{found}}"];
+ const arr3 = arr.concat(arr2);
+ const arr4 = [...arr, ...arr3];
+ return arr4;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["element_1", "element_2"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle array expressions with variables correctly", () => {
+ const code = `
+ function startPoint() {
+ const element1 = "element_1";
+ const element2 = "element_2";
+ const arr = [element1, element2];
+ return arr[0];
+ }
+ `;
+ const expectedCode = `
+ function startPoint() {
+ const element1 = "{{found}}";
+ const element2 = "{{found}}";
+ const arr = [element1, element2];
+ return arr[0];
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["element_1", "element_2"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle array expressions with function calls correctly", () => {
+ const code = `
+ function call() {
+ return "element_1";
+ }
+
+ function startPoint() {
+ const arr = [call(), "element_2"];
+ return arr[0];
+ }
+ `;
+ const expectedCode = `
+ function call() {
+ return "{{found}}";
+ }
+
+ function startPoint() {
+ const arr = [call(), "{{found}}"];
+ return arr[0];
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["element_1", "element_2"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle array expressions with function calls with arguments correctly", () => {
+ const code = `
+ function call(arg) {
+ return arg;
+ }
+
+ function startPoint() {
+ const arr = [call("element_1"), "element_2"];
+ return arr[0];
+ }
+ `;
+ const expectedCode = `
+ function call(arg) {
+ return arg;
+ }
+
+ function startPoint() {
+ const arr = [call("{{found}}"), "{{found}}"];
+ return arr[0];
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["element_1", "element_2"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle array expressions with function calls with boolean expressions correctly", () => {
+ const code = `
+ function call(arg) {
+ return arg;
+ }
+
+ function startPoint() {
+ const key = "value";
+ const bool = true;
+ const arr = [call(bool ? key : "another"), "element_2"];
+ return arr[0];
+ }
+ `;
+ const expectedCode = `
+ function call(arg) {
+ return arg;
+ }
+
+ function startPoint() {
+ const key = "{{found}}";
+ const bool = true;
+ const arr = [call(bool ? key : "{{found}}"), "{{found}}"];
+ return arr[0];
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["value", "another", "element_2"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle switch statements correctly", () => {
+ const code = `
+ function startPoint() {
+ const key = "value";
+ let result = "";
+ switch (key) {
+ case "value":
+ const fakeVar = "fake";
+ result = "value";
+ case "another":
+ const fakeVar2 = "fake2";
+ return "another";
+ default:
+ const fakeVar3 = "fake3";
+ return "default";
+ }
+ }
+ `;
+ const expectedCode = `
+ function startPoint() {
+ const key = "value";
+ let result = "";
+ switch (key) {
+ case "value":
+ const fakeVar = "fake";
+ result = "value";
+ case "another":
+ const fakeVar2 = "fake2";
+ return "{{found}}";
+ default:
+ const fakeVar3 = "fake3";
+ return "{{found}}";
+ }
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["another", "default"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle for statements correctly", () => {
+ const code = `
+ function startPoint() {
+ const arr = ["element_1", "element_2"];
+ const fakeArr = ["element_3", "element_4"];
+ let result = "result";
+ for (let i = 0; i < arr.length; i++) {
+ const fakeArr2 = ["element_5", "element_6"];
+ result += arr[i];
+ }
+ return result;
+ }
+ `;
+ const expectedCode = `
+ function startPoint() {
+ const arr = ["{{found}}", "{{found}}"];
+ const fakeArr = ["element_3", "element_4"];
+ let result = "{{found}}";
+ for (let i = 0; i < arr.length; i++) {
+ const fakeArr2 = ["element_5", "element_6"];
+ result += arr[i];
+ }
+ return result;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["element_1", "element_2", "result"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle foreach statements correctly", () => {
+ const code = `
+ function startPoint() {
+ const arr = ["element_1", "element_2"];
+ let result = "result";
+ arr.forEach((element) => {
+ const fakeVar = "fake";
+ result += element;
+ });
+ return result;
+ }
+ `;
+ const expectedCode = `
+ function startPoint() {
+ const arr = ["{{found}}", "{{found}}"];
+ let result = "{{found}}";
+ arr.forEach(element => {
+ const fakeVar = "fake";
+ result += element;
+ });
+ return result;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["element_1", "element_2", "result"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle map statements correctly", () => {
+ const code = `
+ function startPoint() {
+ const arr = ["element_1", "element_2"];
+ const result = arr.map((element) => {
+ const fakeVar = "fake";
+ return element;
+ });
+ return result;
+ }
+ `;
+ const expectedCode = `
+ function startPoint() {
+ const arr = ["{{found}}", "{{found}}"];
+ const result = arr.map(element => {
+ const fakeVar = "fake";
+ return element;
+ });
+ return result;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["element_1", "element_2"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle while statements correctly", () => {
+ const code = `
+ function startPoint() {
+ const arr = ["element_1", "element_2"];
+ const fakeArr = ["element_3", "element_4"];
+ let result = "result";
+ let i = 0;
+ while (i < arr.length) {
+ const fakeArr2 = ["element_5", "element_6"];
+ result += arr[i];
+ i++;
+ }
+ return result;
+ }
+ `;
+ const expectedCode = `
+ function startPoint() {
+ const arr = ["{{found}}", "{{found}}"];
+ const fakeArr = ["element_3", "element_4"];
+ let result = "{{found}}";
+ let i = 0;
+ while (i < arr.length) {
+ const fakeArr2 = ["element_5", "element_6"];
+ result += arr[i];
+ i++;
+ }
+ return result;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["element_1", "element_2", "result"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+ (0, vitest_1.it)("should handle try catch statements correctly", () => {
+ const code = `
+ function startPoint() {
+ const arr = ["element_1", "element_2"];
+ const arr2 = ["element_3", "element_4"];
+ let result = "result";
+ try {
+ result = arr[0];
+ throw new Error("error");
+ } catch (e) {
+ result = arr2[0];
+ result = e.message;
+ }
+ return result;
+ }
+ `;
+ const expectedCode = `
+ function startPoint() {
+ const arr = ["{{found}}", "{{found}}"];
+ const arr2 = ["{{found}}", "{{found}}"];
+ let result = "{{found}}";
+ try {
+ result = arr[0];
+ throw new Error("error");
+ } catch (e) {
+ result = arr2[0];
+ result = e.message;
+ }
+ return result;
+ }
+ `;
+ const ast = parser.parse(code);
+ let result = [];
+ (0, js_ast_1.searchStringLiterals)(findStartPointNode(ast), (str) => {
+ result.push(str);
+ return "{{found}}";
+ });
+ const expected = ["element_1", "element_2", "element_3", "element_4", "result"];
+ (0, vitest_1.expect)(result).toEqual(expected);
+ (0, vitest_1.expect)(stripCode((0, generator_1.default)(ast, {}, code).code)).toEqual(stripCode(expectedCode));
+ });
+});
+(0, vitest_1.describe)("obfuscateJsWithAst", () => {
+ (0, vitest_1.it)("should handle basic real world example 1", () => {
+ 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{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{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:''});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{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{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{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{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:\"\"});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{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{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()}]);`;
+ const { obfuscatedCode } = (0, js_ast_1.obfuscateJsWithAst)(code, undefined);
+ (0, vitest_1.expect)(stripCode(obfuscatedCode)).toEqual(stripCode(expectedCode));
+ });
+});
diff --git a/dist/handlers/js.d.ts b/dist/handlers/js.d.ts
new file mode 100644
index 0000000..90f501f
--- /dev/null
+++ b/dist/handlers/js.d.ts
@@ -0,0 +1,9 @@
+import { SelectorConversion } from "../types";
+declare function searchForwardComponent(content: string): never[] | RegExpMatchArray;
+declare function obfuscateForwardComponentJs(searchContent: string, wholeContent: string, selectorConversion: SelectorConversion): {
+ name: string;
+ componentCode: string;
+ componentObfuscatedCode: string;
+}[];
+declare function obfuscateJs(content: string, key: string, selectorCoversion: SelectorConversion, filePath: string, contentIgnoreRegexes?: RegExp[], useAst?: boolean): string;
+export { obfuscateForwardComponentJs, obfuscateJs, searchForwardComponent, };
diff --git a/dist/handlers/js.js b/dist/handlers/js.js
new file mode 100644
index 0000000..2695413
--- /dev/null
+++ b/dist/handlers/js.js
@@ -0,0 +1,106 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.searchForwardComponent = exports.obfuscateJs = exports.obfuscateForwardComponentJs = void 0;
+const utils_1 = require("../utils");
+const js_ast_1 = require("./js-ast");
+function searchForwardComponent(content) {
+ const componentSearchRegex = /(?<=\.jsx\()[^,|"|']+/g;
+ const match = content.match(componentSearchRegex);
+ if (match) {
+ return match;
+ }
+ return [];
+}
+exports.searchForwardComponent = searchForwardComponent;
+function searchComponent(content, componentName) {
+ const componentSearchRegex = new RegExp(`\\b(?:const|let|var)\\s+(${componentName})\\s*=\\s*.*?(\\{)`, "g");
+ const match = content.match(componentSearchRegex);
+ let openSymbolPos = -1;
+ if (match) {
+ openSymbolPos = content.indexOf(match[0]) + match[0].length;
+ }
+ const closeMarkerPos = (0, utils_1.findClosestSymbolPosition)(content, "{", "}", openSymbolPos, "forward");
+ const componentContent = content.slice(openSymbolPos, closeMarkerPos);
+ return componentContent;
+}
+function obfuscateForwardComponentJs(searchContent, wholeContent, selectorConversion) {
+ const componentNames = searchForwardComponent(searchContent).filter((componentName) => {
+ return !componentName.includes(".");
+ });
+ const componentsCode = componentNames.map(componentName => {
+ const componentContent = searchComponent(wholeContent, componentName);
+ return {
+ name: componentName,
+ code: componentContent
+ };
+ });
+ const componentsObfuscatedCode = componentsCode.map((componentContent) => {
+ const classNameBlocks = (0, utils_1.findContentBetweenMarker)(componentContent.code, "className:", "{", "}");
+ const obfuscatedClassNameBlocks = classNameBlocks.map(block => {
+ const { obfuscatedContent, usedKeys } = (0, utils_1.obfuscateKeys)(selectorConversion, block);
+ (0, utils_1.addKeysToRegistery)(usedKeys);
+ return obfuscatedContent;
+ });
+ if (classNameBlocks.length !== obfuscatedClassNameBlocks.length) {
+ (0, utils_1.log)("error", `Component obfuscation:`, `classNameBlocks.length !== obfuscatedClassNameBlocks.length`);
+ return componentContent;
+ }
+ let obscuredCode = componentContent.code;
+ for (let i = 0; i < classNameBlocks.length; i++) {
+ obscuredCode = (0, utils_1.replaceFirstMatch)(obscuredCode, classNameBlocks[i], obfuscatedClassNameBlocks[i]);
+ }
+ (0, utils_1.log)("debug", `Obscured keys in component:`, componentContent.name);
+ return {
+ name: componentContent.name,
+ code: obscuredCode
+ };
+ });
+ const componentObfuscatedcomponentCodePairs = [];
+ for (let i = 0; i < componentsCode.length; i++) {
+ if (componentsCode[i] !== componentsObfuscatedCode[i]) {
+ componentObfuscatedcomponentCodePairs.push({
+ name: componentsCode[i].name,
+ componentCode: componentsCode[i].code,
+ componentObfuscatedCode: componentsObfuscatedCode[i].code
+ });
+ }
+ }
+ for (let i = 0; i < componentsCode.length; i++) {
+ const childComponentObfuscatedcomponentCodePairs = obfuscateForwardComponentJs(componentsCode[i].code, wholeContent, selectorConversion);
+ componentObfuscatedcomponentCodePairs.push(...childComponentObfuscatedcomponentCodePairs);
+ }
+ return componentObfuscatedcomponentCodePairs;
+}
+exports.obfuscateForwardComponentJs = obfuscateForwardComponentJs;
+function obfuscateJs(content, key, selectorCoversion, filePath, contentIgnoreRegexes = [], useAst = false) {
+ if (useAst) {
+ try {
+ const { obfuscatedCode, usedKeys } = (0, js_ast_1.obfuscateJsWithAst)(content, selectorCoversion, key ? [key] : [], true);
+ (0, utils_1.addKeysToRegistery)(usedKeys);
+ if (content !== obfuscatedCode) {
+ (0, utils_1.log)("debug", `Obscured keys with AST and marker "${key}":`, `${(0, utils_1.normalizePath)(filePath)}`);
+ }
+ return obfuscatedCode;
+ }
+ catch (error) {
+ if (error instanceof SyntaxError) {
+ (0, utils_1.log)("warn", "Syntax error ignored:", error);
+ (0, utils_1.log)("warn", "Obfuscation with AST failed:", "Falling back to regex obfuscation");
+ }
+ else {
+ throw error;
+ }
+ }
+ }
+ const truncatedContents = (0, utils_1.findContentBetweenMarker)(content, key, "{", "}");
+ truncatedContents.forEach((truncatedContent) => {
+ const { obfuscatedContent, usedKeys } = (0, utils_1.obfuscateKeys)(selectorCoversion, truncatedContent, contentIgnoreRegexes);
+ (0, utils_1.addKeysToRegistery)(usedKeys);
+ if (truncatedContent !== obfuscatedContent) {
+ content = content.replace(truncatedContent, obfuscatedContent);
+ (0, utils_1.log)("debug", `Obscured keys with marker "${key}":`, `${(0, utils_1.normalizePath)(filePath)}`);
+ }
+ });
+ return content;
+}
+exports.obfuscateJs = obfuscateJs;
diff --git a/dist/handlers/js.test.d.ts b/dist/handlers/js.test.d.ts
new file mode 100644
index 0000000..cb0ff5c
--- /dev/null
+++ b/dist/handlers/js.test.d.ts
@@ -0,0 +1 @@
+export {};
diff --git a/dist/handlers/js.test.js b/dist/handlers/js.test.js
new file mode 100644
index 0000000..58d9483
--- /dev/null
+++ b/dist/handlers/js.test.js
@@ -0,0 +1,60 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const vitest_1 = require("vitest");
+const js_1 = require("./js");
+(0, vitest_1.describe)("searchForwardComponent", () => {
+ (0, vitest_1.test)("should return component name when jsx format is correct", () => {
+ const content = `const element = o.jsx(ComponentName, {data: dataValue, index: "date"});`;
+ const result = (0, js_1.searchForwardComponent)(content);
+ (0, vitest_1.expect)(result).toEqual(["ComponentName"]);
+ });
+ (0, vitest_1.test)("should return multiple component names for multiple matches", () => {
+ const content = `o.jsx(FirstComponent, props); o.jsx(SecondComponent, otherProps);`;
+ const result = (0, js_1.searchForwardComponent)(content);
+ (0, vitest_1.expect)(result).toEqual(["FirstComponent", "SecondComponent"]);
+ });
+ (0, vitest_1.test)("should return an empty array when no component name is found", () => {
+ const content = `o.jsx("h1", {data: dataValue, index: "date"});`;
+ const result = (0, js_1.searchForwardComponent)(content);
+ (0, vitest_1.expect)(result).toEqual([]);
+ });
+ (0, vitest_1.test)("should return an empty array when content is empty", () => {
+ const content = "";
+ const result = (0, js_1.searchForwardComponent)(content);
+ (0, vitest_1.expect)(result).toEqual([]);
+ });
+ (0, vitest_1.test)("should return an empty array when jsx is not used", () => {
+ const content = `const element = React.createElement("div", null, "Hello World");`;
+ const result = (0, js_1.searchForwardComponent)(content);
+ (0, vitest_1.expect)(result).toEqual([]);
+ });
+ (0, vitest_1.test)("should handle special characters in component names", () => {
+ const content = `o.jsx($Comp_1, props); o.jsx(_Comp$2, otherProps);`;
+ const result = (0, js_1.searchForwardComponent)(content);
+ (0, vitest_1.expect)(result).toEqual(["$Comp_1", "_Comp$2"]);
+ });
+ (0, vitest_1.test)("should not return component names when they are quoted", () => {
+ const content = `o.jsx("ComponentName", props); o.jsx('AnotherComponent', otherProps);`;
+ const result = (0, js_1.searchForwardComponent)(content);
+ (0, vitest_1.expect)(result).toEqual([]);
+ });
+ (0, vitest_1.test)("should return component names when they are followed by a brace", () => {
+ const content = `o.jsx(ComponentName, {props: true});`;
+ const result = (0, js_1.searchForwardComponent)(content);
+ (0, vitest_1.expect)(result).toEqual(["ComponentName"]);
+ });
+ (0, vitest_1.test)("should handle content with line breaks and multiple jsx calls", () => {
+ const content = `
+ o.jsx(FirstComponent, {data: dataValue});
+ o.jsx(SecondComponent, {index: "date"});
+ o.jsx(ThirdComponent, {flag: true});
+ `;
+ const result = (0, js_1.searchForwardComponent)(content);
+ (0, vitest_1.expect)(result).toEqual(["FirstComponent", "SecondComponent", "ThirdComponent"]);
+ });
+ (0, vitest_1.test)("should handle content with nested jsx calls", () => {
+ const content = `o.jsx(ParentComponent, {children: o.jsx(ChildComponent, {})})`;
+ const result = (0, js_1.searchForwardComponent)(content);
+ (0, vitest_1.expect)(result).toEqual(["ParentComponent", "ChildComponent"]);
+ });
+});
diff --git a/dist/index.d.ts b/dist/index.d.ts
new file mode 100644
index 0000000..a06a36e
--- /dev/null
+++ b/dist/index.d.ts
@@ -0,0 +1,3 @@
+import { OptionalOptions } from "./types";
+declare function obfuscateCli(): void;
+export { obfuscateCli, type OptionalOptions as Options };
diff --git a/dist/index.js b/dist/index.js
new file mode 100644
index 0000000..7f3ad28
--- /dev/null
+++ b/dist/index.js
@@ -0,0 +1,85 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.obfuscateCli = void 0;
+const tslib_1 = require("tslib");
+const fs_1 = tslib_1.__importDefault(require("fs"));
+const path_1 = tslib_1.__importDefault(require("path"));
+const yargs_1 = tslib_1.__importDefault(require("yargs"));
+const utils_1 = require("./utils");
+const css_1 = require("./handlers/css");
+const config_1 = tslib_1.__importDefault(require("./config"));
+function obfuscate(options) {
+ (0, utils_1.setLogLevel)(options.logLevel);
+ if (!options.enable) {
+ (0, utils_1.log)("info", "Obfuscation", "Obfuscation disabled");
+ return;
+ }
+ const classConversionJsonPaths = (0, utils_1.findAllFilesWithExt)(".json", options.classConversionJsonFolderPath);
+ if (options.refreshClassConversionJson && classConversionJsonPaths.length > 0) {
+ (0, utils_1.log)("info", "Obfuscation", "Refreshing class conversion JSON");
+ for (const jsonPath of classConversionJsonPaths) {
+ fs_1.default.unlinkSync(jsonPath);
+ (0, utils_1.log)("success", "Obfuscation", `Deleted ${jsonPath}`);
+ }
+ }
+ (0, utils_1.log)("info", "Obfuscation", "Creating/Updating class conversion JSON");
+ (0, css_1.createSelectorConversionJson)({
+ selectorConversionJsonFolderPath: options.classConversionJsonFolderPath,
+ buildFolderPath: options.buildFolderPath,
+ mode: options.mode,
+ classNameLength: options.classLength,
+ classPrefix: options.classPrefix,
+ classSuffix: options.classSuffix,
+ classIgnore: options.classIgnore,
+ enableObfuscateMarkerClasses: options.enableMarkers,
+ generatorSeed: options.generatorSeed === "-1" ? undefined : options.generatorSeed,
+ });
+ (0, utils_1.log)("success", "Obfuscation", "Class conversion JSON created/updated");
+ if ((options.includeAnyMatchRegexes && options.includeAnyMatchRegexes.length > 0)
+ || (options.excludeAnyMatchRegexes && options.excludeAnyMatchRegexes.length > 0)) {
+ (0, utils_1.log)("warn", "Obfuscation", "'includeAnyMatchRegexes' and 'excludeAnyMatchRegexes' are deprecated, please use whiteListedFolderPaths and blackListedFolderPaths instead");
+ }
+ (0, utils_1.replaceJsonKeysInFiles)({
+ targetFolder: options.buildFolderPath,
+ allowExtensions: options.allowExtensions,
+ selectorConversionJsonFolderPath: options.classConversionJsonFolderPath,
+ contentIgnoreRegexes: options.contentIgnoreRegexes,
+ whiteListedFolderPaths: [...options.whiteListedFolderPaths, ...(options.includeAnyMatchRegexes || [])],
+ blackListedFolderPaths: [...options.blackListedFolderPaths, ...(options.excludeAnyMatchRegexes || [])],
+ enableObfuscateMarkerClasses: options.enableMarkers,
+ obfuscateMarkerClasses: options.markers,
+ removeObfuscateMarkerClassesAfterObfuscated: options.removeMarkersAfterObfuscated,
+ removeOriginalCss: options.removeOriginalCss,
+ enableJsAst: options.enableJsAst,
+ });
+}
+function obfuscateCli() {
+ const argv = yargs_1.default.option("config", {
+ alias: "c",
+ type: "string",
+ description: "Path to the config file"
+ }).argv;
+ let configPath;
+ if (argv.config) {
+ configPath = path_1.default.resolve(process.cwd(), argv.config);
+ }
+ else {
+ const configFiles = [
+ "next-css-obfuscator.config.ts",
+ "next-css-obfuscator.config.cjs",
+ "next-css-obfuscator.config.mjs",
+ "next-css-obfuscator.config.js",
+ ];
+ for (const file of configFiles) {
+ const potentialPath = path_1.default.join(process.cwd(), file);
+ if (fs_1.default.existsSync(potentialPath)) {
+ configPath = potentialPath;
+ break;
+ }
+ }
+ }
+ const config = new config_1.default(configPath ? require(configPath) : undefined).get();
+ obfuscate(config);
+ (0, utils_1.log)("success", "Obfuscation", "Completed~");
+}
+exports.obfuscateCli = obfuscateCli;
diff --git a/dist/types.d.ts b/dist/types.d.ts
new file mode 100644
index 0000000..a45feb2
--- /dev/null
+++ b/dist/types.d.ts
@@ -0,0 +1,57 @@
+type LogLevel = "debug" | "info" | "warn" | "error" | "success";
+type obfuscateMode = "random" | "simplify" | "simplify-seedable";
+type SelectorConversion = {
+ [key: string]: string;
+};
+type Options = {
+ enable: boolean;
+ mode: obfuscateMode;
+ buildFolderPath: string;
+ classConversionJsonFolderPath: string;
+ refreshClassConversionJson: boolean;
+ classLength: number;
+ classPrefix: string;
+ classSuffix: string;
+ classIgnore: (string | RegExp)[];
+ allowExtensions: string[];
+ contentIgnoreRegexes: RegExp[];
+ whiteListedFolderPaths: (string | RegExp)[];
+ blackListedFolderPaths: (string | RegExp)[];
+ includeAnyMatchRegexes?: RegExp[];
+ excludeAnyMatchRegexes?: RegExp[];
+ enableMarkers: boolean;
+ markers: string[];
+ removeMarkersAfterObfuscated: boolean;
+ removeOriginalCss: boolean;
+ generatorSeed: string;
+ enableJsAst: boolean;
+ logLevel: LogLevel;
+};
+type OptionalOptions = {
+ enable?: boolean;
+ mode?: obfuscateMode;
+ buildFolderPath?: string;
+ classConversionJsonFolderPath?: string;
+ refreshClassConversionJson?: boolean;
+ classLength?: number;
+ classPrefix?: string;
+ classSuffix?: string;
+ classIgnore?: string[];
+ allowExtensions?: string[];
+ contentIgnoreRegexes: RegExp[];
+ whiteListedFolderPaths?: (string | RegExp)[];
+ blackListedFolderPaths?: (string | RegExp)[];
+ includeAnyMatchRegexes?: RegExp[];
+ excludeAnyMatchRegexes?: RegExp[];
+ enableMarkers?: boolean;
+ markers?: string[];
+ removeMarkersAfterObfuscated?: boolean;
+ removeOriginalCss?: boolean;
+ generatorSeed?: string;
+ enableJsAst?: boolean;
+ logLevel?: LogLevel;
+};
+interface HtmlCharacterEntityConversion {
+ [key: string]: string;
+}
+export { type LogLevel, type obfuscateMode, type SelectorConversion, type Options, type OptionalOptions, type HtmlCharacterEntityConversion };
diff --git a/dist/types.js b/dist/types.js
new file mode 100644
index 0000000..c8ad2e5
--- /dev/null
+++ b/dist/types.js
@@ -0,0 +1,2 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
diff --git a/dist/utils.d.ts b/dist/utils.d.ts
new file mode 100644
index 0000000..db2fb0c
--- /dev/null
+++ b/dist/utils.d.ts
@@ -0,0 +1,44 @@
+import { type LogLevel, type SelectorConversion } from "./types";
+declare function log(type: LogLevel, task: string, data: any): void;
+declare function setLogLevel(level: LogLevel): void;
+declare const usedKeyRegistery: Set;
+declare function replaceJsonKeysInFiles({ targetFolder, allowExtensions, selectorConversionJsonFolderPath, contentIgnoreRegexes, whiteListedFolderPaths, blackListedFolderPaths, enableObfuscateMarkerClasses, obfuscateMarkerClasses, removeObfuscateMarkerClassesAfterObfuscated, removeOriginalCss, enableJsAst, }: {
+ targetFolder: string;
+ allowExtensions: string[];
+ selectorConversionJsonFolderPath: string;
+ contentIgnoreRegexes: RegExp[];
+ whiteListedFolderPaths: (string | RegExp)[];
+ blackListedFolderPaths: (string | RegExp)[];
+ enableObfuscateMarkerClasses: boolean;
+ obfuscateMarkerClasses: string[];
+ removeObfuscateMarkerClassesAfterObfuscated: boolean;
+ removeOriginalCss: boolean;
+ enableJsAst: boolean;
+}): void;
+declare function obfuscateKeys(selectorConversion: SelectorConversion, fileContent: string, contentIgnoreRegexes?: RegExp[], useHtmlEntity?: boolean): {
+ obfuscatedContent: string;
+ usedKeys: Set;
+};
+declare function getFilenameFromPath(filePath: string): string;
+declare function normalizePath(filePath: string): string;
+declare function loadAndMergeJsonFiles(jsonFolderPath: string): {
+ [key: string]: any;
+};
+declare function findClosestSymbolPosition(content: string, openMarker: string, closeMarker: string, startPosition?: number, direction?: "forward" | "backward"): number;
+declare function findContentBetweenMarker(content: string, targetStr: string, openMarker: string, closeMarker: string): string[];
+declare function addKeysToRegistery(usedKeys: Set | string[]): void;
+declare function findAllFilesWithExt(ext: string, targetFolderPath: string): string[];
+declare function getRandomString(length: number, seed?: string, rngStateCode?: string, str?: string): {
+ rngStateCode: string;
+ randomString: string;
+};
+declare function seedableSimplifyString(str: string, seed?: string, rngStateCode?: string): {
+ rngStateCode: string;
+ randomString: string;
+};
+declare function simplifyString(alphabetPoistion: number): string;
+declare function replaceFirstMatch(source: string, find: string, replace: string): string;
+declare function duplicationCheck(arr: string[]): boolean;
+declare function createKey(str: string): string;
+declare function decodeKey(str: string): string;
+export { getFilenameFromPath, log, normalizePath, loadAndMergeJsonFiles, replaceJsonKeysInFiles, setLogLevel, findContentBetweenMarker, replaceFirstMatch, findAllFilesWithExt, getRandomString, seedableSimplifyString, usedKeyRegistery, obfuscateKeys, findClosestSymbolPosition, addKeysToRegistery, duplicationCheck, createKey, decodeKey, simplifyString };
diff --git a/dist/utils.js b/dist/utils.js
new file mode 100644
index 0000000..906db97
--- /dev/null
+++ b/dist/utils.js
@@ -0,0 +1,381 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.simplifyString = exports.decodeKey = exports.createKey = exports.duplicationCheck = exports.addKeysToRegistery = exports.findClosestSymbolPosition = exports.obfuscateKeys = exports.usedKeyRegistery = exports.seedableSimplifyString = exports.getRandomString = exports.findAllFilesWithExt = exports.replaceFirstMatch = exports.findContentBetweenMarker = exports.setLogLevel = exports.replaceJsonKeysInFiles = exports.loadAndMergeJsonFiles = exports.normalizePath = exports.log = exports.getFilenameFromPath = void 0;
+const tslib_1 = require("tslib");
+const fs_1 = tslib_1.__importDefault(require("fs"));
+const path_1 = tslib_1.__importDefault(require("path"));
+const recoverable_random_1 = tslib_1.__importDefault(require("recoverable-random"));
+const css_1 = require("./handlers/css");
+const html_1 = require("./handlers/html");
+const js_1 = require("./handlers/js");
+const issuer = "[next-css-obfuscator]";
+let logLevel = "info";
+const levels = ["debug", "info", "warn", "error", "success"];
+function log(type, task, data) {
+ if (levels.indexOf(type) < levels.indexOf(logLevel)) {
+ return;
+ }
+ const mainColor = "\x1b[38;2;99;102;241m%s\x1b[0m";
+ switch (type) {
+ case "debug":
+ console.debug(mainColor, issuer, "[Debug] \x1b[37m", task, data, "\x1b[0m");
+ break;
+ case "info":
+ console.info(mainColor, issuer, "🗯️ \x1b[36m", task, data, "\x1b[0m");
+ break;
+ case "warn":
+ console.warn(mainColor, issuer, "⚠️ \x1b[33m", task, data, "\x1b[0m");
+ break;
+ case "error":
+ console.error(mainColor, issuer, "⛔ \x1b[31m", task, data, "\x1b[0m");
+ break;
+ case "success":
+ console.log(mainColor, issuer, "✅ \x1b[32m", task, data, "\x1b[0m");
+ break;
+ default:
+ console.log("'\x1b[0m'", issuer, task, data, "\x1b[0m");
+ break;
+ }
+}
+exports.log = log;
+function setLogLevel(level) {
+ logLevel = level;
+}
+exports.setLogLevel = setLogLevel;
+const usedKeyRegistery = new Set();
+exports.usedKeyRegistery = usedKeyRegistery;
+function replaceJsonKeysInFiles({ targetFolder, allowExtensions, selectorConversionJsonFolderPath, contentIgnoreRegexes, whiteListedFolderPaths, blackListedFolderPaths, enableObfuscateMarkerClasses, obfuscateMarkerClasses, removeObfuscateMarkerClassesAfterObfuscated, removeOriginalCss, enableJsAst, }) {
+ const classConversion = loadAndMergeJsonFiles(selectorConversionJsonFolderPath);
+ if (removeObfuscateMarkerClassesAfterObfuscated) {
+ obfuscateMarkerClasses.forEach(obfuscateMarkerClass => {
+ classConversion[`.${obfuscateMarkerClass}`] = "";
+ });
+ }
+ const cssPaths = [];
+ const replaceJsonKeysInFile = (filePath) => {
+ const fileExt = path_1.default.extname(filePath).toLowerCase();
+ if (fs_1.default.statSync(filePath).isDirectory()) {
+ fs_1.default.readdirSync(filePath).forEach((subFilePath) => {
+ replaceJsonKeysInFile(path_1.default.join(filePath, subFilePath));
+ });
+ }
+ else if (allowExtensions.includes(fileExt)) {
+ let isTargetFile = true;
+ if (whiteListedFolderPaths.length > 0) {
+ isTargetFile = whiteListedFolderPaths.some((incloudPath) => {
+ if (typeof incloudPath === "string") {
+ return normalizePath(filePath).includes(incloudPath);
+ }
+ const regex = new RegExp(incloudPath);
+ return regex.test(normalizePath(filePath));
+ });
+ }
+ if (blackListedFolderPaths.length > 0) {
+ const res = !blackListedFolderPaths.some((incloudPath) => {
+ if (typeof incloudPath === "string") {
+ return normalizePath(filePath).includes(incloudPath);
+ }
+ const regex = new RegExp(incloudPath);
+ return regex.test(normalizePath(filePath));
+ });
+ if (!res) {
+ isTargetFile = false;
+ }
+ }
+ if (!isTargetFile) {
+ return;
+ }
+ let fileContent = fs_1.default.readFileSync(filePath, "utf-8");
+ const fileContentOriginal = fileContent;
+ if (enableObfuscateMarkerClasses) {
+ obfuscateMarkerClasses.forEach(obfuscateMarkerClass => {
+ const isHtml = [".html"].includes(fileExt);
+ if (isHtml) {
+ const htmlRegex = new RegExp(`(<(.*)>(.*)<\/([^br][A-Za-z0-9]+)>)`, 'g');
+ const htmlMatch = fileContent.match(htmlRegex);
+ if (htmlMatch) {
+ const htmlOriginal = htmlMatch[0];
+ const { obfuscatedContent, usedKeys } = (0, html_1.obfuscateHtmlClassNames)({
+ html: htmlOriginal,
+ selectorConversion: classConversion,
+ obfuscateMarkerClass: obfuscateMarkerClass,
+ contentIgnoreRegexes: contentIgnoreRegexes,
+ });
+ addKeysToRegistery(usedKeys);
+ if (htmlOriginal !== obfuscatedContent) {
+ fileContent = fileContent.replace(htmlOriginal, obfuscatedContent);
+ }
+ }
+ }
+ else {
+ const obfuscateScriptContent = (0, js_1.obfuscateJs)(fileContent, obfuscateMarkerClass, classConversion, filePath, contentIgnoreRegexes, enableJsAst);
+ if (fileContent !== obfuscateScriptContent) {
+ fileContent = obfuscateScriptContent;
+ log("debug", `Obscured keys in JS like content file:`, normalizePath(filePath));
+ }
+ }
+ });
+ }
+ else {
+ if ([".js"].includes(fileExt)) {
+ const obfuscateScriptContent = (0, js_1.obfuscateJs)(fileContent, enableJsAst ? "" : "jsx", classConversion, filePath, contentIgnoreRegexes, enableJsAst);
+ if (fileContent !== obfuscateScriptContent) {
+ fileContent = obfuscateScriptContent;
+ log("debug", `Obscured keys in JSX related file:`, normalizePath(filePath));
+ }
+ }
+ else if ([".html"].includes(fileExt)) {
+ const { obfuscatedContent, usedKeys } = (0, html_1.obfuscateHtmlClassNames)({
+ html: fileContent,
+ selectorConversion: classConversion,
+ contentIgnoreRegexes: contentIgnoreRegexes,
+ });
+ fileContent = obfuscatedContent;
+ addKeysToRegistery(usedKeys);
+ }
+ else {
+ const { obfuscatedContent, usedKeys } = obfuscateKeys(classConversion, fileContent, contentIgnoreRegexes);
+ fileContent = obfuscatedContent;
+ addKeysToRegistery(usedKeys);
+ }
+ }
+ if (fileContentOriginal !== fileContent) {
+ log("success", "Data obfuscated:", normalizePath(filePath));
+ fs_1.default.writeFileSync(filePath, fileContent);
+ }
+ }
+ else if (fileExt === ".css") {
+ cssPaths.push(filePath);
+ }
+ };
+ replaceJsonKeysInFile(targetFolder);
+ cssPaths.forEach((cssPath) => {
+ (0, css_1.obfuscateCss)(classConversion, cssPath, removeOriginalCss, !enableObfuscateMarkerClasses);
+ });
+}
+exports.replaceJsonKeysInFiles = replaceJsonKeysInFiles;
+function obfuscateKeys(selectorConversion, fileContent, contentIgnoreRegexes = [], useHtmlEntity = false) {
+ const usedKeys = new Set();
+ Object.keys(selectorConversion).forEach((key) => {
+ const fileContentOriginal = fileContent;
+ let keyUse = key.slice(1);
+ keyUse = escapeRegExp(keyUse.replace(/\\/g, ""));
+ let exactMatchRegex = new RegExp(`([\\s"'\\\`]|^)(${keyUse})(?=$|[\\s"'\\\`]|\\\\n|\\\\",|\\\\"})`, 'g');
+ const replacement = `$1` + selectorConversion[key].slice(1).replace(/\\/g, "");
+ const matches = fileContent.match(exactMatchRegex);
+ const originalObscuredContentPairs = matches === null || matches === void 0 ? void 0 : matches.map((match) => {
+ return { originalContent: match, obscuredContent: match.replace(exactMatchRegex, replacement) };
+ });
+ fileContent = fileContent.replace(exactMatchRegex, replacement);
+ if (contentIgnoreRegexes.length > 0) {
+ contentIgnoreRegexes.forEach((regex) => {
+ const originalContentFragments = fileContentOriginal.match(regex);
+ originalContentFragments === null || originalContentFragments === void 0 ? void 0 : originalContentFragments.map((originalContentFragment) => {
+ originalObscuredContentPairs === null || originalObscuredContentPairs === void 0 ? void 0 : originalObscuredContentPairs.map((pair) => {
+ if (originalContentFragments === null || originalContentFragments === void 0 ? void 0 : originalContentFragments.some((fragment) => fragment.includes(pair.originalContent))) {
+ log("debug", "Obscured keys:", `Ignored ${pair.originalContent} at ${originalContentFragment}`);
+ fileContent = fileContent.replace(originalContentFragment.replace(pair.originalContent, pair.obscuredContent), originalContentFragment);
+ }
+ });
+ });
+ });
+ }
+ if (fileContentOriginal !== fileContent && !usedKeys.has(key)) {
+ usedKeys.add(key);
+ }
+ });
+ return { obfuscatedContent: fileContent, usedKeys: usedKeys };
+}
+exports.obfuscateKeys = obfuscateKeys;
+function escapeRegExp(str) {
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+}
+function getFilenameFromPath(filePath) {
+ return filePath.replace(/^.*[\\/]/, '');
+}
+exports.getFilenameFromPath = getFilenameFromPath;
+function normalizePath(filePath) {
+ return filePath.replace(/\\/g, "/");
+}
+exports.normalizePath = normalizePath;
+function loadAndMergeJsonFiles(jsonFolderPath) {
+ const jsonFiles = {};
+ fs_1.default.readdirSync(jsonFolderPath).forEach((file) => {
+ const filePath = path_1.default.join(jsonFolderPath, file);
+ const fileData = JSON.parse(fs_1.default.readFileSync(filePath, "utf-8"));
+ Object.assign(jsonFiles, fileData);
+ });
+ return jsonFiles;
+}
+exports.loadAndMergeJsonFiles = loadAndMergeJsonFiles;
+function findClosestSymbolPosition(content, openMarker, closeMarker, startPosition = 0, direction = "backward") {
+ let level = 0;
+ let currentPos = startPosition;
+ if (direction === "backward") {
+ while (currentPos >= 0 && level >= 0) {
+ if (content.slice(currentPos, currentPos + openMarker.length) === openMarker) {
+ level--;
+ }
+ else if (content.slice(currentPos, currentPos + closeMarker.length) === closeMarker) {
+ level++;
+ }
+ currentPos--;
+ }
+ if (level < 0) {
+ currentPos += 2;
+ }
+ }
+ else {
+ while (currentPos < content.length && level >= 0) {
+ if (content.slice(currentPos, currentPos + openMarker.length) === openMarker) {
+ level++;
+ }
+ else if (content.slice(currentPos, currentPos + closeMarker.length) === closeMarker) {
+ level--;
+ }
+ currentPos++;
+ }
+ if (level < 0) {
+ currentPos--;
+ }
+ }
+ return currentPos;
+}
+exports.findClosestSymbolPosition = findClosestSymbolPosition;
+function findContentBetweenMarker(content, targetStr, openMarker, closeMarker) {
+ if (openMarker === closeMarker) {
+ throw new Error("openMarker and closeMarker can not be the same");
+ }
+ let targetStrPosition = content.indexOf(targetStr);
+ const truncatedContents = [];
+ while (targetStrPosition !== -1 && targetStrPosition < content.length) {
+ const openPos = findClosestSymbolPosition(content, openMarker, closeMarker, targetStrPosition, "backward");
+ const closePos = findClosestSymbolPosition(content, openMarker, closeMarker, targetStrPosition, "forward");
+ if (openPos === -1 && closePos === -1) {
+ break;
+ }
+ if (openPos > -1 && closePos > -1) {
+ truncatedContents.push(content.slice(openPos, closePos));
+ targetStrPosition = content.indexOf(targetStr, closePos + 1);
+ }
+ else {
+ targetStrPosition = content.indexOf(targetStr, targetStrPosition + 1);
+ }
+ }
+ return truncatedContents;
+}
+exports.findContentBetweenMarker = findContentBetweenMarker;
+function addKeysToRegistery(usedKeys) {
+ usedKeys.forEach((key) => {
+ usedKeyRegistery.add(key);
+ });
+}
+exports.addKeysToRegistery = addKeysToRegistery;
+function findAllFilesWithExt(ext, targetFolderPath) {
+ if (!fs_1.default.existsSync(targetFolderPath)) {
+ return [];
+ }
+ const targetExtFiles = [];
+ function findCssFiles(dir) {
+ const files = fs_1.default.readdirSync(dir);
+ files.forEach((file) => {
+ const filePath = normalizePath(path_1.default.join(dir, file));
+ if (fs_1.default.statSync(filePath).isDirectory()) {
+ findCssFiles(filePath);
+ }
+ else {
+ if (file.endsWith(ext)) {
+ targetExtFiles.push(filePath);
+ }
+ }
+ });
+ }
+ findCssFiles(targetFolderPath);
+ return targetExtFiles;
+}
+exports.findAllFilesWithExt = findAllFilesWithExt;
+let rng = undefined;
+function getRandomString(length, seed, rngStateCode, str) {
+ if (length <= 0 || !Number.isInteger(length)) {
+ throw new Error("Length must be a positive integer");
+ }
+ if (!rng) {
+ rng = new recoverable_random_1.default(seed);
+ }
+ if (rngStateCode) {
+ rng.recoverState(rngStateCode);
+ }
+ let rn = rng.random(0, 1, true);
+ if (str && seed) {
+ rn = parseFloat(`0.${recoverable_random_1.default.stringToSeed(str) + recoverable_random_1.default.stringToSeed(seed)}`);
+ }
+ const randomString = rn.toString(36).substring(2, length - 1 + 2);
+ const randomLetter = String.fromCharCode(Math.floor(rng.random(0, 1, true) * 26) + 97);
+ return {
+ rngStateCode: rng.getStateCode(),
+ randomString: `${randomLetter}${randomString}`,
+ };
+}
+exports.getRandomString = getRandomString;
+function seedableSimplifyString(str, seed, rngStateCode) {
+ if (!str) {
+ throw new Error("String can not be empty");
+ }
+ if (!rng) {
+ rng = new recoverable_random_1.default(seed);
+ }
+ if (rngStateCode) {
+ rng.recoverState(rngStateCode);
+ }
+ const tempStr = str.replace(/[aeiouw\d_-]/gi, "");
+ return {
+ rngStateCode: rng.getStateCode(),
+ randomString: tempStr.length < 1
+ ? String.fromCharCode(Math.floor(rng.random(0, 1, true) * 26) + 97) + tempStr
+ : tempStr,
+ };
+}
+exports.seedableSimplifyString = seedableSimplifyString;
+function simplifyString(alphabetPoistion) {
+ if (alphabetPoistion <= 0 || !Number.isInteger(alphabetPoistion)) {
+ throw new Error("Position must be a positive integer");
+ }
+ let dividend = alphabetPoistion;
+ let columnName = "";
+ let modulo = 0;
+ while (dividend > 0) {
+ modulo = (dividend - 1) % 26;
+ columnName = String.fromCharCode(97 + modulo) + columnName;
+ dividend = Math.floor((dividend - modulo) / 26);
+ }
+ return columnName;
+}
+exports.simplifyString = simplifyString;
+function replaceFirstMatch(source, find, replace) {
+ const index = source.indexOf(find);
+ if (index !== -1) {
+ return source.slice(0, index) + replace + source.slice(index + find.length);
+ }
+ return source;
+}
+exports.replaceFirstMatch = replaceFirstMatch;
+function duplicationCheck(arr) {
+ const set = new Set(arr);
+ return arr.length !== set.size;
+}
+exports.duplicationCheck = duplicationCheck;
+function createKey(str) {
+ const b64 = Buffer.from(str).toString("base64").replace(/=/g, "");
+ return `{{{{{{${b64}}}}}}}`;
+}
+exports.createKey = createKey;
+function decodeKey(str) {
+ const regex = /{{{{{{([\w\+\/]+)}}}}}}/g;
+ str = str.replace(regex, (match, p1) => {
+ const padding = p1.length % 4 === 0 ? 0 : 4 - (p1.length % 4);
+ const b64 = p1 + "=".repeat(padding);
+ return Buffer.from(b64, "base64").toString("ascii");
+ });
+ return str;
+}
+exports.decodeKey = decodeKey;
diff --git a/package-lock.json b/package-lock.json
index 594bad7..b4428f1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "next-css-obfuscator",
- "version": "2.2.14-beta.2",
+ "version": "2.2.16",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "next-css-obfuscator",
- "version": "2.2.14-beta.2",
+ "version": "2.2.16",
"license": "MIT",
"dependencies": {
"@babel/generator": "^7.23.6",
diff --git a/package.json b/package.json
index eacbef1..9addedc 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
"scripts": {
"test": "tsc & vitest",
"test@cover": "tsc & vitest run --coverage",
- "build": "npm run test && tsc",
+ "build": "tsc",
"dev": "tsc -w",
"pub": "npm run build && npm publish",
"pub@beta": "npm run build && npm publish --tag beta"
diff --git a/src/handlers/js-ast.ts b/src/handlers/js-ast.ts
index aadf976..affc447 100644
--- a/src/handlers/js-ast.ts
+++ b/src/handlers/js-ast.ts
@@ -136,21 +136,21 @@ function searchStringLiterals(path: NodePath,
path.replaceWith(t.stringLiteral(replacement));
}
}
- else if (t.isIdentifier(path.node)) {
- const variableName = path.node.name;
- const binding = path.scope.getBinding(variableName);
- if (binding && t.isVariableDeclarator(binding.path.node)) {
- const init = binding.path.get("init");
- if (init && !Array.isArray(init)) {
- searchStringLiterals(init, callback, scannedNodes);
- }
- } else if (binding && t.isFunctionDeclaration(binding.path.node)) {
- const body = binding.path.get("body");
- if (body && !Array.isArray(body)) {
- searchStringLiterals(body, callback, scannedNodes);
- }
- }
- }
+ // else if (t.isIdentifier(path.node)) {
+ // const variableName = path.node.name;
+ // const binding = path.scope.getBinding(variableName);
+ // if (binding && t.isVariableDeclarator(binding.path.node)) {
+ // const init = binding.path.get("init");
+ // if (init && !Array.isArray(init)) {
+ // searchStringLiterals(init, callback, scannedNodes);
+ // }
+ // } else if (binding && t.isFunctionDeclaration(binding.path.node)) {
+ // const body = binding.path.get("body");
+ // if (body && !Array.isArray(body)) {
+ // searchStringLiterals(body, callback, scannedNodes);
+ // }
+ // }
+ // }
/* call expression (e.g. const a = call()) */
else if (t.isCallExpression(path.node)) {
const callee = path.get("callee");