From 16a4541691d41868e0ef1ae9311096596c2ab4a3 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Sun, 20 Apr 2025 14:44:13 +0100 Subject: [PATCH 01/32] refactor: Move test files to `__tests__` directory Moves test files from the `src/handlers` directory to the `src/__tests__` directory for better organization and separation of concerns. This change improves the project structure and makes it easier to locate and manage test files. Also, updates import paths in test files. --- src/{handlers => __tests__}/css.test.ts | 2 +- src/{handlers => __tests__}/html.test.ts | 2 +- src/{handlers => __tests__}/js-ast.test.ts | 2 +- src/{handlers => __tests__}/js.test.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/{handlers => __tests__}/css.test.ts (99%) rename src/{handlers => __tests__}/html.test.ts (99%) rename src/{handlers => __tests__}/js-ast.test.ts (99%) rename src/{handlers => __tests__}/js.test.ts (99%) diff --git a/src/handlers/css.test.ts b/src/__tests__/css.test.ts similarity index 99% rename from src/handlers/css.test.ts rename to src/__tests__/css.test.ts index 0d908b0..0697f9b 100644 --- a/src/handlers/css.test.ts +++ b/src/__tests__/css.test.ts @@ -7,7 +7,7 @@ import { copyCssData, renameCssSelector, extractClassFromSelector, -} from "./css"; +} from "../handlers/css"; const testCss = ` .s0-1 { diff --git a/src/handlers/html.test.ts b/src/__tests__/html.test.ts similarity index 99% rename from src/handlers/html.test.ts rename to src/__tests__/html.test.ts index 58982b4..5319c8e 100644 --- a/src/handlers/html.test.ts +++ b/src/__tests__/html.test.ts @@ -5,7 +5,7 @@ import { describe, it, expect } from "vitest"; import { findHtmlTagContentsByClass, obfuscateHtmlClassNames, -} from "./html"; +} from "../handlers/html"; //! ================================ diff --git a/src/handlers/js-ast.test.ts b/src/__tests__/js-ast.test.ts similarity index 99% rename from src/handlers/js-ast.test.ts rename to src/__tests__/js-ast.test.ts index 38449ea..4e5a0aa 100644 --- a/src/handlers/js-ast.test.ts +++ b/src/__tests__/js-ast.test.ts @@ -3,7 +3,7 @@ import traverse, { NodePath } from "@babel/traverse"; import * as t from "@babel/types"; import * as parser from "@babel/parser"; import generator from "@babel/generator"; -import { searchStringLiterals, obfuscateJsWithAst } from "./js-ast"; +import { searchStringLiterals, obfuscateJsWithAst } from "../handlers/js-ast"; function stripCode(code: string) { return code.replace(/\s/g, ""); diff --git a/src/handlers/js.test.ts b/src/__tests__/js.test.ts similarity index 99% rename from src/handlers/js.test.ts rename to src/__tests__/js.test.ts index 6e33ffc..3fea976 100644 --- a/src/handlers/js.test.ts +++ b/src/__tests__/js.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, test } from "vitest"; import { searchForwardComponent, -} from "./js"; +} from "../handlers/js"; //! ================================ //! searchForwardComponent From 6b5f0002eaead4c56118eae82f0db4f5bf3c7023 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Sun, 20 Apr 2025 18:39:29 +0100 Subject: [PATCH 02/32] refactor(test): Moves test file to the `__tests__` directory Moves the test file from the `src` directory to the `__tests__` directory for better organization and separation of concerns. This change improves the project structure and makes it easier to locate test files. --- src/{ => __tests__}/utils.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/{ => __tests__}/utils.test.ts (99%) diff --git a/src/utils.test.ts b/src/__tests__/utils.test.ts similarity index 99% rename from src/utils.test.ts rename to src/__tests__/utils.test.ts index 54505ee..f921647 100644 --- a/src/utils.test.ts +++ b/src/__tests__/utils.test.ts @@ -6,7 +6,7 @@ import { seedableSimplifyString, duplicationCheck, simplifyString, -} from "./utils"; +} from "../utils"; import NumberGenerator from "recoverable-random"; From d38b38fdf51fbe3e5f68e49ba1f239adf480bf91 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Sun, 20 Apr 2025 19:21:13 +0100 Subject: [PATCH 03/32] chore: Migrates from Prettier to Biome Migrates the project's code formatting and linting from Prettier to Biome. This change removes the existing Prettier configuration and implements Biome. The Biome configuration enforces consistent code style and catches potential issues, improving code quality and maintainability. --- .prettierignore | 5 ---- .prettierrc | 7 ----- biome.json | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 12 deletions(-) delete mode 100644 .prettierignore delete mode 100644 .prettierrc create mode 100644 biome.json diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 7605e7f..0000000 --- a/.prettierignore +++ /dev/null @@ -1,5 +0,0 @@ -build -coverage -dist -node_modules -*.test.* \ No newline at end of file diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index cf7c9eb..0000000 --- a/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "semi": true, - "trailingComma": "all", - "singleQuote": false, - "printWidth": 100, - "tabWidth": 2 -} diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..03fb901 --- /dev/null +++ b/biome.json @@ -0,0 +1,69 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false + }, + "files": { + "ignoreUnknown": false, + "ignore": [] + }, + "formatter": { + "ignore": ["node_modules", ".next"], + "enabled": true, + "indentStyle": "space", + "lineWidth": 80, + "indentWidth": 2, + "lineEnding": "lf" + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "ignore": ["node_modules", ".next", "packages/tsconfig"], + "enabled": true, + "rules": { + "recommended": true, + + "complexity": { + "noUselessTypeConstraint": "error", + "useLiteralKeys": "error", + "useOptionalChain": "error", + "noForEach": "off", + }, + "correctness": { + "noUnusedVariables": "info", + "useArrayLiterals": "error" + }, + "style": { + "noInferrableTypes": "error", + "noNamespace": "error", + "useAsConstAssertion": "error", + "useBlockStatements": "error", + "useConsistentArrayType": "error", + "useForOf": "error", + "useShorthandFunctionType": "error", + "useImportType": "error" + }, + "suspicious": { + "noDebugger": "info", + "noConsoleLog": "info", + "noEmptyBlockStatements": "error", + "noExplicitAny": "error", + "noExtraNonNullAssertion": "error", + "noMisleadingInstantiator": "error", + "noUnsafeDeclarationMerging": "error", + "useAwait": "warn", + "useNamespaceKeyword": "error" + } + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double", + "semicolons": "always", + "trailingCommas": "all" + } + } +} From 0afd7808b2c6405b5fbedb4b8ff0e34f813ccd82 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Sun, 20 Apr 2025 19:22:26 +0100 Subject: [PATCH 04/32] feat: Upgrades to v3 with new CSS transformation Upgrades the core CSS transformation engine to `css-seasoning` and `lightningcss-wasm`. - Replaces PostCSS with `css-seasoning` for CSS parsing and transformation. - Integrates `lightningcss-wasm` for CSS minification and optimization. - Introduces a new `obfuscateCssFiles` function for processing CSS files. - Removes the deprecated `classLength` option. - Improves the way of css obfuscation with new logic and features. This change significantly enhances the performance and flexibility of the CSS obfuscation process. --- README.md | 3 + package-lock.json | 235 +++++++++++++++++++ package.json | 5 +- src/config.ts | 4 + src/handlers/css.ts | 543 +++++++++++++++++++++++++++++--------------- src/index.ts | 60 +++-- src/types.ts | 8 +- src/utils.ts | 177 +++++++++------ 8 files changed, 765 insertions(+), 270 deletions(-) diff --git a/README.md b/README.md index 138fe9a..3e6ede2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ + + + # NEXT-CSS-OBFUSCATOR Project starts on 30-10-2023 diff --git a/package-lock.json b/package-lock.json index 7f52229..50d0aea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,8 +13,10 @@ "@babel/parser": "^7.23.9", "@babel/traverse": "^7.23.9", "css-parse": "^2.0.0", + "css-seasoning": "^1.3.0", "html-escaper": "^3.0.3", "htmlparser2": "^10.0.0", + "lightningcss-wasm": "^1.29.3", "recoverable-random": "^1.0.5", "yargs": "^17.7.2" }, @@ -22,6 +24,7 @@ "next-css-obfuscator": "bin/cli.mjs" }, "devDependencies": { + "@biomejs/biome": "1.9.4", "@types/babel__generator": "^7.6.8", "@types/babel__traverse": "^7.20.5", "@types/html-escaper": "^3.0.4", @@ -341,6 +344,197 @@ "node": ">=18" } }, + "node_modules/@biomejs/biome": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz", + "integrity": "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==", + "dev": true, + "hasInstallScript": true, + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "1.9.4", + "@biomejs/cli-darwin-x64": "1.9.4", + "@biomejs/cli-linux-arm64": "1.9.4", + "@biomejs/cli-linux-arm64-musl": "1.9.4", + "@biomejs/cli-linux-x64": "1.9.4", + "@biomejs/cli-linux-x64-musl": "1.9.4", + "@biomejs/cli-win32-arm64": "1.9.4", + "@biomejs/cli-win32-x64": "1.9.4" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz", + "integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz", + "integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz", + "integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz", + "integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz", + "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz", + "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz", + "integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz", + "integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@deno/shim-deno": { + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/@deno/shim-deno/-/shim-deno-0.18.2.tgz", + "integrity": "sha512-oQ0CVmOio63wlhwQF75zA4ioolPvOwAoK0yuzcS5bDC1JUvH3y1GS8xPh8EOpcoDQRU4FTG8OQfxhpR+c6DrzA==", + "dependencies": { + "@deno/shim-deno-test": "^0.5.0", + "which": "^4.0.0" + } + }, + "node_modules/@deno/shim-deno-test": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@deno/shim-deno-test/-/shim-deno-test-0.5.0.tgz", + "integrity": "sha512-4nMhecpGlPi0cSzT67L+Tm+GOJqvuk8gqHBziqcUQOarnuIax1z96/gJHCSIz2Z0zhxE6Rzwb3IZXPtFh51j+w==" + }, + "node_modules/@deno/shim-deno/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "engines": { + "node": ">=16" + } + }, + "node_modules/@deno/shim-deno/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", @@ -1539,6 +1733,19 @@ "css": "^2.0.0" } }, + "node_modules/css-seasoning": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/css-seasoning/-/css-seasoning-1.3.0.tgz", + "integrity": "sha512-D/Th17IefXLulL4AKphSN1uaclv+UV8eyY83GRVRxfsOzwreNwy4VA2ksmVRQoDKDUlJw2XpCO6HabFgPX5PlA==", + "dependencies": { + "@deno/shim-deno": "~0.18.0", + "lightningcss-wasm": "^1.29.1", + "xxhash-wasm": "^1.1.0" + }, + "bin": { + "css-seasoning": "bin/cli.mjs" + } + }, "node_modules/csstype": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", @@ -1939,6 +2146,29 @@ "node": ">=4" } }, + "node_modules/lightningcss-wasm": { + "version": "1.29.3", + "resolved": "https://registry.npmjs.org/lightningcss-wasm/-/lightningcss-wasm-1.29.3.tgz", + "integrity": "sha512-j02QNSRVBKxsSBinpSCgx3x8XwwOoO50ekDO1O5rBf+dS0j46qSojv+3BDzMNCaGFJ18EjcnXGDkG3ImIbU/PQ==", + "bundleDependencies": [ + "napi-wasm" + ], + "dependencies": { + "napi-wasm": "^1.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-wasm/node_modules/napi-wasm": { + "version": "1.1.3", + "inBundle": true, + "license": "MIT" + }, "node_modules/loupe": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", @@ -2678,6 +2908,11 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/xxhash-wasm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", + "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==" + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 07ca11f..c2f462b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next-css-obfuscator", - "version": "2.2.19", + "version": "3.0.0-b1", "description": "A package deeply inspired by PostCSS-Obfuscator but for Next.js.", "main": "dist/index.js", "type": "commonjs", @@ -27,12 +27,15 @@ "@babel/parser": "^7.23.9", "@babel/traverse": "^7.23.9", "css-parse": "^2.0.0", + "css-seasoning": "^1.3.0", "html-escaper": "^3.0.3", "htmlparser2": "^10.0.0", + "lightningcss-wasm": "^1.29.3", "recoverable-random": "^1.0.5", "yargs": "^17.7.2" }, "devDependencies": { + "@biomejs/biome": "1.9.4", "@types/babel__generator": "^7.6.8", "@types/babel__traverse": "^7.20.5", "@types/html-escaper": "^3.0.4", diff --git a/src/config.ts b/src/config.ts index 0f7f17e..15409dd 100644 --- a/src/config.ts +++ b/src/config.ts @@ -7,7 +7,11 @@ const defaultOptions: Options = { classConversionJsonFolderPath: "./css-obfuscator", // The folder path to store the before obfuscate and after obfuscated classes conversion table. refreshClassConversionJson: false, // Refresh the class conversion JSON file. + /** + * @deprecated Not longer used from v3.0.0 + */ classLength: 5, // Length of the obfuscated class name. + classPrefix: "", // Prefix of the obfuscated class name. classSuffix: "", // Suffix of the obfuscated class name. classIgnore: [], // The class names to be ignored during obfuscation. diff --git a/src/handlers/css.ts b/src/handlers/css.ts index 0758d08..95d3ea4 100644 --- a/src/handlers/css.ts +++ b/src/handlers/css.ts @@ -1,8 +1,12 @@ -import path from "path"; -import fs from "fs"; +import type { obfuscateMode, SelectorConversion } from "../types"; + +import path from "node:path"; +import fs from "node:fs"; // @ts-ignore -import css from 'css'; +import css from "css"; import NumberGenerator from "recoverable-random"; +import { initTransform, transform, ConversionTable, cssEscape, type ConversionTables, type TransformProps } from "css-seasoning"; +import lightningcssInit, { transform as lightningcssTransform } from "lightningcss-wasm"; import { log, @@ -16,8 +20,9 @@ import { duplicationCheck, createKey, decodeKey, + stringToNumber, + loadConversionTables, } from "../utils"; -import { obfuscateMode, SelectorConversion } from "../types"; let randomStringGeneraterStateCode: string | undefined = undefined; let currentAlphabetPoistion = 1; @@ -44,9 +49,9 @@ function createNewClassName( case "random": ({ rngStateCode, randomString } = getRandomString(classNameLength, seed, undefined, className)); break; - case "simplify-seedable": - ({ rngStateCode, randomString } = seedableSimplifyString(className, seed, seed + NumberGenerator.stringToSeed(className).toString())); - break; + // case "simplify-seedable": + // ({ rngStateCode, randomString } = seedableSimplifyString(className, seed, seed + NumberGenerator.stringToSeed(className).toString())); + // break; case "simplify": randomString = simplifyString(currentAlphabetPoistion); currentAlphabetPoistion++; @@ -90,7 +95,7 @@ const findActionSelectorsRegex = /(?)/g * // Returns: [] * extractClassFromSelector("div"); */ -function extractClassFromSelector(selector: string, replacementClassNames?: (string | undefined)[]) { +export function extractClassFromSelector(selector: string, replacementClassNames?: (string | undefined)[]) { //? "\\[\w\%\:\.\!\*\<\>\/]" handle escaped characters //? "(?:\\\[(?:[^\[\]\s])*\\\]))+)" handle [attribute / Tailwind CSS custom parameter] selector const extractClassRegex = /(?<=[.:!]|(?\/]|(?:\\\[(?:[^\[\]\s])*\\\]))+)(?![\w\-]*\()/g; @@ -152,7 +157,11 @@ function getAllSelector(cssObj: any): any[] { return selectors; } -function createSelectorConversionJson( +/** + * + * @deprecated will be replaced by new css transformer + */ +export function createSelectorConversionJson( { selectorConversionJsonFolderPath, buildFolderPath, @@ -178,114 +187,114 @@ function createSelectorConversionJson( enableObfuscateMarkerClasses?: boolean, generatorSeed?: string, }) { - if (!fs.existsSync(selectorConversionJsonFolderPath)) { - fs.mkdirSync(selectorConversionJsonFolderPath); - } - - const selectorConversion: SelectorConversion = loadAndMergeJsonFiles(selectorConversionJsonFolderPath); - - // pre-defined ".dark", mainly for tailwindcss dark mode - if (enableObfuscateMarkerClasses) { - selectorConversion[".dark"] = ".dark"; - } - - // get all css selectors - const cssPaths = findAllFilesWithExt(".css", buildFolderPath); - const selectors: string[] = []; - cssPaths.forEach((cssPath) => { - const cssContent = fs.readFileSync(cssPath, "utf-8"); - const cssObj = css.parse(cssContent); - selectors.push(...getAllSelector(cssObj)); - }); - - // remove duplicated selectors - 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: { [key: string]: string[] } = {}; - - for (let i = 0; i < uniqueSelectors.length; i++) { - const originalSelector = uniqueSelectors[i]; - const { extractedClasses } = extractClassFromSelector(originalSelector) || []; - selectorClassPair[originalSelector] = extractedClasses; - } - - //? since a multi part selector normally grouped by multiple basic selectors - //? so we need to obfuscate the basic selector first - //? eg. ":is(.class1 .class2)" grouped by ".class1" and ".class2" - // sort the selectorClassPair by the number of classes in the selector (from least to most) - // and remove the selector with no class - 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) => { - - // apply ignore list - if (classIgnore.some(regex => { - if (typeof regex === "string") { - return className === regex; - } - return new RegExp(regex).test(className) - })) { - return className; - } - - // try to get the obfuscated selector from the selectorConversion - // if not found, create a new one - let obfuscatedSelector = selectorConversion[`.${className}`]; - if (!obfuscatedSelector) { - const obfuscatedClass = createNewClassName(mode, className, classPrefix, classSuffix, classNameLength, generatorSeed); - obfuscatedSelector = `.${obfuscatedClass}`; - selectorConversion[`.${className}`] = obfuscatedSelector; - } - - // return the obfuscated class - return obfuscatedSelector.slice(1) - }); - - // obfuscate the selector - const { selector: obfuscatedSelector } = extractClassFromSelector(originalSelector, classes); - - selectorConversion[originalSelector] = obfuscatedSelector; - } - } - - const jsonPath = path.join(process.cwd(), selectorConversionJsonFolderPath, "conversion.json"); - fs.writeFileSync(jsonPath, JSON.stringify(selectorConversion, null, 2)); - if (duplicationCheck(Object.keys(selectorConversion))) { - if (mode == "random") { - 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 { - 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"); - } - } + // if (!fs.existsSync(selectorConversionJsonFolderPath)) { + // fs.mkdirSync(selectorConversionJsonFolderPath); + // } + + // const selectorConversion = loadConversionTables(selectorConversionJsonFolderPath); + + // // pre-defined ".dark", mainly for tailwindcss dark mode + // if (enableObfuscateMarkerClasses) { + // selectorConversion[".dark"] = ".dark"; + // } + + // // get all css selectors + // const cssPaths = findAllFilesWithExt(".css", buildFolderPath); + // const selectors: string[] = []; + // cssPaths.forEach((cssPath) => { + // const cssContent = fs.readFileSync(cssPath, "utf-8"); + // const cssObj = css.parse(cssContent); + // selectors.push(...getAllSelector(cssObj)); + // }); + + // // remove duplicated selectors + // 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: { [key: string]: string[] } = {}; + + // for (let i = 0; i < uniqueSelectors.length; i++) { + // const originalSelector = uniqueSelectors[i]; + // const { extractedClasses } = extractClassFromSelector(originalSelector) || []; + // selectorClassPair[originalSelector] = extractedClasses; + // } + + // //? since a multi part selector normally grouped by multiple basic selectors + // //? so we need to obfuscate the basic selector first + // //? eg. ":is(.class1 .class2)" grouped by ".class1" and ".class2" + // // sort the selectorClassPair by the number of classes in the selector (from least to most) + // // and remove the selector with no class + // 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) => { + + // // apply ignore list + // if (classIgnore.some(regex => { + // if (typeof regex === "string") { + // return className === regex; + // } + // return new RegExp(regex).test(className) + // })) { + // return className; + // } + + // // try to get the obfuscated selector from the selectorConversion + // // if not found, create a new one + // let obfuscatedSelector = selectorConversion[`.${className}`]; + // if (!obfuscatedSelector) { + // const obfuscatedClass = createNewClassName(mode, className, classPrefix, classSuffix, classNameLength, generatorSeed); + // obfuscatedSelector = `.${obfuscatedClass}`; + // selectorConversion[`.${className}`] = obfuscatedSelector; + // } + + // // return the obfuscated class + // return obfuscatedSelector.slice(1) + // }); + + // // obfuscate the selector + // const { selector: obfuscatedSelector } = extractClassFromSelector(originalSelector, classes); + + // selectorConversion[originalSelector] = obfuscatedSelector; + // } + // } + + // const jsonPath = path.join(process.cwd(), selectorConversionJsonFolderPath, "conversion.json"); + // fs.writeFileSync(jsonPath, JSON.stringify(selectorConversion, null, 2)); + // if (duplicationCheck(Object.keys(selectorConversion))) { + // if (mode == "random") { + // 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 { + // 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"); + // } + // } } -function copyCssData(targetSelector: string, newSelectorName: string, cssObj: any) { +export function copyCssData(targetSelector: string, newSelectorName: string, cssObj: any) { function recursive(rules: any[]): any[] { return rules.map((item: any) => { if (item.rules) { @@ -316,7 +325,7 @@ function copyCssData(targetSelector: string, newSelectorName: string, cssObj: an return cssObj; } -function renameCssSelector(oldSelector: string, newSelector: string, cssObj: any) { +export function renameCssSelector(oldSelector: string, newSelector: string, cssObj: any) { function recursive(rules: any[]): any[] { return rules.map((item: any) => { if (item.rules) { @@ -340,90 +349,266 @@ function renameCssSelector(oldSelector: string, newSelector: string, cssObj: any return cssObj; } -function obfuscateCss( - selectorConversion: SelectorConversion, +/** + * + * @deprecated WIP + */ +export const obfuscateCss = async ({ + cssPath, + removeOriginalCss, + isFullObfuscation, + outCssPath, + conversionTables, + + mode = "random", + prefix, + suffix, + classIgnore = [], + generatorSeed = Math.random().toString().slice(2, 10), // take 8 digits from the random number +}: { cssPath: string, - replaceOriginalSelector: boolean = false, - isFullObfuscation: boolean = false, + removeOriginalCss?: boolean, + isFullObfuscation?: boolean, outCssPath?: string, -) { + conversionTables?: ConversionTables, + + mode?: obfuscateMode, + prefix?: string, + suffix?: string, + classIgnore?: (string | RegExp)[], + generatorSeed?: string, +}) => { if (!outCssPath) { + // If no output path is provided, use the input path outCssPath = cssPath; } else if (!fs.existsSync(path.dirname(outCssPath))) { + // Create the output directory if it doesn't exist fs.mkdirSync(path.dirname(outCssPath)); } - let cssContent = fs.readFileSync(cssPath, "utf-8"); + const cssContent = fs.readFileSync(cssPath, "utf-8"); - let cssObj = css.parse(cssContent); - const cssRulesCount = cssObj.stylesheet.rules.length; + // TODO: use const instead of function + // TODO: this function is combined createSelectorConversionJson and obfuscateCss + // TODO: connect props to this function + // TODO: bc we updated conversion table, so other function using the table have to update to the new table - if (isFullObfuscation) { - Object.keys(selectorConversion).forEach((key) => { - usedKeyRegistery.add(key); - }); - } else { - // join all selectors start with ":" (eg. ":is") - Object.keys(selectorConversion).forEach((key) => { - if (key.startsWith(":")) { - usedKeyRegistery.add(key); - } - }); - - // join all selectors with action selectors - const actionSelectors = getAllSelector(cssObj).filter((selector) => selector.match(findActionSelectorsRegex)); - actionSelectors.forEach((actionSelector) => { - usedKeyRegistery.add(actionSelector); - }); - - // join all Tailwind CSS [child] selectors (eg. ".\[\&_\.side-box\]\:absolute .side-box") - const tailwindCssChildSelectors = getAllSelector(cssObj).filter((selector) => selector.startsWith(".\\[")); - tailwindCssChildSelectors.forEach((tailwindCssChildSelector) => { - usedKeyRegistery.add(tailwindCssChildSelector); - }); - - // join all child selectors (eg. ">*") - const universalSelectors = getAllSelector(cssObj).filter((selector) => selector.includes(">")); - universalSelectors.forEach((universalSelector) => { - usedKeyRegistery.add(universalSelector); - }); + let transformerMode: TransformProps["mode"] = mode === "simplify" ? "minimal" : "hash"; + if (!transformerMode) { + // @ts-expect-error - "simplify-seedable" is deprecated but for backward compatibility + if (mode === "simplify-seedable") { + log("warn", "CSS obfuscation", "The 'simplify-seedable' mode is deprecated, please use 'random' or 'simplify' instead. Now will fall back to 'random' mode."); + transformerMode = "hash"; + } + log("error", "CSS obfuscation", `Invalid mode: ${mode}`); + throw new Error(`Invalid mode: ${mode}`); } - // modify css rules - usedKeyRegistery.forEach((key) => { - const originalSelectorName = key; - const obfuscatedSelectorName = selectorConversion[key]; - if (obfuscatedSelectorName) { - if (replaceOriginalSelector) { - cssObj = renameCssSelector(originalSelectorName, selectorConversion[key], cssObj); - } else { - cssObj = copyCssData(originalSelectorName, selectorConversion[key], cssObj); - } + let finCss = ""; + const { css: obfuscatedCss, conversionTables: newConversionTables } = transform({ + css: cssContent, + conversionTables: conversionTables, + mode: transformerMode, + prefix: prefix, + suffix: suffix, + seed: stringToNumber(generatorSeed), + ignorePatterns: { + selector: classIgnore } }); - if (replaceOriginalSelector) { - log("info", "CSS rules:", `Modified ${usedKeyRegistery.size} CSS rules to ${getFilenameFromPath(cssPath)}`); + if (removeOriginalCss) { + finCss = obfuscatedCss; } else { - log("info", "CSS rules:", `Added ${cssObj.stylesheet.rules.length - cssRulesCount} new CSS rules to ${getFilenameFromPath(cssPath)}`); + // if keep original selector, we need to merge the original css with the obfuscated css + // then minify the css in order to marge the css + const mixedCss = cssContent + obfuscatedCss; + const { code } = lightningcssTransform({ + filename: "style.css", + code: new TextEncoder().encode(mixedCss), + minify: true, + }); + finCss = new TextDecoder().decode(code); } - const cssOptions = { - compress: true, - }; - const cssObfuscatedContent = css.stringify(cssObj, cssOptions); + const totalConversion = Object.keys(newConversionTables.selector).length + Object.keys(newConversionTables.ident).length; + if (removeOriginalCss) { + log("info", "CSS rules:", `Modified ${totalConversion} CSS rules to ${getFilenameFromPath(cssPath)}`); + } else { + const oldTotalConversion = conversionTables ? Object.keys(conversionTables.selector).length + Object.keys(conversionTables.ident).length : 0; + log("info", "CSS rules:", `Added ${totalConversion - oldTotalConversion} new CSS rules to ${getFilenameFromPath(cssPath)}`); + } + // Save the obfuscated CSS to the output path const sizeBefore = Buffer.byteLength(cssContent, "utf8"); - fs.writeFileSync(outCssPath, cssObfuscatedContent); - const sizeAfter = Buffer.byteLength(cssObfuscatedContent, "utf8"); + fs.writeFileSync(outCssPath, obfuscatedCss); + const sizeAfter = Buffer.byteLength(obfuscatedCss, "utf8"); const percentChange = Math.round(((sizeAfter) / sizeBefore) * 100); log("success", "CSS obfuscated:", `Size from ${sizeBefore} to ${sizeAfter} bytes (${percentChange}%) in ${getFilenameFromPath(cssPath)}`); + + return { + conversionTables: newConversionTables, + } } -export { - copyCssData, - renameCssSelector, - createSelectorConversionJson, - obfuscateCss, - extractClassFromSelector, -} \ No newline at end of file +// export const obfuscateCss = async ( +// selectorConversion: ConversionTable, +// cssPath: string, +// replaceOriginalSelector = false, +// isFullObfuscation = false, +// outCssPath?: string, +// ) => { +// if (!outCssPath) { +// outCssPath = cssPath; +// } else if (!fs.existsSync(path.dirname(outCssPath))) { +// fs.mkdirSync(path.dirname(outCssPath)); +// } + +// const cssContent = fs.readFileSync(cssPath, "utf-8"); + +// let cssObj = css.parse(cssContent); +// const cssRulesCount = cssObj.stylesheet.rules.length; + +// if (isFullObfuscation) { +// Object.keys(selectorConversion).forEach((key) => { +// usedKeyRegistery.add(key); +// }); +// } else { +// // join all selectors start with ":" (eg. ":is") +// Object.keys(selectorConversion).forEach((key) => { +// if (key.startsWith(":")) { +// usedKeyRegistery.add(key); +// } +// }); + +// // join all selectors with action selectors +// const actionSelectors = getAllSelector(cssObj).filter((selector) => selector.match(findActionSelectorsRegex)); +// actionSelectors.forEach((actionSelector) => { +// usedKeyRegistery.add(actionSelector); +// }); + +// // join all Tailwind CSS [child] selectors (eg. ".\[\&_\.side-box\]\:absolute .side-box") +// const tailwindCssChildSelectors = getAllSelector(cssObj).filter((selector) => selector.startsWith(".\\[")); +// tailwindCssChildSelectors.forEach((tailwindCssChildSelector) => { +// usedKeyRegistery.add(tailwindCssChildSelector); +// }); + +// // join all child selectors (eg. ">*") +// const universalSelectors = getAllSelector(cssObj).filter((selector) => selector.includes(">")); +// universalSelectors.forEach((universalSelector) => { +// usedKeyRegistery.add(universalSelector); +// }); +// } + +// // modify css rules +// usedKeyRegistery.forEach((key) => { +// const originalSelectorName = key; +// const obfuscatedSelectorName = selectorConversion[key]; +// if (obfuscatedSelectorName) { +// if (replaceOriginalSelector) { +// cssObj = renameCssSelector(originalSelectorName, selectorConversion[key], cssObj); +// } else { +// cssObj = copyCssData(originalSelectorName, selectorConversion[key], cssObj); +// } +// } +// }); + +// if (replaceOriginalSelector) { +// log("info", "CSS rules:", `Modified ${usedKeyRegistery.size} CSS rules to ${getFilenameFromPath(cssPath)}`); +// } else { +// log("info", "CSS rules:", `Added ${cssObj.stylesheet.rules.length - cssRulesCount} new CSS rules to ${getFilenameFromPath(cssPath)}`); +// } + +// const cssOptions = { +// compress: true, +// }; +// const cssObfuscatedContent = css.stringify(cssObj, cssOptions); + +// const sizeBefore = Buffer.byteLength(cssContent, "utf8"); +// fs.writeFileSync(outCssPath, cssObfuscatedContent); +// const sizeAfter = Buffer.byteLength(cssObfuscatedContent, "utf8"); +// const percentChange = Math.round(((sizeAfter) / sizeBefore) * 100); +// log("success", "CSS obfuscated:", `Size from ${sizeBefore} to ${sizeAfter} bytes (${percentChange}%) in ${getFilenameFromPath(cssPath)}`); +// } + +/** + * + */ +export const obfuscateCssFiles = async ({ + selectorConversionJsonFolderPath, + buildFolderPath, + whiteListedFolderPaths = [], + blackListedFolderPaths = [], + mode = "random", + prefix, + suffix, + classIgnore = [], + generatorSeed = new Date().getTime().toString(), + removeOriginalCss = false, +}: { + selectorConversionJsonFolderPath: string, + buildFolderPath: string, + whiteListedFolderPaths: (string | RegExp)[], + blackListedFolderPaths: (string | RegExp)[], + mode?: obfuscateMode, + prefix?: string, + suffix?: string, + classIgnore?: (string | RegExp)[], + generatorSeed?: string, + removeOriginalCss?: boolean, +}) => { + // Initialize nessesary modules + await Promise.all([ + initTransform(), + lightningcssInit(), + ]); + + // Create the selector conversion JSON folder if it doesn't exist + if (!fs.existsSync(selectorConversionJsonFolderPath)) { + fs.mkdirSync(selectorConversionJsonFolderPath); + } + + // Load and merge all JSON files in the selector conversion folder + const conversionTables = loadConversionTables(selectorConversionJsonFolderPath); + + // get all css selectors + const cssPaths = findAllFilesWithExt(".css", buildFolderPath, { + blackListedFolderPaths: blackListedFolderPaths, + whiteListedFolderPaths: whiteListedFolderPaths, + }); + const tables: ConversionTables = { + selector: {}, + ident: {}, + }; + cssPaths.forEach(async (cssPath) => { + const { conversionTables: newConversionTables } = await obfuscateCss({ + cssPath: cssPath, + conversionTables: conversionTables, + + prefix, + suffix, + mode, + classIgnore, + generatorSeed, + removeOriginalCss, + }); + + // Merge the conversion tables + Object.entries(newConversionTables.selector).forEach(([key, value]) => { + if (!tables.selector[key]) { + // If it doesn't exist, create a new entry + tables.selector[key] = value; + } + }); + Object.entries(newConversionTables.ident).forEach(([key, value]) => { + if (!tables.ident[key]) { + // If it doesn't exist, create a new entry + tables.ident[key] = value; + } + }); + }); + + return { + conversionTables: tables, + }; +} diff --git a/src/index.ts b/src/index.ts index c76898b..932442d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,19 +1,19 @@ -import fs from "fs"; -import path from "path"; +import type { Options, OptionalOptions } from "./types"; + +import fs from "node:fs"; +import path from "node:path"; import yargs from "yargs"; import { log, replaceJsonKeysInFiles, setLogLevel, findAllFilesWithExt, + getFilenameFromPath, } from "./utils"; - -import { createSelectorConversionJson } from "./handlers/css"; - +import { createSelectorConversionJson, obfuscateCss, obfuscateCssFiles } from "./handlers/css"; import Config from "./config"; -import { Options, OptionalOptions } from "./types"; -function obfuscate(options: Options) { +const obfuscate = async (options: Options) => { setLogLevel(options.logLevel); if (!options.enable) { @@ -31,19 +31,42 @@ function obfuscate(options: Options) { } log("info", "Obfuscation", "Creating/Updating class conversion JSON"); - createSelectorConversionJson({ + + // Create conversion tables and obfuscate CSS files + const { conversionTables } = await obfuscateCssFiles({ selectorConversionJsonFolderPath: options.classConversionJsonFolderPath, buildFolderPath: options.buildFolderPath, + whiteListedFolderPaths: [...options.whiteListedFolderPaths, ...(options.includeAnyMatchRegexes || [])], + blackListedFolderPaths: [...options.blackListedFolderPaths, ...(options.excludeAnyMatchRegexes || [])], mode: options.mode, - classNameLength: options.classLength, - classPrefix: options.classPrefix, - classSuffix: options.classSuffix, + prefix: options.classPrefix, + suffix: options.classSuffix, classIgnore: options.classIgnore, - enableObfuscateMarkerClasses: options.enableMarkers, - generatorSeed: options.generatorSeed === "-1" ? undefined : options.generatorSeed, + generatorSeed: options.generatorSeed, + removeOriginalCss: options.removeOriginalCss, }); + + // Save the conversion table to a JSON file + const jsonPath = path.join(process.cwd(), options.classConversionJsonFolderPath, "conversion.json"); + console.log({ jsonPath }); + fs.writeFileSync(jsonPath, JSON.stringify(conversionTables, null, 2)); + log("success", "CSS obfuscation:", `Saved conversion table to ${getFilenameFromPath(jsonPath)}`); + + // 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, + // }); log("success", "Obfuscation", "Class conversion JSON created/updated"); if ((options.includeAnyMatchRegexes && options.includeAnyMatchRegexes.length > 0) @@ -52,9 +75,9 @@ function obfuscate(options: Options) { } replaceJsonKeysInFiles({ + conversionTables: conversionTables, targetFolder: options.buildFolderPath, allowExtensions: options.allowExtensions, - selectorConversionJsonFolderPath: options.classConversionJsonFolderPath, contentIgnoreRegexes: options.contentIgnoreRegexes, @@ -63,20 +86,19 @@ function obfuscate(options: Options) { enableObfuscateMarkerClasses: options.enableMarkers, obfuscateMarkerClasses: options.markers, removeObfuscateMarkerClassesAfterObfuscated: options.removeMarkersAfterObfuscated, - removeOriginalCss: options.removeOriginalCss, enableJsAst: options.enableJsAst, }); } -function obfuscateCli() { +export const obfuscateCli = async () => { const argv = yargs.option("config", { alias: "c", type: "string", description: "Path to the config file" }).argv; - let configPath; + let configPath: string | undefined = undefined; // @ts-ignore if (argv.config) { @@ -100,8 +122,8 @@ function obfuscateCli() { } const config = new Config(configPath ? require(configPath) : undefined).get(); - obfuscate(config); + await obfuscate(config); log("success", "Obfuscation", "Completed~"); } -export { obfuscateCli, type OptionalOptions as Options }; +export { type OptionalOptions as Options }; diff --git a/src/types.ts b/src/types.ts index a45bdf6..8d9387d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,8 @@ +import type { ConversionTables } from "css-seasoning"; + type LogLevel = "debug" | "info" | "warn" | "error" | "success"; -type obfuscateMode = "random" | "simplify" | "simplify-seedable"; -type SelectorConversion = { [key: string]: string }; +type obfuscateMode = "random" | "simplify"; +type SelectorConversion = ConversionTables["selector"]; type Options = { enable: boolean; @@ -73,7 +75,7 @@ type OptionalOptions = { interface HtmlCharacterEntityConversion { [key: string]: string; - } +} export { type LogLevel, diff --git a/src/utils.ts b/src/utils.ts index 510218a..721ce5f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -6,6 +6,7 @@ import type { SelectorConversion, HtmlCharacterEntityConversion } from "./types"; +import { cssEscape, type ConversionTables } from "css-seasoning"; import { obfuscateCss } from "./handlers/css"; import { obfuscateHtmlClassNames } from "./handlers/html"; @@ -75,12 +76,11 @@ function setLogLevel(level: LogLevel) { const usedKeyRegistery = new Set(); - -function replaceJsonKeysInFiles( +const replaceJsonKeysInFiles = ( { + conversionTables, targetFolder, allowExtensions, - selectorConversionJsonFolderPath, contentIgnoreRegexes, @@ -89,13 +89,12 @@ function replaceJsonKeysInFiles( enableObfuscateMarkerClasses, obfuscateMarkerClasses, removeObfuscateMarkerClassesAfterObfuscated, - removeOriginalCss, enableJsAst, }: { + conversionTables: ConversionTables, targetFolder: string, allowExtensions: string[], - selectorConversionJsonFolderPath: string, contentIgnoreRegexes: RegExp[], @@ -104,17 +103,14 @@ function replaceJsonKeysInFiles( enableObfuscateMarkerClasses: boolean, obfuscateMarkerClasses: string[], removeObfuscateMarkerClassesAfterObfuscated: boolean, - removeOriginalCss: boolean, enableJsAst: boolean, - }) { + }) => { //ref: https://github.com/n4j1Br4ch1D/postcss-obfuscator/blob/main/utils.js - const classConversion: SelectorConversion = loadAndMergeJsonFiles(selectorConversionJsonFolderPath); - if (removeObfuscateMarkerClassesAfterObfuscated) { obfuscateMarkerClasses.forEach(obfuscateMarkerClass => { - classConversion[`.${obfuscateMarkerClass}`] = ""; + conversionTables.selector[cssEscape(`.${obfuscateMarkerClass}`)] = ""; }); } @@ -131,7 +127,6 @@ function replaceJsonKeysInFiles( } else if ( allowExtensions.includes(fileExt) ) { - let isTargetFile = true; if (whiteListedFolderPaths.length > 0) { isTargetFile = whiteListedFolderPaths.some((incloudPath) => { @@ -172,45 +167,9 @@ function replaceJsonKeysInFiles( const htmlMatch = fileContent.match(htmlRegex); if (htmlMatch) { const htmlOriginal = htmlMatch[0]; - // let html = htmlOriginal; - - //! rollback - // const tagContents = findHtmlTagContentsByClass(html, obfuscateMarkerClass); - // tagContents.forEach(tagContent => { - // const { obfuscatedContent, usedKeys } = obfuscateKeys(classConversion, tagContent, contentIgnoreRegexes); - // addKeysToRegistery(usedKeys); - // if (tagContent !== obfuscatedContent) { - // html = html.replace(tagContent, obfuscatedContent); - // log("debug", `Obscured keys under HTML tag in file:`, normalizePath(filePath)); - // } - // }); - - //! rollback - // const scriptTagContents = findHtmlTagContents(html, "script"); - // scriptTagContents.forEach(scriptTagContent => { - // const obfuscateScriptContent = obfuscateJs( - // scriptTagContent, - // obfuscateMarkerClass, - // classConversion, - // filePath, - // contentIgnoreRegexes, - // enableJsAst - // ); - // if (scriptTagContent !== obfuscateScriptContent) { - // html = html.replace(scriptTagContent, obfuscateScriptContent); - // log("debug", `Obscured keys under HTML script tag in file:`, normalizePath(filePath)); - // } - // }); - - //! rollback - // if (htmlOriginal !== html) { - // fileContent = fileContent.replace(htmlOriginal, html); - // } - - //! NEW const { obfuscatedContent, usedKeys } = obfuscateHtmlClassNames({ html: htmlOriginal, - selectorConversion: classConversion, + selectorConversion: conversionTables.selector, obfuscateMarkerClass: obfuscateMarkerClass, contentIgnoreRegexes: contentIgnoreRegexes, }); @@ -222,7 +181,7 @@ function replaceJsonKeysInFiles( } else { const obfuscateScriptContent = obfuscateJs(fileContent, obfuscateMarkerClass, - classConversion, + conversionTables.selector, filePath, contentIgnoreRegexes, enableJsAst @@ -241,7 +200,7 @@ function replaceJsonKeysInFiles( const obfuscateScriptContent = obfuscateJs( fileContent, enableJsAst ? "" : "jsx", - classConversion, + conversionTables.selector, filePath, contentIgnoreRegexes, enableJsAst @@ -254,7 +213,7 @@ function replaceJsonKeysInFiles( //! NEW const { obfuscatedContent, usedKeys } = obfuscateHtmlClassNames({ html: fileContent, - selectorConversion: classConversion, + selectorConversion: conversionTables.selector, contentIgnoreRegexes: contentIgnoreRegexes, }); @@ -262,7 +221,7 @@ function replaceJsonKeysInFiles( addKeysToRegistery(usedKeys); } else { const { obfuscatedContent, usedKeys } = obfuscateKeys( - classConversion, + conversionTables.selector, fileContent, contentIgnoreRegexes ); @@ -287,10 +246,9 @@ function replaceJsonKeysInFiles( replaceJsonKeysInFile(targetFolder); // Obfuscate CSS files - cssPaths.forEach((cssPath) => { - obfuscateCss(classConversion, cssPath, removeOriginalCss, !enableObfuscateMarkerClasses); - }); - + // cssPaths.forEach(async (cssPath) => { + // await obfuscateCss(classConversion, cssPath, removeOriginalCss, !enableObfuscateMarkerClasses); + // }); } function obfuscateKeys( @@ -468,34 +426,70 @@ function findContentBetweenMarker(content: string, targetStr: string, openMarker return truncatedContents; } -function addKeysToRegistery(usedKeys: Set | string[]) { +export function addKeysToRegistery(usedKeys: Set | string[]) { usedKeys.forEach((key) => { usedKeyRegistery.add(key); }); } /** - * Find all files with the specified extension in the build folder - * @param ext - the extension of the files to find (e.g. .css) "." is required - * @param targetFolderPath - the path to the folder to start searching from - * @returns - an array of file relative paths + * Find all files with the specified extension in the build folder. + * + * @param ext - the extension of the files to find (e.g. .css) "." is required. + * @param targetFolderPath - the path to the folder to start searching from. + * @param options - optional parameters. + * @param options.whiteListedFolderPaths - an array of folder paths to include in the search. + * @param options.blackListedFolderPaths - an array of folder paths to exclude from the search. Higher priority than whiteListedFolderPaths. + * @returns - an array of file relative paths. */ -function findAllFilesWithExt(ext: string, targetFolderPath: string): string[] { +const findAllFilesWithExt = ( + ext: string, targetFolderPath: string, + options?: { + whiteListedFolderPaths?: (string | RegExp)[], + blackListedFolderPaths?: (string | RegExp)[], + } +): string[] => { if (!fs.existsSync(targetFolderPath)) { return []; } const targetExtFiles: string[] = []; + const whiteList = options?.whiteListedFolderPaths || []; + const blackList = options?.blackListedFolderPaths || []; - function findCssFiles(dir: string) { + const findFiles = (dir: string) => { const files = fs.readdirSync(dir); files.forEach((file) => { const filePath = normalizePath(path.join(dir, file)); + // Check if the path is blacklisted (higher priority) + const isBlacklisted = blackList.some((excludePath) => { + if (typeof excludePath === "string") { + return filePath.includes(excludePath); + } + return excludePath.test(filePath); + }); + + if (isBlacklisted) { + return; // Skip this file/directory + } + + // Check if the path is whitelisted (if whitelist is not empty) + const isWhitelisted = whiteList.length === 0 || whiteList.some((includePath) => { + if (typeof includePath === "string") { + return filePath.includes(includePath); + } + return includePath.test(filePath); + }); + + if (!isWhitelisted) { + return; // Skip this file/directory + } + if (fs.statSync(filePath).isDirectory()) { - // if it's a directory, recursively search for CSS files - findCssFiles(filePath); + // if it's a directory, recursively search for files + findFiles(filePath); } else { // check if the file has the specified extension if (file.endsWith(ext)) { @@ -505,8 +499,8 @@ function findAllFilesWithExt(ext: string, targetFolderPath: string): string[] { }); } - // start searching for CSS files from the specified directory - findCssFiles(targetFolderPath); + // start searching for files from the specified directory + findFiles(targetFolderPath); return targetExtFiles; } @@ -637,10 +631,57 @@ function decodeKey(str: string) { return str; } +/** + * Convert a string to a number by summing the char codes of each character + * + * @param str - The string to convert + * @returns The sum of the char codes of each character in the string + * + * @example + * stringToNumber("abc") // returns 294 (97 + 98 + 99) + * stringToNumber("hello") // returns 532 (104 + 101 + 108 + 108 + 111) + */ +export const stringToNumber = (str: string) => { + return str.split("").reduce((acc, char) => { + return acc + char.charCodeAt(0); + }, 0); +} + + +/** + * Loads and merges all JSON files (Conversion Tables) in the specified folder. + * + * @param folderPath - The folder path to load the conversion tables from. + * @returns ConversionTables - The merged conversion tables. + */ +export const loadConversionTables = (folderPath: string): ConversionTables => { + const tables: ConversionTables = { + ident: {}, + selector: {}, + }; + + fs.readdirSync(folderPath).forEach((file: string) => { + const filePath = path.join(folderPath, file); + const fileData = JSON.parse(fs.readFileSync(filePath, "utf-8")); + + if (Object.keys(fileData).includes("ident") && Object.keys(fileData).includes("selector")) { + Object.assign(tables.ident, fileData.ident); + Object.assign(tables.selector, fileData.selector); + } else { + // if the file doesn't have ident, it should be selector + //? For backward compatibility + Object.assign(tables.selector, fileData); + } + }); + + return tables; +} + + export { getFilenameFromPath, log, normalizePath, loadAndMergeJsonFiles , replaceJsonKeysInFiles, setLogLevel, findContentBetweenMarker, replaceFirstMatch , findAllFilesWithExt, getRandomString, seedableSimplifyString, usedKeyRegistery - , obfuscateKeys, findClosestSymbolPosition, addKeysToRegistery, duplicationCheck + , obfuscateKeys, findClosestSymbolPosition, duplicationCheck , createKey, decodeKey, simplifyString }; From 10e0ac1fb5c97de4fc277752bf5af6f434684e33 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Sun, 20 Apr 2025 19:33:24 +0100 Subject: [PATCH 05/32] refactor: Remove deprecated functions and streamline path filtering logic --- src/handlers/css.ts | 232 ++------------------------------------------ src/index.ts | 31 ++---- src/types.ts | 38 ++------ src/utils.ts | 91 +++++++++-------- 4 files changed, 64 insertions(+), 328 deletions(-) diff --git a/src/handlers/css.ts b/src/handlers/css.ts index 95d3ea4..138270c 100644 --- a/src/handlers/css.ts +++ b/src/handlers/css.ts @@ -157,143 +157,6 @@ function getAllSelector(cssObj: any): any[] { return selectors; } -/** - * - * @deprecated will be replaced by new css transformer - */ -export function createSelectorConversionJson( - { - selectorConversionJsonFolderPath, - buildFolderPath, - - mode = "random", - classNameLength = 5, - classPrefix = "", - classSuffix = "", - classIgnore = [], - - enableObfuscateMarkerClasses = false, - generatorSeed = Math.random().toString().slice(2, 10), // take 8 digits from the random number - }: { - selectorConversionJsonFolderPath: string, - buildFolderPath: string, - - mode?: obfuscateMode, - classNameLength?: number, - classPrefix?: string, - classSuffix?: string, - classIgnore?: (string | RegExp)[], - - enableObfuscateMarkerClasses?: boolean, - generatorSeed?: string, - }) { - // if (!fs.existsSync(selectorConversionJsonFolderPath)) { - // fs.mkdirSync(selectorConversionJsonFolderPath); - // } - - // const selectorConversion = loadConversionTables(selectorConversionJsonFolderPath); - - // // pre-defined ".dark", mainly for tailwindcss dark mode - // if (enableObfuscateMarkerClasses) { - // selectorConversion[".dark"] = ".dark"; - // } - - // // get all css selectors - // const cssPaths = findAllFilesWithExt(".css", buildFolderPath); - // const selectors: string[] = []; - // cssPaths.forEach((cssPath) => { - // const cssContent = fs.readFileSync(cssPath, "utf-8"); - // const cssObj = css.parse(cssContent); - // selectors.push(...getAllSelector(cssObj)); - // }); - - // // remove duplicated selectors - // 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: { [key: string]: string[] } = {}; - - // for (let i = 0; i < uniqueSelectors.length; i++) { - // const originalSelector = uniqueSelectors[i]; - // const { extractedClasses } = extractClassFromSelector(originalSelector) || []; - // selectorClassPair[originalSelector] = extractedClasses; - // } - - // //? since a multi part selector normally grouped by multiple basic selectors - // //? so we need to obfuscate the basic selector first - // //? eg. ":is(.class1 .class2)" grouped by ".class1" and ".class2" - // // sort the selectorClassPair by the number of classes in the selector (from least to most) - // // and remove the selector with no class - // 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) => { - - // // apply ignore list - // if (classIgnore.some(regex => { - // if (typeof regex === "string") { - // return className === regex; - // } - // return new RegExp(regex).test(className) - // })) { - // return className; - // } - - // // try to get the obfuscated selector from the selectorConversion - // // if not found, create a new one - // let obfuscatedSelector = selectorConversion[`.${className}`]; - // if (!obfuscatedSelector) { - // const obfuscatedClass = createNewClassName(mode, className, classPrefix, classSuffix, classNameLength, generatorSeed); - // obfuscatedSelector = `.${obfuscatedClass}`; - // selectorConversion[`.${className}`] = obfuscatedSelector; - // } - - // // return the obfuscated class - // return obfuscatedSelector.slice(1) - // }); - - // // obfuscate the selector - // const { selector: obfuscatedSelector } = extractClassFromSelector(originalSelector, classes); - - // selectorConversion[originalSelector] = obfuscatedSelector; - // } - // } - - // const jsonPath = path.join(process.cwd(), selectorConversionJsonFolderPath, "conversion.json"); - // fs.writeFileSync(jsonPath, JSON.stringify(selectorConversion, null, 2)); - // if (duplicationCheck(Object.keys(selectorConversion))) { - // if (mode == "random") { - // 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 { - // 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"); - // } - // } -} - export function copyCssData(targetSelector: string, newSelectorName: string, cssObj: any) { function recursive(rules: any[]): any[] { return rules.map((item: any) => { @@ -349,11 +212,8 @@ export function renameCssSelector(oldSelector: string, newSelector: string, cssO return cssObj; } -/** - * - * @deprecated WIP - */ -export const obfuscateCss = async ({ + +const obfuscateCss = async ({ cssPath, removeOriginalCss, isFullObfuscation, @@ -451,86 +311,6 @@ export const obfuscateCss = async ({ } } -// export const obfuscateCss = async ( -// selectorConversion: ConversionTable, -// cssPath: string, -// replaceOriginalSelector = false, -// isFullObfuscation = false, -// outCssPath?: string, -// ) => { -// if (!outCssPath) { -// outCssPath = cssPath; -// } else if (!fs.existsSync(path.dirname(outCssPath))) { -// fs.mkdirSync(path.dirname(outCssPath)); -// } - -// const cssContent = fs.readFileSync(cssPath, "utf-8"); - -// let cssObj = css.parse(cssContent); -// const cssRulesCount = cssObj.stylesheet.rules.length; - -// if (isFullObfuscation) { -// Object.keys(selectorConversion).forEach((key) => { -// usedKeyRegistery.add(key); -// }); -// } else { -// // join all selectors start with ":" (eg. ":is") -// Object.keys(selectorConversion).forEach((key) => { -// if (key.startsWith(":")) { -// usedKeyRegistery.add(key); -// } -// }); - -// // join all selectors with action selectors -// const actionSelectors = getAllSelector(cssObj).filter((selector) => selector.match(findActionSelectorsRegex)); -// actionSelectors.forEach((actionSelector) => { -// usedKeyRegistery.add(actionSelector); -// }); - -// // join all Tailwind CSS [child] selectors (eg. ".\[\&_\.side-box\]\:absolute .side-box") -// const tailwindCssChildSelectors = getAllSelector(cssObj).filter((selector) => selector.startsWith(".\\[")); -// tailwindCssChildSelectors.forEach((tailwindCssChildSelector) => { -// usedKeyRegistery.add(tailwindCssChildSelector); -// }); - -// // join all child selectors (eg. ">*") -// const universalSelectors = getAllSelector(cssObj).filter((selector) => selector.includes(">")); -// universalSelectors.forEach((universalSelector) => { -// usedKeyRegistery.add(universalSelector); -// }); -// } - -// // modify css rules -// usedKeyRegistery.forEach((key) => { -// const originalSelectorName = key; -// const obfuscatedSelectorName = selectorConversion[key]; -// if (obfuscatedSelectorName) { -// if (replaceOriginalSelector) { -// cssObj = renameCssSelector(originalSelectorName, selectorConversion[key], cssObj); -// } else { -// cssObj = copyCssData(originalSelectorName, selectorConversion[key], cssObj); -// } -// } -// }); - -// if (replaceOriginalSelector) { -// log("info", "CSS rules:", `Modified ${usedKeyRegistery.size} CSS rules to ${getFilenameFromPath(cssPath)}`); -// } else { -// log("info", "CSS rules:", `Added ${cssObj.stylesheet.rules.length - cssRulesCount} new CSS rules to ${getFilenameFromPath(cssPath)}`); -// } - -// const cssOptions = { -// compress: true, -// }; -// const cssObfuscatedContent = css.stringify(cssObj, cssOptions); - -// const sizeBefore = Buffer.byteLength(cssContent, "utf8"); -// fs.writeFileSync(outCssPath, cssObfuscatedContent); -// const sizeAfter = Buffer.byteLength(cssObfuscatedContent, "utf8"); -// const percentChange = Math.round(((sizeAfter) / sizeBefore) * 100); -// log("success", "CSS obfuscated:", `Size from ${sizeBefore} to ${sizeAfter} bytes (${percentChange}%) in ${getFilenameFromPath(cssPath)}`); -// } - /** * */ @@ -571,15 +351,17 @@ export const obfuscateCssFiles = async ({ // Load and merge all JSON files in the selector conversion folder const conversionTables = loadConversionTables(selectorConversionJsonFolderPath); - // get all css selectors + // Get all CSS files using the unified path filtering function const cssPaths = findAllFilesWithExt(".css", buildFolderPath, { - blackListedFolderPaths: blackListedFolderPaths, - whiteListedFolderPaths: whiteListedFolderPaths, + whiteListedFolderPaths, + blackListedFolderPaths, }); + const tables: ConversionTables = { selector: {}, ident: {}, }; + cssPaths.forEach(async (cssPath) => { const { conversionTables: newConversionTables } = await obfuscateCss({ cssPath: cssPath, diff --git a/src/index.ts b/src/index.ts index 932442d..fe7e6ad 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,7 +10,7 @@ import { findAllFilesWithExt, getFilenameFromPath, } from "./utils"; -import { createSelectorConversionJson, obfuscateCss, obfuscateCssFiles } from "./handlers/css"; +import { obfuscateCssFiles } from "./handlers/css"; import Config from "./config"; const obfuscate = async (options: Options) => { @@ -36,8 +36,8 @@ const obfuscate = async (options: Options) => { const { conversionTables } = await obfuscateCssFiles({ selectorConversionJsonFolderPath: options.classConversionJsonFolderPath, buildFolderPath: options.buildFolderPath, - whiteListedFolderPaths: [...options.whiteListedFolderPaths, ...(options.includeAnyMatchRegexes || [])], - blackListedFolderPaths: [...options.blackListedFolderPaths, ...(options.excludeAnyMatchRegexes || [])], + whiteListedFolderPaths: options.whiteListedFolderPaths, + blackListedFolderPaths: options.blackListedFolderPaths, mode: options.mode, prefix: options.classPrefix, @@ -54,26 +54,7 @@ const obfuscate = async (options: Options) => { fs.writeFileSync(jsonPath, JSON.stringify(conversionTables, null, 2)); log("success", "CSS obfuscation:", `Saved conversion table to ${getFilenameFromPath(jsonPath)}`); - // 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, - // }); - log("success", "Obfuscation", "Class conversion JSON created/updated"); - - if ((options.includeAnyMatchRegexes && options.includeAnyMatchRegexes.length > 0) - || (options.excludeAnyMatchRegexes && options.excludeAnyMatchRegexes.length > 0)) { - log("warn", "Obfuscation", "'includeAnyMatchRegexes' and 'excludeAnyMatchRegexes' are deprecated, please use whiteListedFolderPaths and blackListedFolderPaths instead"); - } - + // Use the same unified paths for replacing JSON keys in files replaceJsonKeysInFiles({ conversionTables: conversionTables, targetFolder: options.buildFolderPath, @@ -81,8 +62,8 @@ const obfuscate = async (options: Options) => { contentIgnoreRegexes: options.contentIgnoreRegexes, - whiteListedFolderPaths: [...options.whiteListedFolderPaths, ...(options.includeAnyMatchRegexes || [])], - blackListedFolderPaths: [...options.blackListedFolderPaths, ...(options.excludeAnyMatchRegexes || [])], + whiteListedFolderPaths: options.whiteListedFolderPaths, + blackListedFolderPaths: options.blackListedFolderPaths, enableObfuscateMarkerClasses: options.enableMarkers, obfuscateMarkerClasses: options.markers, removeObfuscateMarkerClassesAfterObfuscated: options.removeMarkersAfterObfuscated, diff --git a/src/types.ts b/src/types.ts index 8d9387d..11bc364 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,10 +1,10 @@ import type { ConversionTables } from "css-seasoning"; -type LogLevel = "debug" | "info" | "warn" | "error" | "success"; -type obfuscateMode = "random" | "simplify"; -type SelectorConversion = ConversionTables["selector"]; +export type LogLevel = "debug" | "info" | "warn" | "error" | "success"; +export type obfuscateMode = "random" | "simplify"; +export type SelectorConversion = ConversionTables["selector"]; -type Options = { +export type Options = { enable: boolean; mode: obfuscateMode; buildFolderPath: string; @@ -20,14 +20,6 @@ type Options = { whiteListedFolderPaths: (string | RegExp)[]; blackListedFolderPaths: (string | RegExp)[]; - /** - * @deprecated - */ - includeAnyMatchRegexes?: RegExp[]; - /** - * @deprecated - */ - excludeAnyMatchRegexes?: RegExp[]; enableMarkers: boolean; markers: string[]; removeMarkersAfterObfuscated: boolean; @@ -38,7 +30,8 @@ type Options = { logLevel: LogLevel; } -type OptionalOptions = { + +export type OptionalOptions = { enable?: boolean; mode?: obfuscateMode; buildFolderPath?: string; @@ -54,14 +47,6 @@ type OptionalOptions = { whiteListedFolderPaths?: (string | RegExp)[]; blackListedFolderPaths?: (string | RegExp)[]; - /** - * @deprecated - */ - includeAnyMatchRegexes?: RegExp[]; - /** - * @deprecated - */ - excludeAnyMatchRegexes?: RegExp[]; enableMarkers?: boolean; markers?: string[]; removeMarkersAfterObfuscated?: boolean; @@ -73,15 +58,6 @@ type OptionalOptions = { logLevel?: LogLevel; } -interface HtmlCharacterEntityConversion { +export interface HtmlCharacterEntityConversion { [key: string]: string; } - -export { - type LogLevel, - type obfuscateMode, - type SelectorConversion, - type Options, - type OptionalOptions, - type HtmlCharacterEntityConversion -} \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index 721ce5f..77652fd 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -4,14 +4,51 @@ import NumberGenerator from "recoverable-random"; import type { LogLevel, SelectorConversion, - HtmlCharacterEntityConversion } from "./types"; import { cssEscape, type ConversionTables } from "css-seasoning"; -import { obfuscateCss } from "./handlers/css"; import { obfuscateHtmlClassNames } from "./handlers/html"; import { obfuscateJs } from "./handlers/js"; +// Add a new function for path filtering +/** + * Checks if a path should be included based on whitelist and blacklist rules + * + * @param filePath - The file path to check + * @param whiteListedFolderPaths - Paths to include + * @param blackListedFolderPaths - Paths to exclude (higher priority than whitelist) + * @returns - True if the path should be included, false otherwise + */ +function shouldIncludePath( + filePath: string, + whiteListedFolderPaths: (string | RegExp)[] = [], + blackListedFolderPaths: (string | RegExp)[] = [] +): boolean { + const normalizedPath = normalizePath(filePath); + + // Check if the path is blacklisted (higher priority) + const isBlacklisted = blackListedFolderPaths.some((excludePath) => { + if (typeof excludePath === "string") { + return normalizedPath.includes(excludePath); + } + return excludePath.test(normalizedPath); + }); + + if (isBlacklisted) { + return false; // Skip this file/directory + } + + // Check if the path is whitelisted (if whitelist is not empty) + const isWhitelisted = whiteListedFolderPaths.length === 0 || whiteListedFolderPaths.some((includePath) => { + if (typeof includePath === "string") { + return normalizedPath.includes(includePath); + } + return includePath.test(normalizedPath); + }); + + return isWhitelisted; +} + //! ==================== //! Log //! ==================== @@ -127,29 +164,8 @@ const replaceJsonKeysInFiles = ( } 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) { + // Use the unified path filtering function + if (!shouldIncludePath(filePath, whiteListedFolderPaths, blackListedFolderPaths)) { return; } @@ -461,29 +477,9 @@ const findAllFilesWithExt = ( const files = fs.readdirSync(dir); files.forEach((file) => { - const filePath = normalizePath(path.join(dir, file)); - - // Check if the path is blacklisted (higher priority) - const isBlacklisted = blackList.some((excludePath) => { - if (typeof excludePath === "string") { - return filePath.includes(excludePath); - } - return excludePath.test(filePath); - }); + const filePath = path.join(dir, file); - if (isBlacklisted) { - return; // Skip this file/directory - } - - // Check if the path is whitelisted (if whitelist is not empty) - const isWhitelisted = whiteList.length === 0 || whiteList.some((includePath) => { - if (typeof includePath === "string") { - return filePath.includes(includePath); - } - return includePath.test(filePath); - }); - - if (!isWhitelisted) { + if (!shouldIncludePath(filePath, whiteList, blackList)) { return; // Skip this file/directory } @@ -685,3 +681,4 @@ export { , obfuscateKeys, findClosestSymbolPosition, duplicationCheck , createKey, decodeKey, simplifyString }; + From 3c1ca32b4411e07f5f216904d579c94690c46f46 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Sun, 20 Apr 2025 19:53:16 +0100 Subject: [PATCH 06/32] refactor(css): Refactor CSS obfuscation with css-seasoning Refactors the CSS obfuscation process to leverage the `css-seasoning` library, replacing custom CSS parsing and manipulation logic. This change improves maintainability and leverages a dedicated library for CSS transformations. Removes the old CSS test file as the functionality is now covered by `css-seasoning`. --- package-lock.json | 4 +- src/__tests__/css.test.ts | 529 -------------------------------------- src/handlers/css.ts | 205 +-------------- src/handlers/html.ts | 3 +- src/utils.ts | 4 +- 5 files changed, 10 insertions(+), 735 deletions(-) delete mode 100644 src/__tests__/css.test.ts diff --git a/package-lock.json b/package-lock.json index 50d0aea..539af01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "next-css-obfuscator", - "version": "2.2.19", + "version": "3.0.0-b1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "next-css-obfuscator", - "version": "2.2.19", + "version": "3.0.0-b1", "license": "MIT", "dependencies": { "@babel/generator": "^7.23.6", diff --git a/src/__tests__/css.test.ts b/src/__tests__/css.test.ts deleted file mode 100644 index 0697f9b..0000000 --- a/src/__tests__/css.test.ts +++ /dev/null @@ -1,529 +0,0 @@ -import { describe, it, expect, test } from "vitest"; - -// @ts-ignore -import css from "css"; - -import { - copyCssData, - renameCssSelector, - extractClassFromSelector, -} from "../handlers/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; -} -` - -// function getCssRulesIncludedSelector(selector: string, cssObj: any): any[] { -// function recursive(rules: any[]) { -// for (const item of rules) { -// if (item.rules) { -// const result: any = recursive(item.rules); -// if (result !== null) { -// return [{ ...item, rules: result }]; -// } -// } else if (item.selectors.includes(selector)) { -// // remove empty selectors -// item.selectors = item.selectors.filter((selector: any) => selector !== ""); - -// return [{ ...item, selectors: [selector] }]; -// } -// } -// return null; -// } -// return recursive(cssObj.stylesheet.rules) || []; -// } - -// describe("getCssRulesIncludedSelector", () => { -// it("should return the correct CSS rules (single selector, no nested rule)", () => { -// const cssObj = css.parse(testCss); - -// const selector = ".s0-1"; -// 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 } } }]; - -// const result = getCssRulesIncludedSelector(selector, cssObj); -// expect(result).toEqual(expectedOutput); -// }); - -// it("should return the correct CSS rules (multiple nested rules)", () => { -// const cssObj = css.parse(testCss); - -// const selector = ".s2-3"; -// const expectedOutput = [{ "type": "media", "media": "(min-width: 640px)", "rules": [{ "type": "media", "media": "(min-width: 768px)", "rules": [{ "type": "rule", "selectors": [".s2-3"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181818", "position": { "start": { "line": 26, "column": 13 }, "end": { "line": 26, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181819", "position": { "start": { "line": 27, "column": 13 }, "end": { "line": 27, "column": 27 } } }], "position": { "start": { "line": 24, "column": 10 }, "end": { "line": 28, "column": 10 } } }], "position": { "start": { "line": 15, "column": 5 }, "end": { "line": 29, "column": 6 } } }], "position": { "start": { "line": 7, "column": 1 }, "end": { "line": 36, "column": 2 } } }]; - -// const result = getCssRulesIncludedSelector(selector, cssObj); -// expect(result).toEqual(expectedOutput); -// }); - -// it("should return the correct CSS rules (multiple selector in same rule)", () => { -// const cssObj = css.parse(testCss); - -// const selector = ".s2-2-2"; -// const expectedOutput = [{ "type": "media", "media": "(min-width: 640px)", "rules": [{ "type": "media", "media": "(min-width: 768px)", "rules": [{ "type": "rule", "selectors": [".s2-2-2"], "declarations": [{ "type": "declaration", "property": "background", "value": "#181816", "position": { "start": { "line": 22, "column": 13 }, "end": { "line": 22, "column": 32 } } }, { "type": "declaration", "property": "color", "value": "#181817", "position": { "start": { "line": 23, "column": 13 }, "end": { "line": 23, "column": 27 } } }], "position": { "start": { "line": 20, "column": 10 }, "end": { "line": 24, "column": 10 } } }], "position": { "start": { "line": 15, "column": 5 }, "end": { "line": 29, "column": 6 } } }], "position": { "start": { "line": 7, "column": 1 }, "end": { "line": 36, "column": 2 } } }]; - -// const result = getCssRulesIncludedSelector(selector, cssObj); -// expect(result).toEqual(expectedOutput); -// }); - -// it("should return the empty array", () => { -// const cssObj = css.parse(testCss); - -// const selector = ".s2-2-3"; -// const expectedOutput: [] = []; - -// const result = getCssRulesIncludedSelector(selector, cssObj); -// expect(result).toEqual(expectedOutput); -// }); -// }); - - -//! ================================ -//! renameCssSelector -//! ================================ - -describe("renameCssSelector", () => { - it("should rename the CSS selector (single selector, no nested rule)", () => { - const cssObj = css.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 = renameCssSelector(oldSelector, newSelector, cssObj); - expect(result.stylesheet.rules).toEqual(expectedOutput); - }); - - it("should rename the CSS selector (multiple nested media queries)", () => { - const cssObj = css.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 = renameCssSelector(oldSelector, newSelector, cssObj); - expect(result.stylesheet.rules).toEqual(expectedOutput); - }); -}); - -//! ================================ -//! copyCssData -//! ================================ - -describe("copyCssData", () => { - it("should copy the CSS data (single selector, no nested rule)", () => { - const cssObj = css.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 = copyCssData(targetSelector, newSelectorName, cssObj); - expect(result.stylesheet.rules).toEqual(expectedOutput); - }); - - it("should copy the CSS data (multiple nested rules)", () => { - const cssObj = css.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 = copyCssData(targetSelector, newSelectorName, cssObj); - expect(result.stylesheet.rules).toEqual(expectedOutput); - }); - - it("should copy the CSS data (multiple selector in same rule)", () => { - const cssObj = css.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 = copyCssData(targetSelector, newSelectorName, cssObj); - expect(result.stylesheet.rules).toEqual(expectedOutput); - }); - - it("should copy the CSS data (same selector with different declarations)", () => { - const cssObj = css.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 = copyCssData(targetSelector, newSelectorName, cssObj); - expect(result.stylesheet.rules).toEqual(expectedOutput); - }); -}); - -//! ================================ -//! extractClassFromSelector -//! ================================ - -describe("extractClassFromSelector", () => { - - it("should extract single class from simple selector", () => { - const sample = ".example htmlTag"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["example"] - }); - }); - - test("should extract multiple classes from complex selector", () => { - const sample = ":is(.some-class .some-class\\:bg-dark::-moz-placeholder)[data-active=\'true\']"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["some-class", "some-class\\:bg-dark"] - }); - }); - - test("should handle selector with no classes", () => { - const sample = "div"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: [] - }); - }); - - test("should handle selector with action pseudo-classes and not extract them", () => { - const sample = ".btn:hover .btn-active::after"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["btn", "btn-active"] - }); - }); - - 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"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["btn-moz", "btn-ms", "btn-webkit", "btn-o"] - }); - }); - - test("should handle selector with escaped characters", () => { - const sample = ".escaped\\:class:action"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["escaped\\:class"] - }); - }); - - test("should handle selector with multiple classes separated by spaces", () => { - const sample = ".class1 .class2 .class3"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["class1", "class2", "class3"] - }); - }); - - test("should handle selector with multiple classes separated by commas", () => { - const sample = ".class1, .class2, .class3"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["class1", "class2", "class3"] - }); - }); - - test("should handle selector with a combination of classes and ids", () => { - const sample = ".class1 #id .class2"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["class1", "class2"] - }); - }); - - test("should handle [attribute] selector", () => { - const sample = ".class1[data-attr=\"value\"] .class2[data-attr='value']"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["class1", "class2"] - }); - }); - - 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"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["class1\\:hover\\:class2", "class3\\:hover\\:class4", "class5", "class6\\:hover\\:class7", "hover\\:class8\\:class9"] - }); - }); - - test("should ignore [attribute] selector that not in the same scope as class", () => { - const sample = ":is(.class1 .class2\\:class3\\:\\!class4)[aria-selected=\"true\"]"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["class1", "class2\\:class3\\:\\!class4"] - }); - }); - - test("should return null for invalid input types", () => { - // Act & Assert - // @ts-ignore - expect(() => extractClassFromSelector(null)).toThrow(TypeError); - // @ts-ignore - expect(() => extractClassFromSelector(undefined)).toThrow(TypeError); - expect(() => extractClassFromSelector(123 as any)).toThrow(TypeError); - }); - - - //? ********************* - //? Tailwind CSS - //? ********************* - test("should handle Tailwind CSS important selector '!'", () => { - const sample = ".\\!my-0 .some-class\\:\\!bg-white"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["\\!my-0", "some-class\\:\\!bg-white"] - }) - }); - - test("should handle Tailwind CSS selector with start with '-'", () => { - const sample = ".-class-1"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["-class-1"] - }) - }); - - test("should handle Tailwind CSS selector with '.' at the number", () => { - const sample = ".class-0\\.5 .class-1\\.125"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["class-0\\.5", "class-1\\.125"] - }) - }); - - test("should handle Tailwind CSS selector with '/' at the number", () => { - const sample = ".class-1\\/2"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["class-1\\/2"] - }) - }); - - test("should handle Tailwind CSS selector with '%' at the number", () => { - const sample = ".\\[\\.class1\\]\\:to-85\\%"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["\\[\\.class1\\]\\:to-85\\%"] - }) - }); - - test("should handle Tailwind CSS universal selector", () => { - const sample = ".\\*\\:class1 .class2\\*\\:class3 .class4\\*:.class5"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["\\*\\:class1", "class2\\*\\:class3", "class4\\*", "class5"] - }) - }); - - test("should handle Tailwind CSS [custom parameter] selector", () => { - const sample = ".class1\\[100\\] .class2-\\[200\\]"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["class1\\[100\\]", "class2-\\[200\\]"] - }) - }); - - test("should handle Tailwind CSS [custom parameter] selector with escaped characters", () => { - const sample = ".class1\\[1em\\] .class2-\\[2em\\] .class3\\[3\\%\\] .class4-\\[4\\%\\]"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["class1\\[1em\\]", "class2-\\[2em\\]", "class3\\[3\\%\\]", "class4-\\[4\\%\\]"] - }) - }); - - test("should handle complex Tailwind CSS [custom parameter] selector", () => { - const sample = ".w-\\[calc\\(10\\%\\+5px\\)\\]"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["w-\\[calc\\(10\\%\\+5px\\)\\]"] - }) - }); - - test("should ignore Tailwind CSS [custom parameter] selector that not in the same scope as class", () => { - const sample = ":is(.class1)[100]"; - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: ["class1"] - }) - }); - - 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 - `; - - // .\[\&_a\[class\]\]:hover <= will not work, (?:\\\[(?:[^\[\]\s]*|(?R))*\\\]))+) , javascript regex does not support recursive regex - - // Act - const result = extractClassFromSelector(sample); - - // Assert - expect(result).toEqual({ - selector: sample, - extractedClasses: [ - "\\[\\&\\>\\class1\\]", "\\[\\&_class2\\]", - "\\[\\&_\\.class3\\]\\:class4", "\\[\\&_\\#id1\\]", - "\\[\\&_\\#id2\\]\\:class5", "\\[\\&_\\#id\\]\\:class6" - ] - }) - }); -}); diff --git a/src/handlers/css.ts b/src/handlers/css.ts index 138270c..0cef632 100644 --- a/src/handlers/css.ts +++ b/src/handlers/css.ts @@ -1,217 +1,20 @@ -import type { obfuscateMode, SelectorConversion } from "../types"; +import type { obfuscateMode } from "../types"; import path from "node:path"; import fs from "node:fs"; -// @ts-ignore -import css from "css"; -import NumberGenerator from "recoverable-random"; -import { initTransform, transform, ConversionTable, cssEscape, type ConversionTables, type TransformProps } from "css-seasoning"; +import { initTransform, transform, type ConversionTables, type TransformProps } from "css-seasoning"; import lightningcssInit, { transform as lightningcssTransform } from "lightningcss-wasm"; +// TODO: html failed with . + import { log, - getRandomString, - seedableSimplifyString, - simplifyString, - loadAndMergeJsonFiles, findAllFilesWithExt, - usedKeyRegistery, getFilenameFromPath, - duplicationCheck, - createKey, - decodeKey, stringToNumber, loadConversionTables, } from "../utils"; -let randomStringGeneraterStateCode: string | undefined = undefined; -let currentAlphabetPoistion = 1; -function createNewClassName( - mode: obfuscateMode, - className: string, - classPrefix: string = "", - classSuffix: string = "", - classNameLength: number = 5, - seed: string = Math.random().toString() -) { - let newClassName = className; - - let { rngStateCode, randomString }: { rngStateCode: string, randomString: string } = { rngStateCode: "", randomString: "" }; - - //? Based on the mechanism behind `recoverable-random` library - //? `stateCode` is directly equivalent to the `seed` - //? so recover the stateCode is the same as setting the seed - //? so the seed input `seedableSimplifyString` - //? in the following usage is meaningless - //? :) - - switch (mode) { - case "random": - ({ rngStateCode, randomString } = getRandomString(classNameLength, seed, undefined, className)); - break; - // case "simplify-seedable": - // ({ rngStateCode, randomString } = seedableSimplifyString(className, seed, seed + NumberGenerator.stringToSeed(className).toString())); - // break; - case "simplify": - randomString = simplifyString(currentAlphabetPoistion); - currentAlphabetPoistion++; - break; - default: - break; - } - - newClassName = randomString; - randomStringGeneraterStateCode = rngStateCode; - - if (classPrefix) { - newClassName = `${classPrefix}${newClassName}`; - } - if (classSuffix) { - newClassName = `${newClassName}${classSuffix}`; - } - - return newClassName; -} - -//? CSS action selectors always at the end of the selector -//? and they can be stacked, eg. "class:hover:active" -//? action selectors can start with ":" or "::" -const findActionSelectorsRegex = /(?)/g; - -/** - * Extracts classes from a CSS selector. - * - * @param selector - The CSS selector to extract classes from. - * @param replacementClassNames - The replacement class names. - * The position of the class name in the array should match the - * position of the class in the selector that you want to replece. - * @returns An array of extracted classes. - * - * @example - * // Returns: ["some-class", "some-class", "bg-white", "some-class", "bg-dark"] - * extractClassFromSelector(":is(.some-class .some-class\\:!bg-white .some-class\\:bg-dark::-moz-placeholder)[data-active=\'true\']"); - * - * @example - * // Returns: [] - * extractClassFromSelector("div"); - */ -export function extractClassFromSelector(selector: string, replacementClassNames?: (string | undefined)[]) { - //? "\\[\w\%\:\.\!\*\<\>\/]" handle escaped characters - //? "(?:\\\[(?:[^\[\]\s])*\\\]))+)" handle [attribute / Tailwind CSS custom parameter] selector - const extractClassRegex = /(?<=[.:!]|(?\/]|(?:\\\[(?:[^\[\]\s])*\\\]))+)(?![\w\-]*\()/g; - - const vendorPseudoClassRegexes = [ - /::?-moz-[\w-]+/g, // Firefox - /::?-ms-[\w-]+/g, // Internet Explorer, Edge - /::?-webkit-[\w-]+/g, // Safari, Chrome, and Opera - /::?-o-[\w-]+/g, // Opera (old ver) - ] - - // temporary remove action selectors - selector = selector.replace(findActionSelectorsRegex, (match) => { - return createKey(match); - }); - - // temporary remove vendor pseudo classes - vendorPseudoClassRegexes.forEach((regex, i) => { - selector = selector.replace(regex, (match) => { - return createKey(match); - }); - }); - - // extract classes - let classes = selector.match(extractClassRegex) as string[] | undefined; - - // replace classes with replacementClassNames - if (replacementClassNames !== undefined) { - selector = selector.replace(extractClassRegex, (originalClassName) => { - return replacementClassNames.shift() || originalClassName; - }); - } - - // place back the pseudo classes - selector = decodeKey(selector); - - return { - selector: selector, - extractedClasses: classes || [] - }; -} - -function getAllSelector(cssObj: any): any[] { - const selectors: string[] = []; - function recursive(rules: any[]) { - for (const item of rules) { - if (item.rules) { - recursive(item.rules); - } else if (item.selectors) { - // remove empty selectors - item.selectors = item.selectors.filter((selector: any) => selector !== ""); - - selectors.push(...item.selectors); - } - } - return null; - } - recursive(cssObj.stylesheet.rules); - return selectors; -} - -export function copyCssData(targetSelector: string, newSelectorName: string, cssObj: any) { - function recursive(rules: any[]): any[] { - return rules.map((item: any) => { - if (item.rules) { - let newRules = recursive(item.rules); - if (Array.isArray(newRules)) { - newRules = newRules.flat(); - } - return { ...item, rules: newRules }; - } else if (item.selectors) { - // remove empty selectors - item.selectors = item.selectors.filter((selector: any) => selector !== ""); - - // check if the selector is the target 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; -} - -export function renameCssSelector(oldSelector: string, newSelector: string, cssObj: any) { - function recursive(rules: any[]): any[] { - return rules.map((item: any) => { - if (item.rules) { - return { ...item, rules: recursive(item.rules) }; - } else if (item.selectors) { - // remove empty selectors - item.selectors = item.selectors.filter((selector: any) => selector !== ""); - - let updatedSelectors = item.selectors.map((selector: any) => - selector === oldSelector ? newSelector : selector - ); - - return { ...item, selectors: updatedSelectors }; - } else { - return item; - } - }); - } - - cssObj.stylesheet.rules = recursive(cssObj.stylesheet.rules); - return cssObj; -} - const obfuscateCss = async ({ cssPath, diff --git a/src/handlers/html.ts b/src/handlers/html.ts index 3b32b82..4463138 100644 --- a/src/handlers/html.ts +++ b/src/handlers/html.ts @@ -4,6 +4,7 @@ import * as htmlparser2 from "htmlparser2"; import { escape as htmlEscape } from "html-escaper"; import { log, obfuscateKeys } from "../utils"; import { obfuscateJs } from "./js"; +import { cssUnescape } from "css-seasoning"; /** * @deprecated @@ -257,7 +258,7 @@ function obfuscateHtmlClassNames({ isScriptTag = false; let obfuscatedScriptContent = scriptContent; Object.keys(selectorConversion).forEach((key) => { - const className = key.slice(1); + const className = cssUnescape(key).slice(1); const obfuscatedJs = obfuscateJs( obfuscatedScriptContent, className, diff --git a/src/utils.ts b/src/utils.ts index 77652fd..5564549 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -5,7 +5,7 @@ import type { LogLevel, SelectorConversion, } from "./types"; -import { cssEscape, type ConversionTables } from "css-seasoning"; +import { cssEscape, cssUnescape, type ConversionTables } from "css-seasoning"; import { obfuscateHtmlClassNames } from "./handlers/html"; import { obfuscateJs } from "./handlers/js"; @@ -279,7 +279,7 @@ function obfuscateKeys( Object.keys(selectorConversion).forEach((key) => { const fileContentOriginal = fileContent; // let keyUse = escapeRegExp(key.slice(1).replace(/\\/g, "")); - let keyUse = key.slice(1); + let keyUse = cssUnescape(key).slice(1); //! deprecated // if (useHtmlEntity) { From 7cb985d4c424a4458e2a72db9e76da899c44da63 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Mon, 21 Apr 2025 20:56:58 +0100 Subject: [PATCH 07/32] fix(html): fix incorrect html class name truncation --- src/__tests__/html.test.ts | 31 -------- src/handlers/html.ts | 154 +------------------------------------ src/utils.ts | 15 +--- 3 files changed, 7 insertions(+), 193 deletions(-) diff --git a/src/__tests__/html.test.ts b/src/__tests__/html.test.ts index 5319c8e..44693e7 100644 --- a/src/__tests__/html.test.ts +++ b/src/__tests__/html.test.ts @@ -3,40 +3,9 @@ import type { SelectorConversion } from '../types'; import { describe, it, expect } from "vitest"; import { - findHtmlTagContentsByClass, obfuscateHtmlClassNames, } from "../handlers/html"; - -//! ================================ -//! findHtmlTagContentsByClass -//! ================================ - -/** - * @deprecated - */ -describe("findHtmlTagContentsByClass", () => { - const content = `
12345678
901234
56789
0123456
`; - - it("should return the correct content within the tag that with a given class", () => { - const targetClass = "test1"; - - const expectedOutput = ['
12345678
901234
56789
']; - - const result = findHtmlTagContentsByClass(content, targetClass); - expect(result).toEqual(expectedOutput); - }); - - it("should return empty array if no content found", () => { - const targetClass = "test5"; - - const expectedOutput: unknown[] = []; - - const result = findHtmlTagContentsByClass(content, targetClass); - expect(result).toEqual(expectedOutput); - }); -}); - //! ================================ //! obfuscateHtmlClassNames //! ================================ diff --git a/src/handlers/html.ts b/src/handlers/html.ts index 4463138..2ba059d 100644 --- a/src/handlers/html.ts +++ b/src/handlers/html.ts @@ -2,153 +2,11 @@ import type { SelectorConversion } from "../types"; import * as htmlparser2 from "htmlparser2"; import { escape as htmlEscape } from "html-escaper"; -import { log, obfuscateKeys } from "../utils"; +import { obfuscateKeys } from "../utils"; import { obfuscateJs } from "./js"; import { cssUnescape } from "css-seasoning"; -/** - * @deprecated - */ -function findHtmlTagContentsRecursive( - content: string, - targetTag: string, - targetClass: string | null = null, - foundTagContents: string[] = [], - deep = 0, - maxDeep = -1, -) { - let contentAfterTag = content; - const startTagWithClassRegexStr = targetClass - ? // ref: https://stackoverflow.com/a/16559544 - `(<\\w+?\\s+?class\\s*=\\s*['\"][^'\"]*?\\b${targetClass}\\b)` - : ""; - const startTagRegexStr = `(<${targetTag}[\\s|>])`; - const endTagRegexStr = `(<\/${targetTag}>)`; - - // clear content before the start tag - 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(")`, "i"); - const remainingHtmlTagMatch = remainingHtml.match(remainingHtmlTagRegex); - if (remainingHtmlTagMatch) { - if (maxDeep === -1 || deep < maxDeep) { - return findHtmlTagContentsRecursive( - remainingHtml, - targetTag, - targetClass, - foundTagContents, - deep + 1, - maxDeep, - ); - } - log("warn", "HTML search:", "Max deep reached, recursive break"); - return foundTagContents; - } - } - - return foundTagContents; -} - -/** - * @deprecated - */ -function findHtmlTagContents( - content: string, - targetTag: string, - targetClass: string | null = null, -) { - return findHtmlTagContentsRecursive(content, targetTag, targetClass); -} - -/** - * @deprecated - */ -function findHtmlTagContentsByClass(content: string, targetClass: string) { - 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); - } - return []; -} - -function obfuscateHtmlClassNames({ +export const obfuscateHtmlClassNames = ({ html, selectorConversion, obfuscateMarkerClass = "", @@ -158,7 +16,7 @@ function obfuscateHtmlClassNames({ selectorConversion: SelectorConversion; obfuscateMarkerClass?: string; contentIgnoreRegexes?: RegExp[]; -}) { +}) => { const voidTags = [ "area", "base", @@ -295,9 +153,3 @@ function obfuscateHtmlClassNames({ usedKeys: Array.from(new Set(usedKeys)), }; } - -export { - findHtmlTagContents, - findHtmlTagContentsByClass, - obfuscateHtmlClassNames, -}; diff --git a/src/utils.ts b/src/utils.ts index 5564549..17e9935 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -267,12 +267,12 @@ const replaceJsonKeysInFiles = ( // }); } -function obfuscateKeys( +const obfuscateKeys = ( selectorConversion: SelectorConversion, fileContent: string, contentIgnoreRegexes: RegExp[] = [], useHtmlEntity: boolean = false -) { +) => { //ref: https://github.com/n4j1Br4ch1D/postcss-obfuscator/blob/main/utils.js const usedKeys = new Set(); @@ -281,20 +281,13 @@ function obfuscateKeys( // let keyUse = escapeRegExp(key.slice(1).replace(/\\/g, "")); let keyUse = cssUnescape(key).slice(1); - //! deprecated - // if (useHtmlEntity) { - // const regex = new RegExp(`(${Object.keys(HTML_CHARACTER_ENTITY_CONVERSION).join("|")})`, "g"); - // keyUse = keyUse.replace(regex, (m: string) => { - // return HTML_CHARACTER_ENTITY_CONVERSION[m] - // }); - // } - keyUse = escapeRegExp(keyUse.replace(/\\/g, "")); + keyUse = escapeRegExp(keyUse.replace(/\\/g, "")); // escape the key //? sample: "text-sm w-full\n text-right\n p-2 flex gap-2 hover:bg-gray-100 dark:hover:bg-red-700 text-right" let exactMatchRegex = new RegExp(`([\\s"'\\\`]|^)(${keyUse})(?=$|[\\s"'\\\`]|\\\\n|\\\\",|\\\\"})`, 'g'); // match exact wording & avoid ` ' "" // exactMatchRegex = new RegExp(`([\\s"'\\\`]|^)(${keyUse})(?=$|[\\s"'\\\`])`, 'g'); // match exact wording & avoid ` ' "" - const replacement = `$1` + selectorConversion[key].slice(1).replace(/\\/g, ""); + const replacement = `$1` + selectorConversion[key].slice(1).replace(/\\/g, "").slice(1); const matches = fileContent.match(exactMatchRegex); const originalObscuredContentPairs = matches?.map((match) => { From a290b14b5e42afedb666d2111e1779464ccd1cb0 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Mon, 21 Apr 2025 21:41:48 +0100 Subject: [PATCH 08/32] refactor: Simplify and export utility functions Refactors utility functions for better organization and reusability. - Removes unused random string generation and string simplification functions. - Exports previously internal functions to enhance module accessibility and facilitate testing. - Streamlines the codebase by removing dead code and improving overall structure. --- src/__tests__/utils.test.ts | 186 ------------------------------------ src/config.ts | 6 +- src/handlers/js-ast.ts | 18 ++-- src/handlers/js.ts | 25 ++--- src/utils.ts | 180 +++++----------------------------- 5 files changed, 44 insertions(+), 371 deletions(-) diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts index f921647..48c34e6 100644 --- a/src/__tests__/utils.test.ts +++ b/src/__tests__/utils.test.ts @@ -2,10 +2,7 @@ import { describe, it, expect, test, beforeEach } from "vitest"; import { findContentBetweenMarker, getFilenameFromPath, - getRandomString, - seedableSimplifyString, duplicationCheck, - simplifyString, } from "../utils"; import NumberGenerator from "recoverable-random"; @@ -139,149 +136,6 @@ describe("getFilenameFromPath", () => { }); -//! ================================ -//! getRandomString -//! ================================ - -describe("getRandomString", () => { - let rng: NumberGenerator; - - beforeEach(() => { - rng = new NumberGenerator(); - }); - - test("should generate a random string of a given length", () => { - // Arrange - const length = 10; - const seed = "testSeed"; - rng = new NumberGenerator(seed); // Mocked RNG for consistent results - - // Act - const result = getRandomString(length, seed); - - // Assert - expect(result.randomString).toHaveLength(length); - expect(result.randomString).toMatch(/^[a-z][a-z0-9-_]+$/); - }); - - test("should recover RNG state if state code is provided", () => { - // Arrange - const length = 10; - const seed = "testSeed"; - const stateCode = "someStateCode"; - rng = new NumberGenerator(seed); // Mocked RNG for consistent results - const initialStateCode = rng.getStateCode(); - - // Act - const result = getRandomString(length, seed, stateCode); - - // Assert - expect(result.rngStateCode).not.toBe(initialStateCode); - }); - - test("should throw an error if length is not a positive integer", () => { - // Arrange - const invalidLengths = [0, -1, 1.5, NaN, Infinity]; - - // Act & Assert - invalidLengths.forEach(length => { - expect(() => getRandomString(length as any)).toThrow(); - }); - }); - - test("should handle edge case where length is 1", () => { - // Arrange - const length = 1; - - // Act - const result = getRandomString(length); - - // Assert - expect(result.randomString).toHaveLength(length); - expect(result.randomString).toMatch(/^[a-z]$/); - }); - - test("should return a valid rngStateCode", () => { - // Arrange - const length = 10; - - // Act - const result = getRandomString(length); - - // Assert - expect(result.rngStateCode).toBeDefined(); - expect(typeof parseInt(result.rngStateCode)).toBe("number"); - }); -}); - -//! ================================ -//! seedableSimplifyString -//! ================================ - -describe("seedableSimplifyString", () => { - let rng: NumberGenerator; - - beforeEach(() => { - rng = new NumberGenerator("default-seed"); - }); - - test("should throw an error for empty string", () => { - // Act & Assert - expect(() => seedableSimplifyString("")).toThrow("String can not be empty"); - }); - - test("should return a simplified string and rng state code", () => { - // Arrange - const input = "a1e2i3o4u5w6_-"; - - // Act - const result = seedableSimplifyString(input, "seed"); - - // Assert - expect(result.randomString.length).toBeLessThan(input.length); - expect(typeof parseInt(result.rngStateCode)).toBe("number"); - }); - - test("should recover RNG state from state code", () => { - // Arrange - const stateCode = "some-state-code"; - const input = "test"; - rng.recoverState(stateCode); - const expectedStateCode = rng.getStateCode(); - - // Act - const result = seedableSimplifyString(input, undefined, stateCode); - - // Assert - expect(result.rngStateCode).toBe(expectedStateCode); - }); - - test("should handle strings without vowels or numbers", () => { - // Arrange - const input = "bcdfghjklmnpqrstvxyz"; - const expectedOutput = input; // No vowels or numbers to remove - - // Act - const result = seedableSimplifyString(input, "seed"); - - // Assert - expect(result.randomString).toBe(expectedOutput); - }); - - test("should handle strings with only vowels and numbers", () => { - // Arrange - const input = "aeiou12345"; - const expectedOutput = ""; // All characters should be removed - - // Act - const result = seedableSimplifyString(input, "seed"); - - // Assert - expect(result.randomString).toHaveLength(1); // Should contain one random character - }); -}); - - //! ================================ //! duplicationCheck //! ================================ @@ -376,43 +230,3 @@ describe("duplicationCheck", () => { expect(result).toBe(false); }); }); - - -//! ================================ -//! simplifyString -//! ================================ - -describe("simplifyString", () => { - - test.each([ - { position: 1, expected: "a" }, - { position: 26, expected: "z" }, - { position: 27, expected: "aa" }, - { position: 52, expected: "az" }, - { position: 53, expected: "ba" }, - { position: 702, expected: "zz" }, - { position: 703, expected: "aaa" }, - ])("returns correct string for position $position", ({ position, expected }) => { - // Act - const result = simplifyString(position); - - // Assert - expect(result).toBe(expected); - }); - - test("throws error for negative position", () => { - // Arrange - const input = -1; - - // Act & Assert - expect(() => simplifyString(input)).toThrow("Position must be a positive integer"); - }); - - test("throws error for non-integer position", () => { - // Arrange - const input = 27.5; - - // Act & Assert - expect(() => simplifyString(input)).toThrow("Position must be a positive integer"); - }); -}); \ No newline at end of file diff --git a/src/config.ts b/src/config.ts index 15409dd..2e48a34 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,6 +1,6 @@ import type { Options, OptionalOptions } from "./types"; -const defaultOptions: Options = { +export const defaultOptions: Options = { enable: true, // Enable or disable the plugin. mode: "random", // Obfuscate mode, "random", "simplify" or "simplify-seedable". buildFolderPath: ".next", // Build folder of your project. @@ -36,7 +36,7 @@ const defaultOptions: Options = { logLevel: "info", // Log level }; -class Config { +export class Config { private options: Options; constructor(options?: OptionalOptions) { @@ -55,4 +55,4 @@ class Config { } } -export default Config; \ No newline at end of file +export default Config; diff --git a/src/handlers/js-ast.ts b/src/handlers/js-ast.ts index 44907e8..8b368e3 100644 --- a/src/handlers/js-ast.ts +++ b/src/handlers/js-ast.ts @@ -14,12 +14,12 @@ import { type SelectorConversion } from "../types"; * @param stripUnnecessarySpace - whether to strip unnecessary space in the className, e.g. " a b c " => "a b c" * @returns - the obfuscated code and the used keys */ -function obfuscateJsWithAst( +export const obfuscateJsWithAst = ( code: string, selectorConversion: SelectorConversion | undefined, startingKeys: string[] = [], stripUnnecessarySpace: boolean = true -) { +) => { const ast = parser.parse(code, { sourceType: "module", plugins: ["jsx"] }); const usedKeys: Set = new Set(); @@ -42,6 +42,7 @@ function obfuscateJsWithAst( : str; const { obfuscatedContent, usedKeys: obfuscateUsedKeys } = obfuscateKeys(selectorConversion, str); + if (obfuscatedContent !== str) { obfuscateUsedKeys.forEach(key => usedKeys.add(key)); return obfuscatedContent; @@ -63,7 +64,7 @@ function obfuscateJsWithAst( obfuscatedCode: obfuscatedCode.code, usedKeys: usedKeys }; -} +}; /** @@ -73,12 +74,12 @@ function obfuscateJsWithAst( * @param scannedNodes - (for recursion) keep track of scanned nodes to avoid infinite loop * @returns - the modified AST node */ -function searchStringLiterals(path: NodePath, +export const searchStringLiterals = (path: NodePath, callback: (str: string) => void | string, //? keep track of scanned nodes to avoid infinite loop scannedNodes: Set = new Set() -) { +) => { /* Skip this node if it has already been scanned */ if (path.node && scannedNodes.has(path.node)) { return; @@ -365,9 +366,4 @@ function searchStringLiterals(path: NodePath, }); } return path; -} - -export { - searchStringLiterals, - obfuscateJsWithAst, -} +}; diff --git a/src/handlers/js.ts b/src/handlers/js.ts index 343c98e..292f415 100644 --- a/src/handlers/js.ts +++ b/src/handlers/js.ts @@ -11,8 +11,7 @@ import { import { SelectorConversion } from "../types"; import { obfuscateJsWithAst } from "./js-ast"; - -function searchForwardComponent(content: string) { +export const searchForwardComponent = (content: string) => { const componentSearchRegex = /(?<=\.jsx\()[^,|"|']+/g; //eg. o.jsx(yt,{data:yc,index:"date // then return yt @@ -24,9 +23,9 @@ function searchForwardComponent(content: string) { return match; } return []; -} +}; -function searchComponent(content: string, componentName: string) { +const searchComponent = (content: string, componentName: string) => { const componentSearchRegex = new RegExp(`\\b(?:const|let|var)\\s+(${componentName})\\s*=\\s*.*?(\\{)`, "g"); // eg, let yt=l().forwardRef((e,t)=>{let const match = content.match(componentSearchRegex); @@ -39,9 +38,9 @@ function searchComponent(content: string, componentName: string) { const componentContent = content.slice(openSymbolPos, closeMarkerPos); return componentContent; -} +}; -function obfuscateForwardComponentJs(searchContent: string, wholeContent: string, selectorConversion: SelectorConversion) { +export const obfuscateForwardComponentJs = (searchContent: string, wholeContent: string, selectorConversion: SelectorConversion) => { const componentNames = searchForwardComponent(searchContent).filter((componentName) => { return !componentName.includes("."); }); @@ -93,10 +92,10 @@ function obfuscateForwardComponentJs(searchContent: string, wholeContent: string } return componentObfuscatedcomponentCodePairs; -} +}; -function obfuscateJs(content: string, key: string, selectorCoversion: SelectorConversion - , filePath: string, contentIgnoreRegexes: RegExp[] = [], useAst: boolean = false) { +export const obfuscateJs = (content: string, key: string, selectorCoversion: SelectorConversion + , filePath: string, contentIgnoreRegexes: RegExp[] = [], useAst: boolean = false) => { if (useAst) { try { @@ -126,10 +125,4 @@ function obfuscateJs(content: string, key: string, selectorCoversion: SelectorCo } }); return content; -} - -export { - obfuscateForwardComponentJs, - obfuscateJs, - searchForwardComponent, -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index 17e9935..7b1ab91 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -19,11 +19,11 @@ import { obfuscateJs } from "./handlers/js"; * @param blackListedFolderPaths - Paths to exclude (higher priority than whitelist) * @returns - True if the path should be included, false otherwise */ -function shouldIncludePath( +const shouldIncludePath = ( filePath: string, whiteListedFolderPaths: (string | RegExp)[] = [], blackListedFolderPaths: (string | RegExp)[] = [] -): boolean { +): boolean => { const normalizedPath = normalizePath(filePath); // Check if the path is blacklisted (higher priority) @@ -57,7 +57,7 @@ const issuer = "[next-css-obfuscator]"; let logLevel: LogLevel = "info"; const levels: LogLevel[] = ["debug", "info", "warn", "error", "success"]; -function log(type: LogLevel, task: string, data: any) { +export const log = (type: LogLevel, task: string, data: any) => { //ref: https://github.com/n4j1Br4ch1D/postcss-obfuscator/blob/main/utils.js if (levels.indexOf(type) < levels.indexOf(logLevel)) { return; @@ -85,9 +85,9 @@ function log(type: LogLevel, task: string, data: any) { console.log("'\x1b[0m'", issuer, task, data, "\x1b[0m"); break; } -} +}; -function setLogLevel(level: LogLevel) { +export const setLogLevel = (level: LogLevel) => { logLevel = level; } @@ -110,10 +110,10 @@ function setLogLevel(level: LogLevel) { //! //! ==================== -const usedKeyRegistery = new Set(); +export const usedKeyRegistery = new Set(); -const replaceJsonKeysInFiles = ( +export const replaceJsonKeysInFiles = ( { conversionTables, targetFolder, @@ -267,7 +267,7 @@ const replaceJsonKeysInFiles = ( // }); } -const obfuscateKeys = ( +export const obfuscateKeys = ( selectorConversion: SelectorConversion, fileContent: string, contentIgnoreRegexes: RegExp[] = [], @@ -318,20 +318,20 @@ const obfuscateKeys = ( return { obfuscatedContent: fileContent, usedKeys: usedKeys }; } -function escapeRegExp(str: string) { +const escapeRegExp = (str: string) => { //ref: https://github.com/n4j1Br4ch1D/postcss-obfuscator/blob/main/utils.js return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string -} +}; /** * Get the filename from a file path * @param filePath - The path to the file * @returns The filename of the file */ -function getFilenameFromPath(filePath: string) { +export const getFilenameFromPath = (filePath: string) => { //ref: https://github.com/n4j1Br4ch1D/postcss-obfuscator/blob/main/utils.js return filePath.replace(/^.*[\\/]/, ''); -} +}; /** * Normalizes a file path by replacing backslashes with forward slashes. @@ -347,22 +347,9 @@ function getFilenameFromPath(filePath: string) { * // Returns: 'path/to/file' * normalizePath('path\\to\\file'); */ -function normalizePath(filePath: string) { +export const normalizePath = (filePath: string) => { return filePath.replace(/\\/g, "/"); -} - -function loadAndMergeJsonFiles(jsonFolderPath: string) { - //ref: https://github.com/n4j1Br4ch1D/postcss-obfuscator/blob/main/utils.js - const jsonFiles: { [key: string]: any } = {}; - - fs.readdirSync(jsonFolderPath).forEach((file: string) => { - const filePath = path.join(jsonFolderPath, file); - const fileData = JSON.parse(fs.readFileSync(filePath, "utf-8")); - Object.assign(jsonFiles, fileData); - }); - - return jsonFiles; -} +}; /** * @@ -373,7 +360,7 @@ function loadAndMergeJsonFiles(jsonFolderPath: string) { * @param direction - if "forward", the function will search the closest closeMarker after the startPosition, if "backward", the function will search the closest openMarker before the startPosition * @returns */ -function findClosestSymbolPosition(content: string, openMarker: string, closeMarker: string, startPosition: number = 0, direction: "forward" | "backward" = "backward") { +export const findClosestSymbolPosition = (content: string, openMarker: string, closeMarker: string, startPosition: number = 0, direction: "forward" | "backward" = "backward") => { let level = 0; let currentPos = startPosition; @@ -406,9 +393,9 @@ function findClosestSymbolPosition(content: string, openMarker: string, closeMar } return currentPos; -} +}; -function findContentBetweenMarker(content: string, targetStr: string, openMarker: string, closeMarker: string) { +export const findContentBetweenMarker = (content: string, targetStr: string, openMarker: string, closeMarker: string) => { if (openMarker === closeMarker) { throw new Error("openMarker and closeMarker can not be the same"); } @@ -433,13 +420,13 @@ function findContentBetweenMarker(content: string, targetStr: string, openMarker } return truncatedContents; -} +}; -export function addKeysToRegistery(usedKeys: Set | string[]) { +export const addKeysToRegistery = (usedKeys: Set | string[]) => { usedKeys.forEach((key) => { usedKeyRegistery.add(key); }); -} +}; /** * Find all files with the specified extension in the build folder. @@ -451,7 +438,7 @@ export function addKeysToRegistery(usedKeys: Set | string[]) { * @param options.blackListedFolderPaths - an array of folder paths to exclude from the search. Higher priority than whiteListedFolderPaths. * @returns - an array of file relative paths. */ -const findAllFilesWithExt = ( +export const findAllFilesWithExt = ( ext: string, targetFolderPath: string, options?: { whiteListedFolderPaths?: (string | RegExp)[], @@ -494,131 +481,23 @@ const findAllFilesWithExt = ( return targetExtFiles; } -let rng: NumberGenerator | undefined = undefined; - -function getRandomString(length: number, seed?: string, rngStateCode?: string, str?: string) { - if (length <= 0 || !Number.isInteger(length)) { - throw new Error("Length must be a positive integer"); - } - - if (!rng) { - rng = new NumberGenerator(seed); - } - if (rngStateCode) { - rng.recoverState(rngStateCode); - } - - let rn = rng.random(0, 1, true); - if (str && seed) { - // can create a more collision resistant "random number" but in fact it's not random number - rn = Number.parseFloat(`0.${NumberGenerator.stringToSeed(str) + NumberGenerator.stringToSeed(seed)}`); - } - - //ref: https://github.com/n4j1Br4ch1D/postcss-obfuscator/blob/main/utils.js - // Generate a random string of characters with the specified length - const randomString = rn.toString(36).substring(2, length - 1 + 2); - // Combine the random string with a prefix to make it a valid class name (starts with a letter, contains only letters, digits, hyphens, and underscores) - const randomLetter = String.fromCharCode(Math.floor(rng.random(0, 1, true) * 26) + 97); // 97 is the ASCII code for lowercase 'a' - return { - rngStateCode: rng.getStateCode(), - randomString: `${randomLetter}${randomString}`, - }; -} - -/** - * - * @param str - * @param seed - * @param rngStateCode - * @returns - */ -function seedableSimplifyString(str: string, seed?: string, rngStateCode?: string) { - if (!str) { - throw new Error("String can not be empty"); - } - if (!rng) { - rng = new NumberGenerator(seed); - } - if (rngStateCode) { - rng.recoverState(rngStateCode); - } - - //ref: https://github.com/n4j1Br4ch1D/postcss-obfuscator/blob/main/utils.js - 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, - }; -} - -/** - * Get a simplified string from a number - * @param alphabetPoistion (starting from 1) - * @returns alphabet string - * - * @example - * simplifyString(1) // returns "a" - * simplifyString(26) // returns "z" - * simplifyString(27) // returns "aa" - * simplifyString(52) // returns "az" - * simplifyString(53) // returns "ba" - */ -function simplifyString(alphabetPoistion: number) { - 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; -} - -function replaceFirstMatch(source: string, find: string, replace: string): string { +export const replaceFirstMatch = (source: string, find: string, replace: string): string => { const index = source.indexOf(find); if (index !== -1) { return source.slice(0, index) + replace + source.slice(index + find.length); } return source; -} +}; /** * Check if there are any duplicates in an array of strings * @param arr - an array of strings * @returns - true if there are any duplicates, false otherwise */ -function duplicationCheck(arr: string[]) { +export const duplicationCheck = (arr: string[]) => { const set = new Set(arr); return arr.length !== set.size; -} - - -function createKey(str: string) { - const b64 = Buffer.from(str).toString("base64").replace(/=/g, ""); - return `{{{{{{${b64}}}}}}}`; -} - -function decodeKey(str: string) { - const regex = /{{{{{{([\w\+\/]+)}}}}}}/g; - str = str.replace(regex, (match, p1) => { - // Calculate the number of '=' needed - const padding = p1.length % 4 === 0 ? 0 : 4 - (p1.length % 4); - // Add back the '=' - const b64 = p1 + "=".repeat(padding); - return Buffer.from(b64, "base64").toString("ascii"); - }); - return str; -} +}; /** * Convert a string to a number by summing the char codes of each character @@ -666,12 +545,3 @@ export const loadConversionTables = (folderPath: string): ConversionTables => { return tables; } - -export { - getFilenameFromPath, log, normalizePath, loadAndMergeJsonFiles - , replaceJsonKeysInFiles, setLogLevel, findContentBetweenMarker, replaceFirstMatch - , findAllFilesWithExt, getRandomString, seedableSimplifyString, usedKeyRegistery - , obfuscateKeys, findClosestSymbolPosition, duplicationCheck - , createKey, decodeKey, simplifyString -}; - From 668a5f66abdd174bf8ecbd18e9872a9719c73508 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Mon, 21 Apr 2025 23:39:15 +0100 Subject: [PATCH 09/32] feat: v3 migration with TailwindCSS 4 support Introduces breaking changes to support TailwindCSS 4, nested CSS, and CSS ident obfuscation. - Updates configuration options, including renaming, merging, and removing deprecated options. - Migrates to css-seasoning v1.4.1. - Enables JS AST parsing by default. - Updates documentation with v3 migration guide. --- README.md | 112 +++++++++++++++++++++++++++++------------- docs/upgrade-to-v3.md | 20 ++++++++ package-lock.json | 8 +-- package.json | 2 +- src/config.ts | 31 +++++++++--- src/handlers/css.ts | 39 ++++++--------- src/index.ts | 12 +++-- src/types.ts | 24 +++++++-- src/utils.ts | 22 ++++----- 9 files changed, 184 insertions(+), 86 deletions(-) create mode 100644 docs/upgrade-to-v3.md diff --git a/README.md b/README.md index 3e6ede2..4729f1d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,3 @@ - - - # NEXT-CSS-OBFUSCATOR Project starts on 30-10-2023 @@ -21,20 +18,31 @@ Visit the [GitHub Page](https://github.com/soranoo/next-css-obfuscator/) for bet --- -### 🎉 Version 2.1.0 has NOW been released 🎉 - Shout out to [hoangnhan2ka3](https://github.com/hoangnhan2ka3) for providing a 💪wonderful [issue](https://github.com/soranoo/next-css-obfuscator/issues/6) report and a demo site. +### 🎉 Version 3 has NOW been released 🎉 (💥 Breaking Changes) - #### 📌 Changes - - Much Much Much better quality of CSS selector obfuscation - - Delete original CSS automatically after obfuscation (only apply at full obfuscation) - - Support TailwindCSS Universal Selector (eg. `*:pt-4`) - - More tests +>[!IMPORTANT]\ +> This version is a major update and has breaking changes. Please read the [migration guide](docs/upgrade-to-v3.md) carefully before upgrading. + +>[!TIP]\ +> Don't upgrade to this version unless you are using TailwindCSS 4.0.0 or above. "If it works, don't touch it." :) + + #### 📌 Feature Changes + + - Support TailwindCSS 4. + - Support nested CSS. + - Support CSS idents obfuscation. #### 📌 Configuration Changes - - Removed `customTailwindDarkModeSelector` option, the dark mode selector will be automatically obfuscated at full obfuscation. - - Merged `includeAnyMatchRegexes` and `excludeAnyMatchRegexes` options into `whiteListedFolderPaths` and `blackListedFolderPaths` options. (Directly move the regexes to the `whiteListedFolderPaths` and `blackListedFolderPaths` options) - - Added `removeOriginalCss` option, default to `false`. Set to `true` to delete original CSS from CSS files if it has a obfuscated version. - - `classIgnore` option now supports Regex. + + - `enableJsAst` option is now enabled by default. + - Default `generatorSeed` not longer fixed to `-1`, but a random string. + - `simplify-seedable` mode is not longer supported. Use `random` mode instead. + - Removed `includeAnyMatchRegexes` and `excludeAnyMatchRegexes` options, the `whiteListedFolderPaths` and `blackListedFolderPaths` options will be used instead. + - Deprecated `classLength` option, not longer supported. + - Added `ignorePatterns` option to ignore the class names and idents that match the regexes or strings. + - Not longer preserve TailwindCSS dark mode class names (ie `.dark`). Add the dark mode class name to the `ignorePatterns.selectors` option to preserve it. + - Merge `classIgnore` into `ignorePatterns.selectors` option. + - Renamed `classPrefix` and `classSuffix` to `prefix` and `suffix`. ### 💥 Version 2 (Major Update) This version is deeply inspired by [PostCSS-Obfuscator](https://github.com/n4j1Br4ch1D/postcss-obfuscator). Shout out to [n4j1Br4ch1D](https://github.com/n4j1Br4ch1D) for creating such a great package and thank you [tremor](https://github.com/tremorlabs) for sponsoring this project. @@ -50,10 +58,13 @@ Visit the [GitHub Page](https://github.com/soranoo/next-css-obfuscator/) for bet ### 📚 Migration Guides - [Migrate from version 1.x to 2.x](docs/upgrade-to-v2.md) +- [Migrate from version 2.x to 3.x](docs/upgrade-to-v3.md) [version 1.x README](https://github.com/soranoo/next-css-obfuscator/tree/v.1.1.0) +[version 2.x README](https://github.com/soranoo/next-css-obfuscator/tree/v.2.2.19) + Give me a ⭐ if you like it. ## 📖 Table of Contents @@ -159,17 +170,21 @@ Visit the [npm](https://www.npmjs.com/package/next-css-obfuscator) page. ```javascript module.exports = { enable: true, - mode: "random", // random | simplify | simplify-seedable + mode: "random", // random | simplify refreshClassConversionJson: false, // recommended set to true if not in production allowExtensions: [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"], }; ``` ##### Partially obfuscate + + > [!CAUTION]\ + > Partially obfuscate can be EXTREMELY buggy. Be cautious when using this feature. + ```javascript module.exports = { enable: true, - mode: "random", // random | simplify | simplify-seedable + mode: "random", // random | simplify refreshClassConversionJson: false, // recommended set to true if not in production allowExtensions: [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"], @@ -184,14 +199,14 @@ Visit the [npm](https://www.npmjs.com/package/next-css-obfuscator) page. module.exports = { // other options ... - } as Options; + } satisfies Options; ``` Feel free to checkout [📖 Config Options Reference](#-config-options-reference) for more options and details. - > [!NOTE]\ + > [!TIP]\ > The obfuscation will never work as expected, tweak the options with your own needs. 2. Add the following code to `package.json`: @@ -263,14 +278,14 @@ See [Next 14 App Router Partially Obfuscated Demo](https://github.com/soranoo/ne ## 🔧 My Setting -If you are interested in my setting (from my production site), here it is +If you are interested in my setting, here it is ```javascript // next-css-obfuscator.config.cjs module.exports = { enable: true, - mode: "random", // random | simplify | simplify-seedable + mode: "random", // random | simplify refreshClassConversionJson: false, // recommended set to true if not in production allowExtensions: [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"], @@ -283,6 +298,7 @@ module.exports = { ], }; ``` + [*1] See this [comment](https://github.com/soranoo/next-css-obfuscator/issues/6#issuecomment-1919495298) It may not be the best setting but it works for me. :) @@ -292,14 +308,13 @@ It may not be the best setting but it works for me. :) | Option | Type | Default | Description | | ---------------------------- | ----------------------------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | | enable | boolean | true | Enable or disable the obfuscation. | -|mode| "random" \| "simplify" \| "simplify-seedable" | "random" | Obfuscate mode,

**random**: Fixed size random class name

**simplify**: Alphabetic class name, like [medium](https://medium.com/)

**simplify-seedable**: Random dynamic size class name| +| mode | "random" \| "simplify" | "random" | Obfuscate mode,

**random**: Fixed size random class name

**simplify**: Alphabetic class name, like [medium](https://medium.com/)| |buildFolderPath|string|"./.next"|The folder path to store the build files built by Next.js.| |classConversionJsonFolderPath|string|"./css-obfuscator"|The folder path to store the before obfuscate and after obfuscated classes conversion table.| |refreshClassConversionJson|boolean|false|Refresh the class conversion JSON file(s) at every obfuscation. Good for setting tweaking but not recommended for production.| -|classLength|number|5|The length of the obfuscated class name if in random mode.

It is not recommended to set the length to less than 4. -|classPrefix|string|""|The prefix of the obfuscated class name.| -|classSuffix|string|""|The suffix of the obfuscated class name.| -|classIgnore|(string \| Regex)[ ]|[ ]|The class names to be ignored during obfuscation.| +|prefix|string|""|The prefix of the obfuscated class and ident name.| +|suffix|string|""|The suffix of the obfuscated class and ident name.| +|ignorePatterns|{selectors: [], idents: []}|{selectors: [], idents: []}|The patterns to be ignored during obfuscation.| |allowExtensions|string[ ]|[".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"]|The file extensions to be processed.| |contentIgnoreRegexes|RegExp[ ]|[/\.jsxs\)\("\w+"/g]|The regexes to match the content to be ignored during obfuscation.| |whiteListedFolderPaths|(string \| Regex)[ ]|[ ]|The folder paths/Regex to be processed. Empty array means all folders will be processed.| @@ -308,13 +323,13 @@ It may not be the best setting but it works for me. :) |markers|string[ ]|[ ]|Classes that indicate component(s) need to obfuscate.| |removeMarkersAfterObfuscated|boolean|true|Remove the obfuscation markers from HTML elements after obfuscation.| |removeOriginalCss|boolean|false|Delete original CSS from CSS files if it has a obfuscated version. (*NOT recommended* using in partial obfuscation) -|generatorSeed|string|"-1"|The seed for the random class name generator. "-1" means use random seed.

For "random" and "simplify-seedable" mode only. | +|generatorSeed|string \| undefined|{random string}|The seed for the random class name generator. "undefined" means use random seed.

For "random" mode only. | |logLevel|"debug" \| "info" \| "warn" \| "error" \| "success"| "info"|The log level.| ### Experimental Features Options 🚧 | Option| Type| Default| Description| Stage | | - | - | - | - | - | -|enableJsAst|boolean|false|Whether to obfuscate JS files using abstract syntax tree parser.

`contentIgnoreRegexes` option will be ignored if this option is enabled.|Alpha| +|enableJsAst|boolean|true|Whether to obfuscate JS files using abstract syntax tree parser.

`contentIgnoreRegexes` option will be ignored if this option is enabled.|Alpha| > [!NOTE]\ > The above options are still at the early stages of development and may not work as expected. @@ -334,15 +349,38 @@ It may not be the best setting but it works for me. :) module.exports = { enable: true, // Enable or disable the plugin. - mode: "random", // Obfuscate mode, "random", "simplify" or "simplify-seedable" + mode: "random", // Obfuscate mode, "random", "simplify" or "simplify-seedable". buildFolderPath: ".next", // Build folder of your project. classConversionJsonFolderPath: "./css-obfuscator", // The folder path to store the before obfuscate and after obfuscated classes conversion table. refreshClassConversionJson: false, // Refresh the class conversion JSON file. + /** + * @deprecated Not longer used from v3.0.0 and will be removed in the next major version. + */ classLength: 5, // Length of the obfuscated class name. + + /** + * @deprecated Merged into `prefix` from v3.0.0 and will be removed in the next major version. + */ classPrefix: "", // Prefix of the obfuscated class name. + + /** + * @deprecated Merged into `suffix` from v3.0.0 and will be removed in the next major version. + */ classSuffix: "", // Suffix of the obfuscated class name. + + prefix: "", // Prefix of the obfuscated class and ident name. + suffix: "", // Suffix of the obfuscated class and ident name. + + /** + * @deprecated Merged into `ignorePatterns.selectors` from v3.0.0 and will be removed in the next major version. + */ classIgnore: [], // The class names to be ignored during obfuscation. + ignorePatterns: { // The patterns to be ignored during obfuscation. + selectors: [], // The selectors to be ignored during obfuscation. + idents: [], // The idents to be ignored during obfuscation. + }, + allowExtensions: [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"], // The file extensions to be processed. contentIgnoreRegexes: [ /\.jsxs\)\("\w+"/g, // avoid accidentally obfuscate the HTML tag @@ -354,10 +392,12 @@ module.exports = { markers: ["next-css-obfuscation"], // Classes that indicate component(s) need to obfuscate. removeMarkersAfterObfuscated: true, // Remove the obfuscation markers from HTML elements after obfuscation. removeOriginalCss: false, // Delete original CSS from CSS files if it has a obfuscated version. - generatorSeed: "-1", // The seed for the random generator. "-1" means use random seed. + generatorSeed: undefined, // The seed for the random generator. "undefined" means use random seed. - //! Experimental feature - enableJsAst: false, // Whether to obfuscate JS files using abstract syntax tree parser (Experimental feature) + /** + * Experimental feature + */ + enableJsAst: true, // Whether to obfuscate JS files using abstract syntax tree parser. (Experimental feature) logLevel: "info", // Log level }; @@ -399,18 +439,20 @@ You are not expected to see this: /* obfuscated form */ .d8964 { - --tw-text-opacity: 1; - color: rgb(214 211 209 / var(--tw-text-opacity)); + --d89645: 1; + color: rgb(214 211 209 / var(--d89645)); } ``` + But this: + ```css /* example.css */ /* obfuscated form */ .d8964 { - --tw-text-opacity: 1; - color: rgb(214 211 209 / var(--tw-text-opacity)); + --d89645: 1; + color: rgb(214 211 209 / var(--d89645)); } ``` diff --git a/docs/upgrade-to-v3.md b/docs/upgrade-to-v3.md new file mode 100644 index 0000000..a57c426 --- /dev/null +++ b/docs/upgrade-to-v3.md @@ -0,0 +1,20 @@ +# Toward to version 3 + +Version 3 introduces several breaking changes, primarily focused on supporting TailwindCSS 4, nested CSS, and CSS ident obfuscation. Please review the configuration changes below carefully before upgrading. + +## Configuration + +The following table outlines the changes to the configuration options from version 2.x to 3.x: + +| Old configuration (v2.x) | New configuration (v3.x) | Notes | +| ------------------------------ | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | +| `generatorSeed` (default: -1) | `generatorSeed` (default: {random}) | Default seed is now a random string. Provide a fixed string if you need consistent output across builds (e.g., for CDN caching). | +| `mode: "simplify-seedable"` | ⛔ (Removed) | Use `mode: "random"` with a fixed `generatorSeed` instead. | +| `classLength` | ⛔ (Deprecated) | No longer supported. Will be removed in the next major version. | +| `classPrefix` | `prefix` | Renamed for clarity, now applies to both selectors and idents. `classPrefix` will be removed in the next major version. | +| `classSuffix` | `suffix` | Renamed for clarity, now applies to both selectors and idents. `classSuffix` will be removed in the next major version. | +| `classIgnore` | `ignorePatterns.selectors` | Merged into the new `ignorePatterns` object. `classIgnore` will be removed in the next major version. | +| ➡️ | `ignorePatterns.idents` | New option to ignore specific CSS idents| +| `includeAnyMatchRegexes` | ⛔ (Removed) | Use `whiteListedFolderPaths` instead. | +| `excludeAnyMatchRegexes` | ⛔ (Removed) | Use `blackListedFolderPaths` instead. | +| (Implicit `.dark` preservation)| (No implicit preservation) | TailwindCSS `.dark` class is no longer preserved by default. Add `.dark` or relevant selectors to `ignorePatterns.selectors`. | diff --git a/package-lock.json b/package-lock.json index 539af01..9c39053 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@babel/parser": "^7.23.9", "@babel/traverse": "^7.23.9", "css-parse": "^2.0.0", - "css-seasoning": "^1.3.0", + "css-seasoning": "^1.4.1", "html-escaper": "^3.0.3", "htmlparser2": "^10.0.0", "lightningcss-wasm": "^1.29.3", @@ -1734,9 +1734,9 @@ } }, "node_modules/css-seasoning": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/css-seasoning/-/css-seasoning-1.3.0.tgz", - "integrity": "sha512-D/Th17IefXLulL4AKphSN1uaclv+UV8eyY83GRVRxfsOzwreNwy4VA2ksmVRQoDKDUlJw2XpCO6HabFgPX5PlA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/css-seasoning/-/css-seasoning-1.4.1.tgz", + "integrity": "sha512-m5X8Qu5JWvMJH7KkygW1EzC2Yv504s+rR0AC8VCpabWUdlvzaMDw0BxBgu7+pdreR9BccKDCGd/6QxMrtGs79g==", "dependencies": { "@deno/shim-deno": "~0.18.0", "lightningcss-wasm": "^1.29.1", diff --git a/package.json b/package.json index c2f462b..bd3d144 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@babel/parser": "^7.23.9", "@babel/traverse": "^7.23.9", "css-parse": "^2.0.0", - "css-seasoning": "^1.3.0", + "css-seasoning": "^1.4.1", "html-escaper": "^3.0.3", "htmlparser2": "^10.0.0", "lightningcss-wasm": "^1.29.3", diff --git a/src/config.ts b/src/config.ts index 2e48a34..9c305e9 100644 --- a/src/config.ts +++ b/src/config.ts @@ -8,13 +8,32 @@ export const defaultOptions: Options = { refreshClassConversionJson: false, // Refresh the class conversion JSON file. /** - * @deprecated Not longer used from v3.0.0 + * @deprecated Not longer used from v3.0.0 and will be removed in the next major version. */ classLength: 5, // Length of the obfuscated class name. - + + /** + * @deprecated Merged into `prefix` from v3.0.0 and will be removed in the next major version. + */ classPrefix: "", // Prefix of the obfuscated class name. + + /** + * @deprecated Merged into `suffix` from v3.0.0 and will be removed in the next major version. + */ classSuffix: "", // Suffix of the obfuscated class name. + + prefix: "", // Prefix of the obfuscated class and ident name. + suffix: "", // Suffix of the obfuscated class and ident name. + + /** + * @deprecated Merged into `ignorePatterns.selectors` from v3.0.0 and will be removed in the next major version. + */ classIgnore: [], // The class names to be ignored during obfuscation. + ignorePatterns: { // The patterns to be ignored during obfuscation. + selectors: [], // The selectors to be ignored during obfuscation. + idents: [], // The idents to be ignored during obfuscation. + }, + allowExtensions: [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"], // The file extensions to be processed. contentIgnoreRegexes: [ /\.jsxs\)\("\w+"/g, // avoid accidentally obfuscate the HTML tag @@ -26,13 +45,13 @@ export const defaultOptions: Options = { markers: ["next-css-obfuscation"], // Classes that indicate component(s) need to obfuscate. removeMarkersAfterObfuscated: true, // Remove the obfuscation markers from HTML elements after obfuscation. removeOriginalCss: false, // Delete original CSS from CSS files if it has a obfuscated version. - generatorSeed: "-1", // The seed for the random generator. "-1" means use random seed. - + generatorSeed: undefined, // The seed for the random generator. "undefined" means use random seed. + /** * Experimental feature */ - enableJsAst: false, // Whether to obfuscate JS files using abstract syntax tree parser. (Experimental feature) - + enableJsAst: true, // Whether to obfuscate JS files using abstract syntax tree parser. (Experimental feature) + logLevel: "info", // Log level }; diff --git a/src/handlers/css.ts b/src/handlers/css.ts index 0cef632..728e067 100644 --- a/src/handlers/css.ts +++ b/src/handlers/css.ts @@ -26,7 +26,7 @@ const obfuscateCss = async ({ mode = "random", prefix, suffix, - classIgnore = [], + ignorePatterns, generatorSeed = Math.random().toString().slice(2, 10), // take 8 digits from the random number }: { cssPath: string, @@ -38,7 +38,7 @@ const obfuscateCss = async ({ mode?: obfuscateMode, prefix?: string, suffix?: string, - classIgnore?: (string | RegExp)[], + ignorePatterns?: TransformProps["ignorePatterns"], generatorSeed?: string, }) => { if (!outCssPath) { @@ -51,11 +51,6 @@ const obfuscateCss = async ({ const cssContent = fs.readFileSync(cssPath, "utf-8"); - // TODO: use const instead of function - // TODO: this function is combined createSelectorConversionJson and obfuscateCss - // TODO: connect props to this function - // TODO: bc we updated conversion table, so other function using the table have to update to the new table - let transformerMode: TransformProps["mode"] = mode === "simplify" ? "minimal" : "hash"; if (!transformerMode) { // @ts-expect-error - "simplify-seedable" is deprecated but for backward compatibility @@ -75,9 +70,7 @@ const obfuscateCss = async ({ prefix: prefix, suffix: suffix, seed: stringToNumber(generatorSeed), - ignorePatterns: { - selector: classIgnore - } + ignorePatterns: ignorePatterns, }); if (removeOriginalCss) { @@ -94,11 +87,11 @@ const obfuscateCss = async ({ finCss = new TextDecoder().decode(code); } - const totalConversion = Object.keys(newConversionTables.selector).length + Object.keys(newConversionTables.ident).length; + const totalConversion = Object.keys(newConversionTables.selectors).length + Object.keys(newConversionTables.idents).length; if (removeOriginalCss) { log("info", "CSS rules:", `Modified ${totalConversion} CSS rules to ${getFilenameFromPath(cssPath)}`); } else { - const oldTotalConversion = conversionTables ? Object.keys(conversionTables.selector).length + Object.keys(conversionTables.ident).length : 0; + const oldTotalConversion = conversionTables ? Object.keys(conversionTables.selectors).length + Object.keys(conversionTables.idents).length : 0; log("info", "CSS rules:", `Added ${totalConversion - oldTotalConversion} new CSS rules to ${getFilenameFromPath(cssPath)}`); } @@ -125,7 +118,7 @@ export const obfuscateCssFiles = async ({ mode = "random", prefix, suffix, - classIgnore = [], + ignorePatterns, generatorSeed = new Date().getTime().toString(), removeOriginalCss = false, }: { @@ -136,7 +129,7 @@ export const obfuscateCssFiles = async ({ mode?: obfuscateMode, prefix?: string, suffix?: string, - classIgnore?: (string | RegExp)[], + ignorePatterns?: TransformProps["ignorePatterns"], generatorSeed?: string, removeOriginalCss?: boolean, }) => { @@ -161,8 +154,8 @@ export const obfuscateCssFiles = async ({ }); const tables: ConversionTables = { - selector: {}, - ident: {}, + selectors: {}, + idents: {}, }; cssPaths.forEach(async (cssPath) => { @@ -173,22 +166,22 @@ export const obfuscateCssFiles = async ({ prefix, suffix, mode, - classIgnore, + ignorePatterns, generatorSeed, removeOriginalCss, }); // Merge the conversion tables - Object.entries(newConversionTables.selector).forEach(([key, value]) => { - if (!tables.selector[key]) { + Object.entries(newConversionTables.selectors).forEach(([key, value]) => { + if (!tables.selectors[key]) { // If it doesn't exist, create a new entry - tables.selector[key] = value; + tables.selectors[key] = value; } }); - Object.entries(newConversionTables.ident).forEach(([key, value]) => { - if (!tables.ident[key]) { + Object.entries(newConversionTables.idents).forEach(([key, value]) => { + if (!tables.idents[key]) { // If it doesn't exist, create a new entry - tables.ident[key] = value; + tables.idents[key] = value; } }); }); diff --git a/src/index.ts b/src/index.ts index fe7e6ad..04252c7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,9 +40,15 @@ const obfuscate = async (options: Options) => { blackListedFolderPaths: options.blackListedFolderPaths, mode: options.mode, - prefix: options.classPrefix, - suffix: options.classSuffix, - classIgnore: options.classIgnore, + prefix: options.prefix || options.classPrefix, + suffix: options.suffix || options.classSuffix, + ignorePatterns: { + ...options.ignorePatterns, + selectors: [ + ...options.ignorePatterns?.selectors || [], + ...(options.classIgnore || []), + ] + }, generatorSeed: options.generatorSeed, removeOriginalCss: options.removeOriginalCss, diff --git a/src/types.ts b/src/types.ts index 11bc364..e97630c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,8 +1,8 @@ -import type { ConversionTables } from "css-seasoning"; +import type { ConversionTables, TransformProps } from "css-seasoning"; export type LogLevel = "debug" | "info" | "warn" | "error" | "success"; export type obfuscateMode = "random" | "simplify"; -export type SelectorConversion = ConversionTables["selector"]; +export type SelectorConversion = ConversionTables["selectors"]; export type Options = { enable: boolean; @@ -11,10 +11,28 @@ export type Options = { classConversionJsonFolderPath: string; refreshClassConversionJson: boolean; + /** + * @deprecated Not longer used from v3.0.0 and will be removed in the next major version. + */ classLength: number; + + /** + * @deprecated Merged into `prefix` from v3.0.0 and will be removed in the next major version. + */ classPrefix: string; + /** + * @deprecated Merged into `suffix` from v3.0.0 and will be removed in the next major version. + */ classSuffix: string; + prefix: string; + suffix: string; + + /** + * @deprecated Merged into `ignorePatterns.selectors` from v3.0.0 and will be removed in the next major version. + */ classIgnore: (string | RegExp)[]; + ignorePatterns: TransformProps["ignorePatterns"]; + allowExtensions: string[]; contentIgnoreRegexes: RegExp[]; @@ -24,7 +42,7 @@ export type Options = { markers: string[]; removeMarkersAfterObfuscated: boolean; removeOriginalCss: boolean; - generatorSeed: string; + generatorSeed: string | undefined; enableJsAst: boolean; diff --git a/src/utils.ts b/src/utils.ts index 7b1ab91..79b34e4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -147,7 +147,7 @@ export const replaceJsonKeysInFiles = ( if (removeObfuscateMarkerClassesAfterObfuscated) { obfuscateMarkerClasses.forEach(obfuscateMarkerClass => { - conversionTables.selector[cssEscape(`.${obfuscateMarkerClass}`)] = ""; + conversionTables.selectors[cssEscape(`.${obfuscateMarkerClass}`)] = ""; }); } @@ -185,7 +185,7 @@ export const replaceJsonKeysInFiles = ( const htmlOriginal = htmlMatch[0]; const { obfuscatedContent, usedKeys } = obfuscateHtmlClassNames({ html: htmlOriginal, - selectorConversion: conversionTables.selector, + selectorConversion: conversionTables.selectors, obfuscateMarkerClass: obfuscateMarkerClass, contentIgnoreRegexes: contentIgnoreRegexes, }); @@ -197,7 +197,7 @@ export const replaceJsonKeysInFiles = ( } else { const obfuscateScriptContent = obfuscateJs(fileContent, obfuscateMarkerClass, - conversionTables.selector, + conversionTables.selectors, filePath, contentIgnoreRegexes, enableJsAst @@ -216,7 +216,7 @@ export const replaceJsonKeysInFiles = ( const obfuscateScriptContent = obfuscateJs( fileContent, enableJsAst ? "" : "jsx", - conversionTables.selector, + conversionTables.selectors, filePath, contentIgnoreRegexes, enableJsAst @@ -229,7 +229,7 @@ export const replaceJsonKeysInFiles = ( //! NEW const { obfuscatedContent, usedKeys } = obfuscateHtmlClassNames({ html: fileContent, - selectorConversion: conversionTables.selector, + selectorConversion: conversionTables.selectors, contentIgnoreRegexes: contentIgnoreRegexes, }); @@ -237,7 +237,7 @@ export const replaceJsonKeysInFiles = ( addKeysToRegistery(usedKeys); } else { const { obfuscatedContent, usedKeys } = obfuscateKeys( - conversionTables.selector, + conversionTables.selectors, fileContent, contentIgnoreRegexes ); @@ -524,8 +524,8 @@ export const stringToNumber = (str: string) => { */ export const loadConversionTables = (folderPath: string): ConversionTables => { const tables: ConversionTables = { - ident: {}, - selector: {}, + idents: {}, + selectors: {}, }; fs.readdirSync(folderPath).forEach((file: string) => { @@ -533,12 +533,12 @@ export const loadConversionTables = (folderPath: string): ConversionTables => { const fileData = JSON.parse(fs.readFileSync(filePath, "utf-8")); if (Object.keys(fileData).includes("ident") && Object.keys(fileData).includes("selector")) { - Object.assign(tables.ident, fileData.ident); - Object.assign(tables.selector, fileData.selector); + Object.assign(tables.idents, fileData.ident); + Object.assign(tables.selectors, fileData.selector); } else { // if the file doesn't have ident, it should be selector //? For backward compatibility - Object.assign(tables.selector, fileData); + Object.assign(tables.selectors, fileData); } }); From afe1bffe444b699d4982ca478f1055b5b7a12516 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Mon, 21 Apr 2025 23:40:50 +0100 Subject: [PATCH 10/32] refactor: Standardize code formatting and improve readability across multiple files --- src/__tests__/js.test.ts | 164 ++++++++++---------- src/config.ts | 112 +++++++------- src/handlers/css.ts | 322 +++++++++++++++++++-------------------- src/handlers/html.ts | 2 +- src/handlers/js-ast.ts | 2 +- src/types.ts | 114 +++++++------- 6 files changed, 358 insertions(+), 358 deletions(-) diff --git a/src/__tests__/js.test.ts b/src/__tests__/js.test.ts index 3fea976..2e0d6c7 100644 --- a/src/__tests__/js.test.ts +++ b/src/__tests__/js.test.ts @@ -1,6 +1,6 @@ -import { describe, it, expect, test } from "vitest"; +import { describe, expect, test } from "vitest"; import { - searchForwardComponent, + searchForwardComponent, } from "../handlers/js"; //! ================================ @@ -9,118 +9,118 @@ import { describe("searchForwardComponent", () => { - test("should return component name when jsx format is correct", () => { - // Arrange - const content = `const element = o.jsx(ComponentName, {data: dataValue, index: "date"});`; + test("should return component name when jsx format is correct", () => { + // Arrange + const content = `const element = o.jsx(ComponentName, {data: dataValue, index: "date"});`; - // Act - const result = searchForwardComponent(content); + // Act + const result = searchForwardComponent(content); - // Assert - expect(result).toEqual(["ComponentName"]); - }); + // Assert + expect(result).toEqual(["ComponentName"]); + }); - test("should return multiple component names for multiple matches", () => { - // Arrange - const content = `o.jsx(FirstComponent, props); o.jsx(SecondComponent, otherProps);`; + test("should return multiple component names for multiple matches", () => { + // Arrange + const content = `o.jsx(FirstComponent, props); o.jsx(SecondComponent, otherProps);`; - // Act - const result = searchForwardComponent(content); + // Act + const result = searchForwardComponent(content); - // Assert - expect(result).toEqual(["FirstComponent", "SecondComponent"]); - }); + // Assert + expect(result).toEqual(["FirstComponent", "SecondComponent"]); + }); - test("should return an empty array when no component name is found", () => { - // Arrange - const content = `o.jsx("h1", {data: dataValue, index: "date"});`; + test("should return an empty array when no component name is found", () => { + // Arrange + const content = `o.jsx("h1", {data: dataValue, index: "date"});`; - // Act - const result = searchForwardComponent(content); + // Act + const result = searchForwardComponent(content); - // Assert - expect(result).toEqual([]); - }); + // Assert + expect(result).toEqual([]); + }); - test("should return an empty array when content is empty", () => { - // Arrange - const content = ""; + test("should return an empty array when content is empty", () => { + // Arrange + const content = ""; - // Act - const result = searchForwardComponent(content); + // Act + const result = searchForwardComponent(content); - // Assert - expect(result).toEqual([]); - }); + // Assert + expect(result).toEqual([]); + }); - test("should return an empty array when jsx is not used", () => { - // Arrange - const content = `const element = React.createElement("div", null, "Hello World");`; + test("should return an empty array when jsx is not used", () => { + // Arrange + const content = `const element = React.createElement("div", null, "Hello World");`; - // Act - const result = searchForwardComponent(content); + // Act + const result = searchForwardComponent(content); - // Assert - expect(result).toEqual([]); - }); + // Assert + expect(result).toEqual([]); + }); - test("should handle special characters in component names", () => { - // Arrange - const content = `o.jsx($Comp_1, props); o.jsx(_Comp$2, otherProps);`; + test("should handle special characters in component names", () => { + // Arrange + const content = `o.jsx($Comp_1, props); o.jsx(_Comp$2, otherProps);`; - // Act - const result = searchForwardComponent(content); + // Act + const result = searchForwardComponent(content); - // Assert - expect(result).toEqual(["$Comp_1", "_Comp$2"]); - }); + // Assert + expect(result).toEqual(["$Comp_1", "_Comp$2"]); + }); - test("should not return component names when they are quoted", () => { - // Arrange - const content = `o.jsx("ComponentName", props); o.jsx('AnotherComponent', otherProps);`; + test("should not return component names when they are quoted", () => { + // Arrange + const content = `o.jsx("ComponentName", props); o.jsx('AnotherComponent', otherProps);`; - // Act - const result = searchForwardComponent(content); + // Act + const result = searchForwardComponent(content); - // Assert - expect(result).toEqual([]); - }); + // Assert + expect(result).toEqual([]); + }); - test("should return component names when they are followed by a brace", () => { - // Arrange - const content = `o.jsx(ComponentName, {props: true});`; + test("should return component names when they are followed by a brace", () => { + // Arrange + const content = `o.jsx(ComponentName, {props: true});`; - // Act - const result = searchForwardComponent(content); + // Act + const result = searchForwardComponent(content); - // Assert - expect(result).toEqual(["ComponentName"]); - }); + // Assert + expect(result).toEqual(["ComponentName"]); + }); - test("should handle content with line breaks and multiple jsx calls", () => { - // Arrange - const content = ` + test("should handle content with line breaks and multiple jsx calls", () => { + // Arrange + const content = ` o.jsx(FirstComponent, {data: dataValue}); o.jsx(SecondComponent, {index: "date"}); o.jsx(ThirdComponent, {flag: true}); `; - // Act - const result = searchForwardComponent(content); + // Act + const result = searchForwardComponent(content); - // Assert - expect(result).toEqual(["FirstComponent", "SecondComponent", "ThirdComponent"]); - }); + // Assert + expect(result).toEqual(["FirstComponent", "SecondComponent", "ThirdComponent"]); + }); - test("should handle content with nested jsx calls", () => { - // Arrange - const content = `o.jsx(ParentComponent, {children: o.jsx(ChildComponent, {})})`; + test("should handle content with nested jsx calls", () => { + // Arrange + const content = `o.jsx(ParentComponent, {children: o.jsx(ChildComponent, {})})`; - // Act - const result = searchForwardComponent(content); + // Act + const result = searchForwardComponent(content); - // Assert - expect(result).toEqual(["ParentComponent", "ChildComponent"]); - }); + // Assert + expect(result).toEqual(["ParentComponent", "ChildComponent"]); + }); }); diff --git a/src/config.ts b/src/config.ts index 9c305e9..698402c 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,77 +1,77 @@ import type { Options, OptionalOptions } from "./types"; export const defaultOptions: Options = { - enable: true, // Enable or disable the plugin. - mode: "random", // Obfuscate mode, "random", "simplify" or "simplify-seedable". - buildFolderPath: ".next", // Build folder of your project. - classConversionJsonFolderPath: "./css-obfuscator", // The folder path to store the before obfuscate and after obfuscated classes conversion table. - refreshClassConversionJson: false, // Refresh the class conversion JSON file. + enable: true, // Enable or disable the plugin. + mode: "random", // Obfuscate mode, "random", "simplify" or "simplify-seedable". + buildFolderPath: ".next", // Build folder of your project. + classConversionJsonFolderPath: "./css-obfuscator", // The folder path to store the before obfuscate and after obfuscated classes conversion table. + refreshClassConversionJson: false, // Refresh the class conversion JSON file. - /** - * @deprecated Not longer used from v3.0.0 and will be removed in the next major version. - */ - classLength: 5, // Length of the obfuscated class name. + /** + * @deprecated Not longer used from v3.0.0 and will be removed in the next major version. + */ + classLength: 5, // Length of the obfuscated class name. - /** - * @deprecated Merged into `prefix` from v3.0.0 and will be removed in the next major version. - */ - classPrefix: "", // Prefix of the obfuscated class name. + /** + * @deprecated Merged into `prefix` from v3.0.0 and will be removed in the next major version. + */ + classPrefix: "", // Prefix of the obfuscated class name. - /** - * @deprecated Merged into `suffix` from v3.0.0 and will be removed in the next major version. - */ - classSuffix: "", // Suffix of the obfuscated class name. + /** + * @deprecated Merged into `suffix` from v3.0.0 and will be removed in the next major version. + */ + classSuffix: "", // Suffix of the obfuscated class name. - prefix: "", // Prefix of the obfuscated class and ident name. - suffix: "", // Suffix of the obfuscated class and ident name. + prefix: "", // Prefix of the obfuscated class and ident name. + suffix: "", // Suffix of the obfuscated class and ident name. - /** - * @deprecated Merged into `ignorePatterns.selectors` from v3.0.0 and will be removed in the next major version. - */ - classIgnore: [], // The class names to be ignored during obfuscation. - ignorePatterns: { // The patterns to be ignored during obfuscation. - selectors: [], // The selectors to be ignored during obfuscation. - idents: [], // The idents to be ignored during obfuscation. - }, + /** + * @deprecated Merged into `ignorePatterns.selectors` from v3.0.0 and will be removed in the next major version. + */ + classIgnore: [], // The class names to be ignored during obfuscation. + ignorePatterns: { // The patterns to be ignored during obfuscation. + selectors: [], // The selectors to be ignored during obfuscation. + idents: [], // The idents to be ignored during obfuscation. + }, - allowExtensions: [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"], // The file extensions to be processed. - contentIgnoreRegexes: [ - /\.jsxs\)\("\w+"/g, // avoid accidentally obfuscate the HTML tag - ], // The regexes to match the file content to be ignored during obfuscation. + allowExtensions: [".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"], // The file extensions to be processed. + contentIgnoreRegexes: [ + /\.jsxs\)\("\w+"/g, // avoid accidentally obfuscate the HTML tag + ], // The regexes to match the file content to be ignored during obfuscation. - whiteListedFolderPaths: [], // Only obfuscate files in these folders - blackListedFolderPaths: ["./.next/cache"], // Don't obfuscate files in these folders - enableMarkers: false, // Enable or disable the obfuscate marker classes. - markers: ["next-css-obfuscation"], // Classes that indicate component(s) need to obfuscate. - removeMarkersAfterObfuscated: true, // Remove the obfuscation markers from HTML elements after obfuscation. - removeOriginalCss: false, // Delete original CSS from CSS files if it has a obfuscated version. - generatorSeed: undefined, // The seed for the random generator. "undefined" means use random seed. + whiteListedFolderPaths: [], // Only obfuscate files in these folders + blackListedFolderPaths: ["./.next/cache"], // Don't obfuscate files in these folders + enableMarkers: false, // Enable or disable the obfuscate marker classes. + markers: ["next-css-obfuscation"], // Classes that indicate component(s) need to obfuscate. + removeMarkersAfterObfuscated: true, // Remove the obfuscation markers from HTML elements after obfuscation. + removeOriginalCss: false, // Delete original CSS from CSS files if it has a obfuscated version. + generatorSeed: undefined, // The seed for the random generator. "undefined" means use random seed. - /** - * Experimental feature - */ - enableJsAst: true, // Whether to obfuscate JS files using abstract syntax tree parser. (Experimental feature) + /** + * Experimental feature + */ + enableJsAst: true, // Whether to obfuscate JS files using abstract syntax tree parser. (Experimental feature) - logLevel: "info", // Log level + logLevel: "info", // Log level }; export class Config { - private options: Options; + private options: Options; - constructor(options?: OptionalOptions) { - if (!options) { - this.options = defaultOptions; - return; - } - this.options = { - ...defaultOptions, - ...options, - }; + constructor(options?: OptionalOptions) { + if (!options) { + this.options = defaultOptions; + return; } + this.options = { + ...defaultOptions, + ...options, + }; + } - public get(): Options { - return this.options; - } + public get(): Options { + return this.options; + } } export default Config; diff --git a/src/handlers/css.ts b/src/handlers/css.ts index 728e067..1a0facf 100644 --- a/src/handlers/css.ts +++ b/src/handlers/css.ts @@ -8,185 +8,185 @@ import lightningcssInit, { transform as lightningcssTransform } from "lightningc // TODO: html failed with . import { - log, - findAllFilesWithExt, - getFilenameFromPath, - stringToNumber, - loadConversionTables, + log, + findAllFilesWithExt, + getFilenameFromPath, + stringToNumber, + loadConversionTables, } from "../utils"; const obfuscateCss = async ({ - cssPath, - removeOriginalCss, - isFullObfuscation, - outCssPath, - conversionTables, - - mode = "random", - prefix, - suffix, - ignorePatterns, - generatorSeed = Math.random().toString().slice(2, 10), // take 8 digits from the random number + cssPath, + removeOriginalCss, + isFullObfuscation, + outCssPath, + conversionTables, + + mode = "random", + prefix, + suffix, + ignorePatterns, + generatorSeed = Math.random().toString().slice(2, 10), // take 8 digits from the random number }: { - cssPath: string, - removeOriginalCss?: boolean, - isFullObfuscation?: boolean, - outCssPath?: string, - conversionTables?: ConversionTables, - - mode?: obfuscateMode, - prefix?: string, - suffix?: string, - ignorePatterns?: TransformProps["ignorePatterns"], - generatorSeed?: string, + cssPath: string, + removeOriginalCss?: boolean, + isFullObfuscation?: boolean, + outCssPath?: string, + conversionTables?: ConversionTables, + + mode?: obfuscateMode, + prefix?: string, + suffix?: string, + ignorePatterns?: TransformProps["ignorePatterns"], + generatorSeed?: string, }) => { - if (!outCssPath) { - // If no output path is provided, use the input path - outCssPath = cssPath; - } else if (!fs.existsSync(path.dirname(outCssPath))) { - // Create the output directory if it doesn't exist - fs.mkdirSync(path.dirname(outCssPath)); + if (!outCssPath) { + // If no output path is provided, use the input path + outCssPath = cssPath; + } else if (!fs.existsSync(path.dirname(outCssPath))) { + // Create the output directory if it doesn't exist + fs.mkdirSync(path.dirname(outCssPath)); + } + + const cssContent = fs.readFileSync(cssPath, "utf-8"); + + let transformerMode: TransformProps["mode"] = mode === "simplify" ? "minimal" : "hash"; + if (!transformerMode) { + // @ts-expect-error - "simplify-seedable" is deprecated but for backward compatibility + if (mode === "simplify-seedable") { + log("warn", "CSS obfuscation", "The 'simplify-seedable' mode is deprecated, please use 'random' or 'simplify' instead. Now will fall back to 'random' mode."); + transformerMode = "hash"; } - - const cssContent = fs.readFileSync(cssPath, "utf-8"); - - let transformerMode: TransformProps["mode"] = mode === "simplify" ? "minimal" : "hash"; - if (!transformerMode) { - // @ts-expect-error - "simplify-seedable" is deprecated but for backward compatibility - if (mode === "simplify-seedable") { - log("warn", "CSS obfuscation", "The 'simplify-seedable' mode is deprecated, please use 'random' or 'simplify' instead. Now will fall back to 'random' mode."); - transformerMode = "hash"; - } - log("error", "CSS obfuscation", `Invalid mode: ${mode}`); - throw new Error(`Invalid mode: ${mode}`); - } - - let finCss = ""; - const { css: obfuscatedCss, conversionTables: newConversionTables } = transform({ - css: cssContent, - conversionTables: conversionTables, - mode: transformerMode, - prefix: prefix, - suffix: suffix, - seed: stringToNumber(generatorSeed), - ignorePatterns: ignorePatterns, + log("error", "CSS obfuscation", `Invalid mode: ${mode}`); + throw new Error(`Invalid mode: ${mode}`); + } + + let finCss = ""; + const { css: obfuscatedCss, conversionTables: newConversionTables } = transform({ + css: cssContent, + conversionTables: conversionTables, + mode: transformerMode, + prefix: prefix, + suffix: suffix, + seed: stringToNumber(generatorSeed), + ignorePatterns: ignorePatterns, + }); + + if (removeOriginalCss) { + finCss = obfuscatedCss; + } else { + // if keep original selector, we need to merge the original css with the obfuscated css + // then minify the css in order to marge the css + const mixedCss = cssContent + obfuscatedCss; + const { code } = lightningcssTransform({ + filename: "style.css", + code: new TextEncoder().encode(mixedCss), + minify: true, }); - - if (removeOriginalCss) { - finCss = obfuscatedCss; - } else { - // if keep original selector, we need to merge the original css with the obfuscated css - // then minify the css in order to marge the css - const mixedCss = cssContent + obfuscatedCss; - const { code } = lightningcssTransform({ - filename: "style.css", - code: new TextEncoder().encode(mixedCss), - minify: true, - }); - finCss = new TextDecoder().decode(code); - } - - const totalConversion = Object.keys(newConversionTables.selectors).length + Object.keys(newConversionTables.idents).length; - if (removeOriginalCss) { - log("info", "CSS rules:", `Modified ${totalConversion} CSS rules to ${getFilenameFromPath(cssPath)}`); - } else { - const oldTotalConversion = conversionTables ? Object.keys(conversionTables.selectors).length + Object.keys(conversionTables.idents).length : 0; - log("info", "CSS rules:", `Added ${totalConversion - oldTotalConversion} new CSS rules to ${getFilenameFromPath(cssPath)}`); - } - - // Save the obfuscated CSS to the output path - const sizeBefore = Buffer.byteLength(cssContent, "utf8"); - fs.writeFileSync(outCssPath, obfuscatedCss); - const sizeAfter = Buffer.byteLength(obfuscatedCss, "utf8"); - const percentChange = Math.round(((sizeAfter) / sizeBefore) * 100); - log("success", "CSS obfuscated:", `Size from ${sizeBefore} to ${sizeAfter} bytes (${percentChange}%) in ${getFilenameFromPath(cssPath)}`); - - return { - conversionTables: newConversionTables, - } + finCss = new TextDecoder().decode(code); + } + + const totalConversion = Object.keys(newConversionTables.selectors).length + Object.keys(newConversionTables.idents).length; + if (removeOriginalCss) { + log("info", "CSS rules:", `Modified ${totalConversion} CSS rules to ${getFilenameFromPath(cssPath)}`); + } else { + const oldTotalConversion = conversionTables ? Object.keys(conversionTables.selectors).length + Object.keys(conversionTables.idents).length : 0; + log("info", "CSS rules:", `Added ${totalConversion - oldTotalConversion} new CSS rules to ${getFilenameFromPath(cssPath)}`); + } + + // Save the obfuscated CSS to the output path + const sizeBefore = Buffer.byteLength(cssContent, "utf8"); + fs.writeFileSync(outCssPath, obfuscatedCss); + const sizeAfter = Buffer.byteLength(obfuscatedCss, "utf8"); + const percentChange = Math.round(((sizeAfter) / sizeBefore) * 100); + log("success", "CSS obfuscated:", `Size from ${sizeBefore} to ${sizeAfter} bytes (${percentChange}%) in ${getFilenameFromPath(cssPath)}`); + + return { + conversionTables: newConversionTables, + } } /** * */ export const obfuscateCssFiles = async ({ - selectorConversionJsonFolderPath, - buildFolderPath, - whiteListedFolderPaths = [], - blackListedFolderPaths = [], - mode = "random", - prefix, - suffix, - ignorePatterns, - generatorSeed = new Date().getTime().toString(), - removeOriginalCss = false, + selectorConversionJsonFolderPath, + buildFolderPath, + whiteListedFolderPaths = [], + blackListedFolderPaths = [], + mode = "random", + prefix, + suffix, + ignorePatterns, + generatorSeed = new Date().getTime().toString(), + removeOriginalCss = false, }: { - selectorConversionJsonFolderPath: string, - buildFolderPath: string, - whiteListedFolderPaths: (string | RegExp)[], - blackListedFolderPaths: (string | RegExp)[], - mode?: obfuscateMode, - prefix?: string, - suffix?: string, - ignorePatterns?: TransformProps["ignorePatterns"], - generatorSeed?: string, - removeOriginalCss?: boolean, + selectorConversionJsonFolderPath: string, + buildFolderPath: string, + whiteListedFolderPaths: (string | RegExp)[], + blackListedFolderPaths: (string | RegExp)[], + mode?: obfuscateMode, + prefix?: string, + suffix?: string, + ignorePatterns?: TransformProps["ignorePatterns"], + generatorSeed?: string, + removeOriginalCss?: boolean, }) => { - // Initialize nessesary modules - await Promise.all([ - initTransform(), - lightningcssInit(), - ]); - - // Create the selector conversion JSON folder if it doesn't exist - if (!fs.existsSync(selectorConversionJsonFolderPath)) { - fs.mkdirSync(selectorConversionJsonFolderPath); - } - - // Load and merge all JSON files in the selector conversion folder - const conversionTables = loadConversionTables(selectorConversionJsonFolderPath); + // Initialize nessesary modules + await Promise.all([ + initTransform(), + lightningcssInit(), + ]); + + // Create the selector conversion JSON folder if it doesn't exist + if (!fs.existsSync(selectorConversionJsonFolderPath)) { + fs.mkdirSync(selectorConversionJsonFolderPath); + } + + // Load and merge all JSON files in the selector conversion folder + const conversionTables = loadConversionTables(selectorConversionJsonFolderPath); + + // Get all CSS files using the unified path filtering function + const cssPaths = findAllFilesWithExt(".css", buildFolderPath, { + whiteListedFolderPaths, + blackListedFolderPaths, + }); + + const tables: ConversionTables = { + selectors: {}, + idents: {}, + }; + + cssPaths.forEach(async (cssPath) => { + const { conversionTables: newConversionTables } = await obfuscateCss({ + cssPath: cssPath, + conversionTables: conversionTables, + + prefix, + suffix, + mode, + ignorePatterns, + generatorSeed, + removeOriginalCss, + }); - // Get all CSS files using the unified path filtering function - const cssPaths = findAllFilesWithExt(".css", buildFolderPath, { - whiteListedFolderPaths, - blackListedFolderPaths, + // Merge the conversion tables + Object.entries(newConversionTables.selectors).forEach(([key, value]) => { + if (!tables.selectors[key]) { + // If it doesn't exist, create a new entry + tables.selectors[key] = value; + } }); - - const tables: ConversionTables = { - selectors: {}, - idents: {}, - }; - - cssPaths.forEach(async (cssPath) => { - const { conversionTables: newConversionTables } = await obfuscateCss({ - cssPath: cssPath, - conversionTables: conversionTables, - - prefix, - suffix, - mode, - ignorePatterns, - generatorSeed, - removeOriginalCss, - }); - - // Merge the conversion tables - Object.entries(newConversionTables.selectors).forEach(([key, value]) => { - if (!tables.selectors[key]) { - // If it doesn't exist, create a new entry - tables.selectors[key] = value; - } - }); - Object.entries(newConversionTables.idents).forEach(([key, value]) => { - if (!tables.idents[key]) { - // If it doesn't exist, create a new entry - tables.idents[key] = value; - } - }); + Object.entries(newConversionTables.idents).forEach(([key, value]) => { + if (!tables.idents[key]) { + // If it doesn't exist, create a new entry + tables.idents[key] = value; + } }); + }); - return { - conversionTables: tables, - }; + return { + conversionTables: tables, + }; } diff --git a/src/handlers/html.ts b/src/handlers/html.ts index 2ba059d..dec206b 100644 --- a/src/handlers/html.ts +++ b/src/handlers/html.ts @@ -89,7 +89,7 @@ export const obfuscateHtmlClassNames = ({ modifiedHtml += `<${tagName}`; for (const key in attribs) { // modifiedHtml += ` ${key}="${attribs[key]}"`; - modifiedHtml += ` ${key}="${htmlEscape(attribs[key])}"`; + modifiedHtml += ` ${key}="${htmlEscape(attribs[key])}"`; } if (voidTags.includes(tagName)) { modifiedHtml += " />"; diff --git a/src/handlers/js-ast.ts b/src/handlers/js-ast.ts index 8b368e3..8ab82d4 100644 --- a/src/handlers/js-ast.ts +++ b/src/handlers/js-ast.ts @@ -42,7 +42,7 @@ export const obfuscateJsWithAst = ( : str; const { obfuscatedContent, usedKeys: obfuscateUsedKeys } = obfuscateKeys(selectorConversion, str); - + if (obfuscatedContent !== str) { obfuscateUsedKeys.forEach(key => usedKeys.add(key)); return obfuscatedContent; diff --git a/src/types.ts b/src/types.ts index e97630c..f52c053 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,77 +5,77 @@ export type obfuscateMode = "random" | "simplify"; export type SelectorConversion = ConversionTables["selectors"]; export type Options = { - enable: boolean; - mode: obfuscateMode; - buildFolderPath: string; - classConversionJsonFolderPath: string; - refreshClassConversionJson: boolean; + enable: boolean; + mode: obfuscateMode; + buildFolderPath: string; + classConversionJsonFolderPath: string; + refreshClassConversionJson: boolean; - /** - * @deprecated Not longer used from v3.0.0 and will be removed in the next major version. - */ - classLength: number; - - /** - * @deprecated Merged into `prefix` from v3.0.0 and will be removed in the next major version. - */ - classPrefix: string; - /** - * @deprecated Merged into `suffix` from v3.0.0 and will be removed in the next major version. - */ - classSuffix: string; - prefix: string; - suffix: string; + /** + * @deprecated Not longer used from v3.0.0 and will be removed in the next major version. + */ + classLength: number; - /** - * @deprecated Merged into `ignorePatterns.selectors` from v3.0.0 and will be removed in the next major version. - */ - classIgnore: (string | RegExp)[]; - ignorePatterns: TransformProps["ignorePatterns"]; + /** + * @deprecated Merged into `prefix` from v3.0.0 and will be removed in the next major version. + */ + classPrefix: string; + /** + * @deprecated Merged into `suffix` from v3.0.0 and will be removed in the next major version. + */ + classSuffix: string; + prefix: string; + suffix: string; - allowExtensions: string[]; - contentIgnoreRegexes: RegExp[]; + /** + * @deprecated Merged into `ignorePatterns.selectors` from v3.0.0 and will be removed in the next major version. + */ + classIgnore: (string | RegExp)[]; + ignorePatterns: TransformProps["ignorePatterns"]; - whiteListedFolderPaths: (string | RegExp)[]; - blackListedFolderPaths: (string | RegExp)[]; - enableMarkers: boolean; - markers: string[]; - removeMarkersAfterObfuscated: boolean; - removeOriginalCss: boolean; - generatorSeed: string | undefined; + allowExtensions: string[]; + contentIgnoreRegexes: RegExp[]; - enableJsAst: boolean; + whiteListedFolderPaths: (string | RegExp)[]; + blackListedFolderPaths: (string | RegExp)[]; + enableMarkers: boolean; + markers: string[]; + removeMarkersAfterObfuscated: boolean; + removeOriginalCss: boolean; + generatorSeed: string | undefined; - logLevel: LogLevel; + enableJsAst: boolean; + + logLevel: LogLevel; } export type OptionalOptions = { - enable?: boolean; - mode?: obfuscateMode; - buildFolderPath?: string; - classConversionJsonFolderPath?: string; - refreshClassConversionJson?: boolean; + enable?: boolean; + mode?: obfuscateMode; + buildFolderPath?: string; + classConversionJsonFolderPath?: string; + refreshClassConversionJson?: boolean; - classLength?: number; - classPrefix?: string; - classSuffix?: string; - classIgnore?: string[]; - allowExtensions?: string[]; - contentIgnoreRegexes: RegExp[]; + classLength?: number; + classPrefix?: string; + classSuffix?: string; + classIgnore?: string[]; + allowExtensions?: string[]; + contentIgnoreRegexes: RegExp[]; - whiteListedFolderPaths?: (string | RegExp)[]; - blackListedFolderPaths?: (string | RegExp)[]; - enableMarkers?: boolean; - markers?: string[]; - removeMarkersAfterObfuscated?: boolean; - removeOriginalCss?: boolean; - generatorSeed?: string; + whiteListedFolderPaths?: (string | RegExp)[]; + blackListedFolderPaths?: (string | RegExp)[]; + enableMarkers?: boolean; + markers?: string[]; + removeMarkersAfterObfuscated?: boolean; + removeOriginalCss?: boolean; + generatorSeed?: string; - enableJsAst?: boolean; + enableJsAst?: boolean; - logLevel?: LogLevel; + logLevel?: LogLevel; } export interface HtmlCharacterEntityConversion { - [key: string]: string; + [key: string]: string; } From 5492d56d1871c0d58aecbf9de8bc046e2bb6a0ad Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Mon, 21 Apr 2025 23:41:55 +0100 Subject: [PATCH 11/32] refactor: Remove unused dependency 'recoverable-random' from package files --- package-lock.json | 6 ------ package.json | 1 - src/__tests__/utils.test.ts | 2 -- src/utils.ts | 2 -- 4 files changed, 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9c39053..8ed2de5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,6 @@ "html-escaper": "^3.0.3", "htmlparser2": "^10.0.0", "lightningcss-wasm": "^1.29.3", - "recoverable-random": "^1.0.5", "yargs": "^17.7.2" }, "bin": { @@ -2373,11 +2372,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/recoverable-random": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/recoverable-random/-/recoverable-random-1.0.5.tgz", - "integrity": "sha512-/8pSRgo2b96TZPK7UshniNn0A/q3ePARXsDL8z0Yvhhk7+PESziarQ0e8mn4YNM7qB6bX0mPcEfMtda4RY/ahA==" - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", diff --git a/package.json b/package.json index bd3d144..47a26e8 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,6 @@ "html-escaper": "^3.0.3", "htmlparser2": "^10.0.0", "lightningcss-wasm": "^1.29.3", - "recoverable-random": "^1.0.5", "yargs": "^17.7.2" }, "devDependencies": { diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts index 48c34e6..77fbd65 100644 --- a/src/__tests__/utils.test.ts +++ b/src/__tests__/utils.test.ts @@ -4,8 +4,6 @@ import { getFilenameFromPath, duplicationCheck, } from "../utils"; -import NumberGenerator from "recoverable-random"; - //! ================================ //! findContentBetweenMarker diff --git a/src/utils.ts b/src/utils.ts index 79b34e4..1e699db 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,12 +1,10 @@ import fs from "node:fs"; import path from "node:path"; -import NumberGenerator from "recoverable-random"; import type { LogLevel, SelectorConversion, } from "./types"; import { cssEscape, cssUnescape, type ConversionTables } from "css-seasoning"; - import { obfuscateHtmlClassNames } from "./handlers/html"; import { obfuscateJs } from "./handlers/js"; From b43a19477962fe85e7bf26b890624998648be1ba Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Mon, 21 Apr 2025 23:58:16 +0100 Subject: [PATCH 12/32] feat: Enables semantic release for automated publishing Sets up semantic release to automate the release process. This includes: - Adding a release workflow file. - Installing semantic-release and related plugins. - Creating a release configuration file. This automates versioning, changelog generation, and package publishing. --- .github/workflows/release.yml | 32 + package-lock.json | 6514 +++++++++++++++++++++++++++++++-- package.json | 7 +- release.config.cjs | 22 + tsconfig.json | 7 +- 5 files changed, 6176 insertions(+), 406 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 release.config.cjs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..3f251c5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,32 @@ +name: Release + +on: + push: + branches: + - main + - beta + +permissions: + contents: write # to be able to publish a GitHub release + issues: write # to be able to comment on released issues + pull-requests: write # to be able to comment on released pull requests + id-token: write # to enable use of OIDC for npm provenance + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install dependencies + run: npm install + + - name: Build NPM package + run: npm run build + + - name: Create Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: npm run semantic-release diff --git a/package-lock.json b/package-lock.json index 8ed2de5..90623f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,8 @@ }, "devDependencies": { "@biomejs/biome": "1.9.4", + "@semantic-release/git": "^10.0.1", + "@semantic-release/npm": "^12.0.1", "@types/babel__generator": "^7.6.8", "@types/babel__traverse": "^7.20.5", "@types/html-escaper": "^3.0.4", @@ -32,6 +34,7 @@ "@types/yargs": "^17.0.32", "@vitest/coverage-v8": "^3.0.5", "prettier": "^3.2.4", + "semantic-release": "^24.2.3", "tslib": "^2.6.2", "typescript": "^5.0.2", "vitest": "^3.0.5" @@ -51,81 +54,18 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/generator": { "version": "7.23.6", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", @@ -198,83 +138,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/parser": { "version": "7.26.7", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", @@ -498,6 +361,16 @@ "node": ">=14.21.3" } }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@deno/shim-deno": { "version": "0.18.2", "resolved": "https://registry.npmjs.org/@deno/shim-deno/-/shim-deno-0.18.2.tgz", @@ -1107,6 +980,216 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@octokit/auth-token": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz", + "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==", + "dev": true, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.5.tgz", + "integrity": "sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg==", + "dev": true, + "dependencies": { + "@octokit/auth-token": "^5.0.0", + "@octokit/graphql": "^8.2.2", + "@octokit/request": "^9.2.3", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "before-after-hook": "^3.0.2", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.4.tgz", + "integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==", + "dev": true, + "dependencies": { + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.2.tgz", + "integrity": "sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA==", + "dev": true, + "dependencies": { + "@octokit/request": "^9.2.3", + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.0.0.tgz", + "integrity": "sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw==", + "dev": true + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.6.0.tgz", + "integrity": "sha512-n5KPteiF7pWKgBIBJSk8qzoZWcUkza2O6A0za97pMGVrGfPdltxrfmfF5GucHYvHGZD8BdaZmmHGz5cX/3gdpw==", + "dev": true, + "dependencies": { + "@octokit/types": "^13.10.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/plugin-retry": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-7.2.1.tgz", + "integrity": "sha512-wUc3gv0D6vNHpGxSaR3FlqJpTXGWgqmk607N9L3LvPL4QjaxDgX/1nY2mGpT37Khn+nlIXdljczkRnNdTTV3/A==", + "dev": true, + "dependencies": { + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-throttling": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-9.6.1.tgz", + "integrity": "sha512-bt3EBUkeKUzDQXRCcFrR9SWVqlLFRRqcCrr6uAorWt6NXTyjMKqcGrFmXqJy9NCbnKgiIZ2OXWq04theFc76Jg==", + "dev": true, + "dependencies": { + "@octokit/types": "^13.7.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^6.1.3" + } + }, + "node_modules/@octokit/plugin-throttling/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true + }, + "node_modules/@octokit/plugin-throttling/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/request": { + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.3.tgz", + "integrity": "sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w==", + "dev": true, + "dependencies": { + "@octokit/endpoint": "^10.1.4", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "fast-content-type-parse": "^2.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.8.tgz", + "integrity": "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==", + "dev": true, + "dependencies": { + "@octokit/types": "^14.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.0.0.tgz", + "integrity": "sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^25.0.0" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1117,6 +1200,47 @@ "node": ">=14" } }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "dev": true, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dev": true, + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/@pnpm/npm-conf": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", + "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", + "dev": true, + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.34.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.2.tgz", @@ -1364,17 +1488,337 @@ "win32" ] }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true + }, + "node_modules/@semantic-release/commit-analyzer": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-13.0.1.tgz", + "integrity": "sha512-wdnBPHKkr9HhNhXOhZD5a2LNl91+hs8CC2vsAVYxtZH3y0dV3wKn+uZSN61rdJQZ8EGxzWB3inWocBHV9+u/CQ==", "dev": true, "dependencies": { - "@babel/types": "^7.0.0" + "conventional-changelog-angular": "^8.0.0", + "conventional-changelog-writer": "^8.0.0", + "conventional-commits-filter": "^5.0.0", + "conventional-commits-parser": "^6.0.0", + "debug": "^4.0.0", + "import-from-esm": "^2.0.0", + "lodash-es": "^4.17.21", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" } }, - "node_modules/@types/babel__traverse": { - "version": "7.20.5", + "node_modules/@semantic-release/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", + "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@semantic-release/git": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz", + "integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==", + "dev": true, + "dependencies": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "execa": "^5.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.0", + "p-reduce": "^2.0.0" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0" + } + }, + "node_modules/@semantic-release/git/node_modules/@semantic-release/error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", + "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", + "dev": true, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@semantic-release/git/node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/git/node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@semantic-release/git/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@semantic-release/git/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/@semantic-release/git/node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/git/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/git/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@semantic-release/git/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/git/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/git/node_modules/p-reduce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", + "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/git/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/@semantic-release/git/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@semantic-release/github": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-11.0.1.tgz", + "integrity": "sha512-Z9cr0LgU/zgucbT9cksH0/pX9zmVda9hkDPcgIE0uvjMQ8w/mElDivGjx1w1pEQ+MuQJ5CBq3VCF16S6G4VH3A==", + "dev": true, + "dependencies": { + "@octokit/core": "^6.0.0", + "@octokit/plugin-paginate-rest": "^11.0.0", + "@octokit/plugin-retry": "^7.0.0", + "@octokit/plugin-throttling": "^9.0.0", + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "debug": "^4.3.4", + "dir-glob": "^3.0.1", + "globby": "^14.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "issue-parser": "^7.0.0", + "lodash-es": "^4.17.21", + "mime": "^4.0.0", + "p-filter": "^4.0.0", + "url-join": "^5.0.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=24.1.0" + } + }, + "node_modules/@semantic-release/npm": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-12.0.1.tgz", + "integrity": "sha512-/6nntGSUGK2aTOI0rHPwY3ZjgY9FkXmEHbW9Kr+62NVOsyqpKKeP0lrCH+tphv+EsNdJNmqqwijTEnVWUMQ2Nw==", + "dev": true, + "dependencies": { + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "execa": "^9.0.0", + "fs-extra": "^11.0.0", + "lodash-es": "^4.17.21", + "nerf-dart": "^1.0.0", + "normalize-url": "^8.0.0", + "npm": "^10.5.0", + "rc": "^1.2.8", + "read-pkg": "^9.0.0", + "registry-auth-token": "^5.0.0", + "semver": "^7.1.2", + "tempy": "^3.0.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/release-notes-generator": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-14.0.3.tgz", + "integrity": "sha512-XxAZRPWGwO5JwJtS83bRdoIhCiYIx8Vhr+u231pQAsdFIAbm19rSVJLdnBN+Avvk7CKvNQE/nJ4y7uqKH6WTiw==", + "dev": true, + "dependencies": { + "conventional-changelog-angular": "^8.0.0", + "conventional-changelog-writer": "^8.0.0", + "conventional-commits-filter": "^5.0.0", + "conventional-commits-parser": "^6.0.0", + "debug": "^4.0.0", + "get-stream": "^7.0.0", + "import-from-esm": "^2.0.0", + "into-stream": "^7.0.0", + "lodash-es": "^4.17.21", + "read-package-up": "^11.0.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz", + "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", "dev": true, @@ -1403,6 +1847,12 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, "node_modules/@types/prop-types": { "version": "15.7.9", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.9.tgz", @@ -1579,6 +2029,46 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/aggregate-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", + "dev": true, + "dependencies": { + "clean-stack": "^5.2.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1601,6 +2091,30 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/argv-formatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", + "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", + "dev": true + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true + }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -1627,6 +2141,18 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/before-after-hook": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", + "dev": true + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "dev": true + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -1636,6 +2162,18 @@ "balanced-match": "^1.0.0" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -1645,6 +2183,15 @@ "node": ">=8" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/chai": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", @@ -1661,6 +2208,27 @@ "node": ">=12" } }, + "node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/check-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", @@ -1670,49 +2238,299 @@ "node": ">= 16" } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "node_modules/clean-stack": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", + "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", + "dev": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "escape-string-regexp": "5.0.0" }, "engines": { - "node": ">=12" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" }, "engines": { - "node": ">=7.0.0" + "node": ">=8.0.0", + "npm": ">=5.0.0" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "node_modules/cli-highlight/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-highlight/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cli-highlight/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-highlight/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/conventional-changelog-angular": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz", + "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.0.1.tgz", + "integrity": "sha512-hlqcy3xHred2gyYg/zXSMXraY2mjAYYo0msUCpK+BGyaVJMFCKWVXPIHiaacGO2GGp13kvHWXFhYmxT4QQqW3Q==", + "dev": true, + "dependencies": { + "conventional-commits-filter": "^5.0.0", + "handlebars": "^4.7.7", + "meow": "^13.0.0", + "semver": "^7.5.2" + }, + "bin": { + "conventional-changelog-writer": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-commits-filter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz", + "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-commits-parser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.1.0.tgz", + "integrity": "sha512-5nxDo7TwKB5InYBl4ZC//1g9GRwB/F3TXOGR9hgUjMGfvSP4Vu5NkpNro2+1+TIEy1vwxApl5ircECr2ri5JIw==", + "dev": true, + "dependencies": { + "meow": "^13.0.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/convert-hrtime": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", + "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { "node": ">= 8" } }, + "node_modules/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dev": true, + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/css": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", @@ -1784,6 +2602,27 @@ "node": ">=6" } }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -1846,6 +2685,27 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -1857,6 +2717,12 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", + "dev": true + }, "node_modules/entities": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", @@ -1868,72 +2734,264 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", - "dev": true + "node_modules/env-ci": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.1.0.tgz", + "integrity": "sha512-Z8dnwSDbV1XYM9SBF2J0GcNVvmfmfh3a49qddGIROhBoVro6MZVTji15z/sJbQ2ko2ei8n988EU1wzoLU/tF+g==", + "dev": true, + "dependencies": { + "execa": "^8.0.0", + "java-properties": "^1.0.2" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + } }, - "node_modules/esbuild": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", - "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "node_modules/env-ci/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": ">=18" + "node": ">=16.17" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.2", - "@esbuild/android-arm": "0.25.2", - "@esbuild/android-arm64": "0.25.2", - "@esbuild/android-x64": "0.25.2", - "@esbuild/darwin-arm64": "0.25.2", - "@esbuild/darwin-x64": "0.25.2", - "@esbuild/freebsd-arm64": "0.25.2", - "@esbuild/freebsd-x64": "0.25.2", - "@esbuild/linux-arm": "0.25.2", - "@esbuild/linux-arm64": "0.25.2", - "@esbuild/linux-ia32": "0.25.2", - "@esbuild/linux-loong64": "0.25.2", - "@esbuild/linux-mips64el": "0.25.2", - "@esbuild/linux-ppc64": "0.25.2", - "@esbuild/linux-riscv64": "0.25.2", - "@esbuild/linux-s390x": "0.25.2", - "@esbuild/linux-x64": "0.25.2", - "@esbuild/netbsd-arm64": "0.25.2", - "@esbuild/netbsd-x64": "0.25.2", - "@esbuild/openbsd-arm64": "0.25.2", - "@esbuild/openbsd-x64": "0.25.2", - "@esbuild/sunos-x64": "0.25.2", - "@esbuild/win32-arm64": "0.25.2", - "@esbuild/win32-ia32": "0.25.2", - "@esbuild/win32-x64": "0.25.2" + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "node_modules/env-ci/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, "engines": { - "node": ">=6" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "node_modules/env-ci/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, - "dependencies": { - "@types/estree": "^1.0.0" + "engines": { + "node": ">=16.17.0" } }, - "node_modules/expect-type": { - "version": "1.1.0", + "node_modules/env-ci/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", + "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.2", + "@esbuild/android-arm": "0.25.2", + "@esbuild/android-arm64": "0.25.2", + "@esbuild/android-x64": "0.25.2", + "@esbuild/darwin-arm64": "0.25.2", + "@esbuild/darwin-x64": "0.25.2", + "@esbuild/freebsd-arm64": "0.25.2", + "@esbuild/freebsd-x64": "0.25.2", + "@esbuild/linux-arm": "0.25.2", + "@esbuild/linux-arm64": "0.25.2", + "@esbuild/linux-ia32": "0.25.2", + "@esbuild/linux-loong64": "0.25.2", + "@esbuild/linux-mips64el": "0.25.2", + "@esbuild/linux-ppc64": "0.25.2", + "@esbuild/linux-riscv64": "0.25.2", + "@esbuild/linux-s390x": "0.25.2", + "@esbuild/linux-x64": "0.25.2", + "@esbuild/netbsd-arm64": "0.25.2", + "@esbuild/netbsd-x64": "0.25.2", + "@esbuild/openbsd-arm64": "0.25.2", + "@esbuild/openbsd-x64": "0.25.2", + "@esbuild/sunos-x64": "0.25.2", + "@esbuild/win32-arm64": "0.25.2", + "@esbuild/win32-ia32": "0.25.2", + "@esbuild/win32-x64": "0.25.2" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/execa": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.2.tgz", + "integrity": "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.3", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/expect-type": { + "version": "1.1.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", "dev": true, @@ -1941,6 +2999,114 @@ "node": ">=12.0.0" } }, + "node_modules/fast-content-type-parse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", + "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-versions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-6.0.0.tgz", + "integrity": "sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==", + "dev": true, + "dependencies": { + "semver-regex": "^4.0.5", + "super-regex": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", @@ -1957,6 +3123,30 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1971,6 +3161,18 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-timeout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-1.0.2.tgz", + "integrity": "sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1979,6 +3181,32 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-log-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.1.tgz", + "integrity": "sha512-PI+sPDvHXNPl5WNOErAK05s3j0lgwUzMN6o8cyQrDaKfT3qd7TmNJKeXX+SknI5I0QhG5fVPAEwSY4tRGDtYoQ==", + "dev": true, + "dependencies": { + "argv-formatter": "~1.0.0", + "spawn-error-forwarder": "~1.0.0", + "split2": "~1.0.0", + "stream-combiner2": "~1.1.1", + "through2": "~2.0.0", + "traverse": "0.6.8" + } + }, "node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -1999,6 +3227,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -2007,15 +3247,125 @@ "node": ">=4" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { "node": ">=8" } }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/hook-std": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz", + "integrity": "sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hosted-git-info": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", + "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", + "dev": true, + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "node_modules/html-escaper": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", @@ -2039,11 +3389,164 @@ "entities": "^6.0.0" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/ignore": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.3.tgz", + "integrity": "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from-esm": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-2.0.0.tgz", + "integrity": "sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "import-meta-resolve": "^4.0.0" + }, + "engines": { + "node": ">=18.20" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/index-to-position": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.1.0.tgz", + "integrity": "sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/into-stream": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz", + "integrity": "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==", + "dev": true, + "dependencies": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -2052,229 +3555,3624 @@ "node": ">=8" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/issue-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-7.0.1.tgz", + "integrity": "sha512-3YZcUUR2Wt1WsapF+S/WiA2WmlW0cWAoPccMqne7AxEBhCdFeTPjfv/Axb8V2gyCgY3nRw+ksZ3xSUX+R47iAg==", + "dev": true, + "dependencies": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports/node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/java-properties": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", + "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/lightningcss-wasm": { + "version": "1.29.3", + "resolved": "https://registry.npmjs.org/lightningcss-wasm/-/lightningcss-wasm-1.29.3.tgz", + "integrity": "sha512-j02QNSRVBKxsSBinpSCgx3x8XwwOoO50ekDO1O5rBf+dS0j46qSojv+3BDzMNCaGFJ18EjcnXGDkG3ImIbU/PQ==", + "bundleDependencies": [ + "napi-wasm" + ], + "dependencies": { + "napi-wasm": "^1.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-wasm/node_modules/napi-wasm": { + "version": "1.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true + }, + "node_modules/lodash.capitalize": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", + "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", + "dev": true + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "dev": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true + }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", + "dev": true + }, + "node_modules/loupe": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", + "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/marked": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", + "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/marked-terminal": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.3.0.tgz", + "integrity": "sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==", + "dev": true, + "dependencies": { + "ansi-escapes": "^7.0.0", + "ansi-regex": "^6.1.0", + "chalk": "^5.4.1", + "cli-highlight": "^2.1.11", + "cli-table3": "^0.6.5", + "node-emoji": "^2.2.0", + "supports-hyperlinks": "^3.1.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "marked": ">=1 <16" + } + }, + "node_modules/marked-terminal/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.7.tgz", + "integrity": "sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/nerf-dart": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", + "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==", + "dev": true + }, + "node_modules/node-emoji": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz", + "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/normalize-url": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.9.2.tgz", + "integrity": "sha512-iriPEPIkoMYUy3F6f3wwSZAU93E0Eg6cHwIR6jzzOXWSy+SD/rOODEs74cVONHKSx2obXtuUoyidVEhISrisgQ==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "dev": true, + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^8.0.0", + "@npmcli/config": "^9.0.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/map-workspaces": "^4.0.2", + "@npmcli/package-json": "^6.1.0", + "@npmcli/promise-spawn": "^8.0.2", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "@sigstore/tuf": "^3.0.0", + "abbrev": "^3.0.0", + "archy": "~1.0.0", + "cacache": "^19.0.1", + "chalk": "^5.3.0", + "ci-info": "^4.1.0", + "cli-columns": "^4.0.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.4.5", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^8.0.2", + "ini": "^5.0.0", + "init-package-json": "^7.0.2", + "is-cidr": "^5.1.0", + "json-parse-even-better-errors": "^4.0.0", + "libnpmaccess": "^9.0.0", + "libnpmdiff": "^7.0.0", + "libnpmexec": "^9.0.0", + "libnpmfund": "^6.0.0", + "libnpmhook": "^11.0.0", + "libnpmorg": "^7.0.0", + "libnpmpack": "^8.0.0", + "libnpmpublish": "^10.0.1", + "libnpmsearch": "^8.0.0", + "libnpmteam": "^7.0.0", + "libnpmversion": "^7.0.0", + "make-fetch-happen": "^14.0.3", + "minimatch": "^9.0.5", + "minipass": "^7.1.1", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^11.0.0", + "nopt": "^8.0.0", + "normalize-package-data": "^7.0.0", + "npm-audit-report": "^6.0.0", + "npm-install-checks": "^7.1.1", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-profile": "^11.0.1", + "npm-registry-fetch": "^18.0.2", + "npm-user-validate": "^3.0.0", + "p-map": "^4.0.0", + "pacote": "^19.0.1", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "qrcode-terminal": "^0.12.0", + "read": "^4.0.0", + "semver": "^7.6.3", + "spdx-expression-parse": "^4.0.0", + "ssri": "^12.0.0", + "supports-color": "^9.4.0", + "tar": "^6.2.1", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^6.0.0", + "which": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/metavuln-calculator": "^8.0.0", + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.1", + "@npmcli/query": "^4.0.0", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "bin-links": "^5.0.0", + "cacache": "^19.0.1", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^8.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.1", + "pacote": "^19.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "proggy": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "ssri": "^12.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "ci-info": "^4.0.0", + "ini": "^5.0.0", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "8.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^19.0.0", + "json-parse-even-better-errors": "^4.0.0", + "pacote": "^20.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote": { + "version": "20.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "normalize-package-data": "^7.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.1.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "9.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^11.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.3.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/aggregate-error": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "19.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/minizlib": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/p-map": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/tar": { + "version": "7.4.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "5.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.1.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.3.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/diff": { + "version": "5.2.0", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.3.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "10.4.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/package-json": "^6.0.0", + "npm-package-arg": "^12.0.0", + "promzard": "^2.0.0", + "read": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.1.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^4.1.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "3.4.3", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "binary-extensions": "^2.3.0", + "diff": "^5.1.0", + "minimatch": "^9.0.4", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "tar": "^6.2.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.0", + "@npmcli/run-script": "^9.0.1", + "ci-info": "^4.0.0", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "proc-log": "^5.0.0", + "read": "^4.0.0", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "11.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.0", + "@npmcli/run-script": "^9.0.1", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "10.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ci-info": "^4.0.0", + "normalize-package-data": "^7.0.0", + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1", + "proc-log": "^5.0.0", + "semver": "^7.3.7", + "sigstore": "^3.0.0", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.1", + "@npmcli/run-script": "^9.0.1", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "14.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/make-fetch-happen/node_modules/negotiator": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-fetch/node_modules/minizlib": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "11.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/chownr": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/minizlib": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/tar": { + "version": "7.4.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/yallist": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/nopt/node_modules/abbrev": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^8.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "7.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "12.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^7.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "10.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "11.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "18.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^14.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^12.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch/node_modules/minizlib": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/npm/node_modules/pacote": { + "version": "19.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-inflight": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/rimraf": { + "version": "5.0.10", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.6.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^3.0.0", + "@sigstore/tuf": "^3.0.0", + "@sigstore/verify": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/bundle": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/core": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/sign": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^14.0.1", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/verify": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "dev": true, + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.20", + "dev": true, + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/sprintf-js": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm/node_modules/ssri": { + "version": "12.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "3.0.1", + "debug": "^4.3.6", + "make-fetch-happen": "^14.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/@tufjs/models": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/which": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", "dev": true, + "inBundle": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=10" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=10" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "node_modules/npm/node_modules/write-file-atomic": { + "version": "6.0.0", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">=8" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/istanbul-reports/node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "dependencies": { - "@isaacs/cliui": "^8.0.2" + "mimic-fn": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=12" }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "bin": { - "jsesc": "bin/jsesc" - }, + "node_modules/p-each-series": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz", + "integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==", + "dev": true, "engines": { - "node": ">=4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lightningcss-wasm": { - "version": "1.29.3", - "resolved": "https://registry.npmjs.org/lightningcss-wasm/-/lightningcss-wasm-1.29.3.tgz", - "integrity": "sha512-j02QNSRVBKxsSBinpSCgx3x8XwwOoO50ekDO1O5rBf+dS0j46qSojv+3BDzMNCaGFJ18EjcnXGDkG3ImIbU/PQ==", - "bundleDependencies": [ - "napi-wasm" - ], + "node_modules/p-filter": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-4.1.0.tgz", + "integrity": "sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==", + "dev": true, "dependencies": { - "napi-wasm": "^1.0.1" + "p-map": "^7.0.1" }, "engines": { - "node": ">= 12.0.0" + "node": ">=18" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lightningcss-wasm/node_modules/napi-wasm": { - "version": "1.1.3", - "inBundle": true, - "license": "MIT" - }, - "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", - "dev": true + "node_modules/p-is-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", + "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "p-try": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", "dev": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "node_modules/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", "dev": true, - "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "node_modules/p-reduce": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz", + "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==", "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "callsites": "^3.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", "dev": true, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "dependencies": { + "parse5": "^6.0.1" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -2306,6 +7204,15 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/pathe": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.2.tgz", @@ -2325,9 +7232,42 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", + "dev": true, + "dependencies": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/postcss": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", @@ -2372,6 +7312,160 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/pretty-ms": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", + "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", + "dev": true, + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/read-package-up": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", + "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==", + "dev": true, + "dependencies": { + "find-up-simple": "^1.0.0", + "read-pkg": "^9.0.0", + "type-fest": "^4.6.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/registry-auth-token": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.0.tgz", + "integrity": "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==", + "dev": true, + "dependencies": { + "@pnpm/npm-conf": "^2.1.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -2380,12 +7474,31 @@ "node": ">=0.10.0" } }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", "deprecated": "https://github.com/lydell/resolve-url#deprecated" }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rollup": { "version": "4.34.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.2.tgz", @@ -2424,6 +7537,78 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/semantic-release": { + "version": "24.2.3", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.3.tgz", + "integrity": "sha512-KRhQG9cUazPavJiJEFIJ3XAMjgfd0fcK3B+T26qOl8L0UG5aZUjeRfREO0KM5InGtYwxqiiytkJrbcYoLDEv0A==", + "dev": true, + "dependencies": { + "@semantic-release/commit-analyzer": "^13.0.0-beta.1", + "@semantic-release/error": "^4.0.0", + "@semantic-release/github": "^11.0.0", + "@semantic-release/npm": "^12.0.0", + "@semantic-release/release-notes-generator": "^14.0.0-beta.1", + "aggregate-error": "^5.0.0", + "cosmiconfig": "^9.0.0", + "debug": "^4.0.0", + "env-ci": "^11.0.0", + "execa": "^9.0.0", + "figures": "^6.0.0", + "find-versions": "^6.0.0", + "get-stream": "^6.0.0", + "git-log-parser": "^1.2.0", + "hook-std": "^3.0.0", + "hosted-git-info": "^8.0.0", + "import-from-esm": "^2.0.0", + "lodash-es": "^4.17.21", + "marked": "^12.0.0", + "marked-terminal": "^7.0.0", + "micromatch": "^4.0.2", + "p-each-series": "^3.0.0", + "p-reduce": "^3.0.0", + "read-package-up": "^11.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.3.2", + "semver-diff": "^4.0.0", + "signale": "^1.2.1", + "yargs": "^17.5.1" + }, + "bin": { + "semantic-release": "bin/semantic-release.js" + }, + "engines": { + "node": ">=20.8.1" + } + }, "node_modules/semver": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", @@ -2436,46 +7621,194 @@ "semver": "bin/semver.js" }, "engines": { - "node": ">=10" + "node": ">=10" + } + }, + "node_modules/semver-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver-regex": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", + "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/signale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", + "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", + "dev": true, + "dependencies": { + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/signale/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/signale/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/signale/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" } }, - "node_modules/shebang-command": { + "node_modules/signale/node_modules/figures": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", "dev": true, "dependencies": { - "shebang-regex": "^3.0.0" + "escape-string-regexp": "^1.0.5" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/shebang-regex": { + "node_modules/signale/node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/siginfo": { + "node_modules/signale/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/skin-tone": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", + "dev": true, + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, "engines": { - "node": ">=14" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/source-map": { @@ -2514,6 +7847,53 @@ "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", "deprecated": "See https://github.com/lydell/source-map-url#deprecated" }, + "node_modules/spawn-error-forwarder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", + "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true + }, + "node_modules/split2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", + "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", + "dev": true, + "dependencies": { + "through2": "~2.0.0" + } + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -2526,6 +7906,25 @@ "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", "dev": true }, + "node_modules/stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", + "dev": true, + "dependencies": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -2578,6 +7977,52 @@ "node": ">=8" } }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/super-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-1.0.0.tgz", + "integrity": "sha512-CY8u7DtbvucKuquCmOFEKhr9Besln7n9uN8eFbwcoGYWXOMW07u2o8njWaiXt11ylS3qoGF55pILjRmPlbodyg==", + "dev": true, + "dependencies": { + "function-timeout": "^1.0.1", + "time-span": "^5.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2590,6 +8035,73 @@ "node": ">=8" } }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/tempy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", + "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", + "dev": true, + "dependencies": { + "is-stream": "^3.0.0", + "temp-dir": "^3.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/test-exclude": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", @@ -2604,6 +8116,52 @@ "node": ">=18" } }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/time-span": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", + "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", + "dev": true, + "dependencies": { + "convert-hrtime": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -2643,12 +8201,48 @@ "node": ">=14.0.0" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/traverse": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.8.tgz", + "integrity": "sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, + "node_modules/type-fest": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.0.tgz", + "integrity": "sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typescript": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", @@ -2662,18 +8256,107 @@ "node": ">=14.17" } }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dev": true, + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universal-user-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", + "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", + "dev": true + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", "deprecated": "Please see https://github.com/lydell/urix#deprecated" }, + "node_modules/url-join": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", + "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "node_modules/vite": { "version": "6.2.6", "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz", @@ -2868,6 +8551,12 @@ "node": ">=8" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -2902,6 +8591,15 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, "node_modules/xxhash-wasm": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", @@ -2945,6 +8643,18 @@ "engines": { "node": ">=12" } + }, + "node_modules/yoctocolors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", + "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 47a26e8..5c1f2a4 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,9 @@ "build": "npm run test && tsc", "dev": "tsc -w", "pub": "npm run build && npm publish", - "pub@beta": "npm run build && npm publish --tag beta" + "pub@beta": "npm run build && npm publish --tag beta", + "lint": "biome check .", + "semantic-release": "semantic-release" }, "repository": { "type": "git", @@ -35,6 +37,8 @@ }, "devDependencies": { "@biomejs/biome": "1.9.4", + "@semantic-release/git": "^10.0.1", + "@semantic-release/npm": "^12.0.1", "@types/babel__generator": "^7.6.8", "@types/babel__traverse": "^7.20.5", "@types/html-escaper": "^3.0.4", @@ -43,6 +47,7 @@ "@types/yargs": "^17.0.32", "@vitest/coverage-v8": "^3.0.5", "prettier": "^3.2.4", + "semantic-release": "^24.2.3", "tslib": "^2.6.2", "typescript": "^5.0.2", "vitest": "^3.0.5" diff --git a/release.config.cjs b/release.config.cjs new file mode 100644 index 0000000..8ec50a2 --- /dev/null +++ b/release.config.cjs @@ -0,0 +1,22 @@ +module.exports = { + branches: [{ name: "main" }, { name: "beta", prerelease: true }], + plugins: [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + [ + "@semantic-release/npm", + { + npmPublish: true, + pkgRoot: "dist", + }, + ], + [ + "@semantic-release/git", + { + message: + "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}", + assets: ["package.json", "CHANGELOG.md"], + }, + ], + ], +}; diff --git a/tsconfig.json b/tsconfig.json index 132c89a..a719f75 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,7 +13,7 @@ "esModuleInterop": true, "skipLibCheck": true, "moduleResolution": "node", - "newLine": "LF", + "newLine": "LF" }, "exclude": [ "node_modules", @@ -23,6 +23,7 @@ "bin", "patched-postcss-obfuscator", "demos", - "src/*.test.*" + "src/*.test.*", + "release.config.cjs" ] -} \ No newline at end of file +} From f51783907915a9ca11394cd9e8437af069af46de Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Tue, 22 Apr 2025 00:10:04 +0100 Subject: [PATCH 13/32] fix: fix html tests --- bin/cli.mjs | 2 +- biome.json | 13 +++++++---- src/__tests__/html.test.ts | 48 +++++++++++++++++++------------------- src/config.ts | 5 ++-- src/handlers/css.ts | 2 +- src/handlers/js-ast.ts | 6 ++--- src/types.ts | 4 ++-- src/utils.ts | 12 +++++----- 8 files changed, 49 insertions(+), 43 deletions(-) diff --git a/bin/cli.mjs b/bin/cli.mjs index 90ba1c3..40ac88e 100644 --- a/bin/cli.mjs +++ b/bin/cli.mjs @@ -2,4 +2,4 @@ import { obfuscateCli } from "../dist/index.js"; -obfuscateCli(); \ No newline at end of file +obfuscateCli(); diff --git a/biome.json b/biome.json index 03fb901..74c13be 100644 --- a/biome.json +++ b/biome.json @@ -7,10 +7,16 @@ }, "files": { "ignoreUnknown": false, - "ignore": [] + "ignore": [ + "node_modules", + ".next", + "packages/tsconfig", + "demos", + "coverage", + "dist" + ] }, "formatter": { - "ignore": ["node_modules", ".next"], "enabled": true, "indentStyle": "space", "lineWidth": 80, @@ -21,7 +27,6 @@ "enabled": true }, "linter": { - "ignore": ["node_modules", ".next", "packages/tsconfig"], "enabled": true, "rules": { "recommended": true, @@ -30,7 +35,7 @@ "noUselessTypeConstraint": "error", "useLiteralKeys": "error", "useOptionalChain": "error", - "noForEach": "off", + "noForEach": "off" }, "correctness": { "noUnusedVariables": "info", diff --git a/src/__tests__/html.test.ts b/src/__tests__/html.test.ts index 44693e7..80bf098 100644 --- a/src/__tests__/html.test.ts +++ b/src/__tests__/html.test.ts @@ -15,20 +15,20 @@ describe("obfuscateHtmlClassNames", () => { it("should obfuscate class names correctly", () => { // Arrange const html = `
`; - const selectorConversion: SelectorConversion = { ".foo": ".a" }; + const selectorConversion: SelectorConversion = { "\\.foo": "\\.a" }; // Act const result = obfuscateHtmlClassNames({ html, selectorConversion }); // Assert expect(result.obfuscatedContent).toEqual(`
`); - expect(result.usedKeys).to.deep.equal([".foo"]); + expect(result.usedKeys).to.deep.equal(["\\.foo"]); }); it("should handle nested tags with obfuscate class", () => { // Arrange const html = `
`; - const selectorConversion: SelectorConversion = { ".foo": ".a" }; + const selectorConversion: SelectorConversion = { "\\.foo": "\\.a" }; const keyClass = "key"; // Act @@ -36,13 +36,13 @@ describe("obfuscateHtmlClassNames", () => { // Assert expect(result.obfuscatedContent).toEqual(`
`); - expect(result.usedKeys).to.deep.equal([".foo"]); + expect(result.usedKeys).to.deep.equal(["\\.foo"]); }); it("should not obfuscate class names outside of obfuscate class scope", () => { // Arrange const html = `
`; - const selectorConversion: SelectorConversion = { ".foo": ".a", ".bar": ".b" }; + const selectorConversion: SelectorConversion = { "\\.foo": "\\.a", "\\.bar": "\\.b" }; const keyClass = "key"; // Act @@ -56,41 +56,41 @@ describe("obfuscateHtmlClassNames", () => { it("should handle script tags", () => { // Arrange const html = ``; - const selectorConversion: SelectorConversion = { ".fol": ".a", ".foo": ".b" }; + const selectorConversion: SelectorConversion = { "\\.fol": "\\.a", "\\.foo": "\\.b" }; // Act const result = obfuscateHtmlClassNames({ html, selectorConversion, obfuscateMarkerClass: "" }); // Assert expect(result.obfuscatedContent).toEqual(``); - expect(result.usedKeys).to.deep.equal([".fol", ".foo"]); + expect(result.usedKeys).to.deep.equal(["\\.fol", "\\.foo"]); }); it("should handle void tags", () => { // Arrange const html = ``; - const selectorConversion: SelectorConversion = { ".foo": ".a" }; + const selectorConversion: SelectorConversion = { "\\.foo": "\\.a" }; // Act const result = obfuscateHtmlClassNames({ html, selectorConversion }); // Assert expect(result.obfuscatedContent).toEqual(``); - expect(result.usedKeys).to.deep.equal([".foo"]); + expect(result.usedKeys).to.deep.equal(["\\.foo"]); }); it("should handle comments", () => { // Arrange const html = `
`; - const selectorConversion: SelectorConversion = { ".foo": ".a" }; + const selectorConversion: SelectorConversion = { "\\.foo": "\\.a" }; // Act const result = obfuscateHtmlClassNames({ html, selectorConversion }); // Assert expect(result.obfuscatedContent).toEqual(`
`); - expect(result.usedKeys).to.deep.equal([".foo"]); + expect(result.usedKeys).to.deep.equal(["\\.foo"]); }); it("should handle HTML without classes", () => { @@ -109,7 +109,7 @@ describe("obfuscateHtmlClassNames", () => { it("should handle empty HTML", () => { // Arrange const html = ""; - const selectorConversion: SelectorConversion = { ".foo": ".a" }; + const selectorConversion: SelectorConversion = { "\\.foo": "\\.a" }; // Act const result = obfuscateHtmlClassNames({ html, selectorConversion }); @@ -122,33 +122,33 @@ describe("obfuscateHtmlClassNames", () => { it("should handle HTML with multiple classes in one element", () => { // Arrange const html = `
`; - const selectorConversion: SelectorConversion = { ".foo": ".a", ".bar": ".b", ".baz": ".c" }; + const selectorConversion: SelectorConversion = { "\\.foo": "\\.a", "\\.bar": "\\.b", "\\.baz": "\\.c" }; // Act const result = obfuscateHtmlClassNames({ html, selectorConversion }); // Assert expect(result.obfuscatedContent).toEqual(`
`); - expect(result.usedKeys).to.deep.equal([".foo", ".bar", ".baz"]); + expect(result.usedKeys).to.deep.equal(["\\.foo", "\\.bar", "\\.baz"]); }); it("should handle HTML with nested structures and multiple classes", () => { // Arrange const html = `
`; - const selectorConversion: SelectorConversion = { ".foo": ".a", ".bar": ".b", ".baz": ".c" }; + const selectorConversion: SelectorConversion = { "\\.foo": "\\.a", "\\.bar": "\\.b", "\\.baz": "\\.c" }; // Act const result = obfuscateHtmlClassNames({ html, selectorConversion }); // Assert expect(result.obfuscatedContent).toEqual(`
`); - expect(result.usedKeys).to.deep.equal([".foo", ".bar", ".baz"]); + expect(result.usedKeys).to.deep.equal(["\\.foo", "\\.bar", "\\.baz"]); }); it("should handle HTML with obfuscate marker class", () => { // Arrange const html = `
`; - const selectorConversion: SelectorConversion = { ".foo": ".a" }; + const selectorConversion: SelectorConversion = { "\\.foo": "\\.a" }; const obfuscateMarkerClass = "key"; // Act @@ -156,13 +156,13 @@ describe("obfuscateHtmlClassNames", () => { // Assert expect(result.obfuscatedContent).toEqual(`
`); - expect(result.usedKeys).to.deep.equal([".foo"]); + expect(result.usedKeys).to.deep.equal(["\\.foo"]); }); it("should handle HTML with multiple classes and obfuscate marker class", () => { // Arrange const html = `
`; - const selectorConversion: SelectorConversion = { ".foo": ".a", ".bar": ".b", ".baz": ".c" }; + const selectorConversion: SelectorConversion = { "\\.foo": "\\.a", "\\.bar": "\\.b", "\\.baz": "\\.c" }; const obfuscateMarkerClass = "key"; // Act @@ -170,20 +170,20 @@ describe("obfuscateHtmlClassNames", () => { // Assert expect(result.obfuscatedContent).toEqual(`
`); - expect(result.usedKeys).to.deep.equal([".foo", ".bar", ".baz"]); + expect(result.usedKeys).to.deep.equal(["\\.foo", "\\.bar", "\\.baz"]); }); it("should handle HTML instruction", () => { // Arrange const html = `
`; - const selectorConversion: SelectorConversion = { ".foo": ".a" }; + const selectorConversion: SelectorConversion = { "\\.foo": "\\.a" }; // Act const result = obfuscateHtmlClassNames({ html, selectorConversion }); // Assert expect(result.obfuscatedContent).toEqual(`
`); - expect(result.usedKeys).to.deep.equal([".foo"]); + expect(result.usedKeys).to.deep.equal(["\\.foo"]); }); /** @@ -192,13 +192,13 @@ describe("obfuscateHtmlClassNames", () => { it("should handle double quot inside double quot", () => { // Arrange const html = `
`; - const selectorConversion: SelectorConversion = { ".foo": ".a" }; + const selectorConversion: SelectorConversion = { "\\.foo": "\\.a" }; // Act const result = obfuscateHtmlClassNames({ html, selectorConversion }); // Assert expect(result.obfuscatedContent).toEqual(`
`); - expect(result.usedKeys).to.deep.equal([".foo"]); + expect(result.usedKeys).to.deep.equal(["\\.foo"]); }); }); diff --git a/src/config.ts b/src/config.ts index 698402c..251c929 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,4 +1,4 @@ -import type { Options, OptionalOptions } from "./types"; +import type { OptionalOptions, Options } from "./types"; export const defaultOptions: Options = { enable: true, // Enable or disable the plugin. @@ -29,7 +29,8 @@ export const defaultOptions: Options = { * @deprecated Merged into `ignorePatterns.selectors` from v3.0.0 and will be removed in the next major version. */ classIgnore: [], // The class names to be ignored during obfuscation. - ignorePatterns: { // The patterns to be ignored during obfuscation. + ignorePatterns: { + // The patterns to be ignored during obfuscation. selectors: [], // The selectors to be ignored during obfuscation. idents: [], // The idents to be ignored during obfuscation. }, diff --git a/src/handlers/css.ts b/src/handlers/css.ts index 1a0facf..6febdd9 100644 --- a/src/handlers/css.ts +++ b/src/handlers/css.ts @@ -16,7 +16,7 @@ import { } from "../utils"; -const obfuscateCss = async ({ +const obfuscateCss = ({ cssPath, removeOriginalCss, isFullObfuscation, diff --git a/src/handlers/js-ast.ts b/src/handlers/js-ast.ts index 8ab82d4..d0c1354 100644 --- a/src/handlers/js-ast.ts +++ b/src/handlers/js-ast.ts @@ -1,10 +1,10 @@ +import type { SelectorConversion } from "../types"; import * as parser from "@babel/parser"; -import traverse, { NodePath } from "@babel/traverse"; +import traverse, { type NodePath } from "@babel/traverse"; import * as t from "@babel/types"; import generator from "@babel/generator"; import { obfuscateKeys } from "../utils"; -import { type SelectorConversion } from "../types"; /** * Obfuscate the JavaScript code using AST(Abstract Syntax Tree) @@ -18,7 +18,7 @@ export const obfuscateJsWithAst = ( code: string, selectorConversion: SelectorConversion | undefined, startingKeys: string[] = [], - stripUnnecessarySpace: boolean = true + stripUnnecessarySpace = true ) => { const ast = parser.parse(code, { sourceType: "module", plugins: ["jsx"] }); const usedKeys: Set = new Set(); diff --git a/src/types.ts b/src/types.ts index f52c053..acdafcf 100644 --- a/src/types.ts +++ b/src/types.ts @@ -47,7 +47,7 @@ export type Options = { enableJsAst: boolean; logLevel: LogLevel; -} +}; export type OptionalOptions = { enable?: boolean; @@ -74,7 +74,7 @@ export type OptionalOptions = { enableJsAst?: boolean; logLevel?: LogLevel; -} +}; export interface HtmlCharacterEntityConversion { [key: string]: string; diff --git a/src/utils.ts b/src/utils.ts index 1e699db..f61071d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -55,7 +55,7 @@ const issuer = "[next-css-obfuscator]"; let logLevel: LogLevel = "info"; const levels: LogLevel[] = ["debug", "info", "warn", "error", "success"]; -export const log = (type: LogLevel, task: string, data: any) => { +export const log = (type: LogLevel, task: string, data: unknown) => { //ref: https://github.com/n4j1Br4ch1D/postcss-obfuscator/blob/main/utils.js if (levels.indexOf(type) < levels.indexOf(logLevel)) { return; @@ -202,7 +202,7 @@ export const replaceJsonKeysInFiles = ( ); if (fileContent !== obfuscateScriptContent) { fileContent = obfuscateScriptContent; - log("debug", `Obscured keys in JS like content file:`, normalizePath(filePath)); + log("debug", "Obscured keys in JS like content file:", normalizePath(filePath)); } } @@ -221,7 +221,7 @@ export const replaceJsonKeysInFiles = ( ); if (fileContent !== obfuscateScriptContent) { fileContent = obfuscateScriptContent; - log("debug", `Obscured keys in JSX related file:`, normalizePath(filePath)); + log("debug", "Obscured keys in JSX related file:", normalizePath(filePath)); } } else if ([".html"].includes(fileExt)) { //! NEW @@ -269,7 +269,7 @@ export const obfuscateKeys = ( selectorConversion: SelectorConversion, fileContent: string, contentIgnoreRegexes: RegExp[] = [], - useHtmlEntity: boolean = false + useHtmlEntity = false ) => { //ref: https://github.com/n4j1Br4ch1D/postcss-obfuscator/blob/main/utils.js @@ -285,7 +285,7 @@ export const obfuscateKeys = ( let exactMatchRegex = new RegExp(`([\\s"'\\\`]|^)(${keyUse})(?=$|[\\s"'\\\`]|\\\\n|\\\\",|\\\\"})`, 'g'); // match exact wording & avoid ` ' "" // exactMatchRegex = new RegExp(`([\\s"'\\\`]|^)(${keyUse})(?=$|[\\s"'\\\`])`, 'g'); // match exact wording & avoid ` ' "" - const replacement = `$1` + selectorConversion[key].slice(1).replace(/\\/g, "").slice(1); + const replacement = `$1${selectorConversion[key].slice(1).replace(/\\/g, "").slice(1)}`; const matches = fileContent.match(exactMatchRegex); const originalObscuredContentPairs = matches?.map((match) => { @@ -358,7 +358,7 @@ export const normalizePath = (filePath: string) => { * @param direction - if "forward", the function will search the closest closeMarker after the startPosition, if "backward", the function will search the closest openMarker before the startPosition * @returns */ -export const findClosestSymbolPosition = (content: string, openMarker: string, closeMarker: string, startPosition: number = 0, direction: "forward" | "backward" = "backward") => { +export const findClosestSymbolPosition = (content: string, openMarker: string, closeMarker: string, startPosition = 0, direction: "forward" | "backward" = "backward") => { let level = 0; let currentPos = startPosition; From e33c10f9e4434a0dab54f9632b7094cf830379aa Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Tue, 22 Apr 2025 00:14:29 +0100 Subject: [PATCH 14/32] refactor: Update string interpolation to use double quotes for consistency in tests and handlers --- src/__tests__/js-ast.test.ts | 85 ++++++++++++++++++------------------ src/__tests__/js.test.ts | 8 ++-- src/__tests__/utils.test.ts | 11 +---- src/handlers/css.ts | 3 +- src/handlers/js.ts | 7 ++- 5 files changed, 52 insertions(+), 62 deletions(-) diff --git a/src/__tests__/js-ast.test.ts b/src/__tests__/js-ast.test.ts index 4e5a0aa..a4470b9 100644 --- a/src/__tests__/js-ast.test.ts +++ b/src/__tests__/js-ast.test.ts @@ -1,21 +1,20 @@ import { describe, it, expect } from "vitest"; -import traverse, { NodePath } from "@babel/traverse"; -import * as t from "@babel/types"; +import traverse, { type NodePath } from "@babel/traverse"; +import type * as t from "@babel/types"; import * as parser from "@babel/parser"; import generator from "@babel/generator"; import { searchStringLiterals, obfuscateJsWithAst } from "../handlers/js-ast"; -function stripCode(code: string) { +const stripCode = (code: string) => { return code.replace(/\s/g, ""); } - //! ================================ //! searchStringLiterals //! ================================ describe("searchStringLiterals", () => { - function findStartPointNode(ast: t.File) { + const findStartPointNode = (ast: t.File) => { let startPointNode: NodePath | undefined; traverse(ast, { FunctionDeclaration(path) { @@ -25,7 +24,7 @@ describe("searchStringLiterals", () => { }, }); return startPointNode; - } + }; //? ******************************* //? Basic @@ -48,7 +47,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -77,7 +76,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -119,7 +118,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -147,7 +146,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -181,7 +180,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -212,7 +211,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -249,7 +248,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -288,7 +287,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -329,7 +328,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -366,7 +365,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -403,7 +402,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -448,7 +447,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -495,7 +494,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -532,7 +531,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -561,7 +560,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -588,7 +587,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -623,7 +622,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -658,7 +657,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -691,7 +690,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -730,7 +729,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -769,7 +768,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -812,7 +811,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -855,7 +854,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -886,7 +885,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -919,7 +918,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -950,7 +949,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -985,7 +984,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -1020,7 +1019,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -1059,7 +1058,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -1112,7 +1111,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -1155,7 +1154,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -1192,7 +1191,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -1227,7 +1226,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -1270,7 +1269,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -1319,7 +1318,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -1348,7 +1347,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" @@ -1377,7 +1376,7 @@ describe("searchStringLiterals", () => { ` const ast = parser.parse(code); - let result: string[] = []; + const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); return "{{found}}" diff --git a/src/__tests__/js.test.ts b/src/__tests__/js.test.ts index 2e0d6c7..ff7c455 100644 --- a/src/__tests__/js.test.ts +++ b/src/__tests__/js.test.ts @@ -22,7 +22,7 @@ describe("searchForwardComponent", () => { test("should return multiple component names for multiple matches", () => { // Arrange - const content = `o.jsx(FirstComponent, props); o.jsx(SecondComponent, otherProps);`; + const content = "o.jsx(FirstComponent, props); o.jsx(SecondComponent, otherProps);"; // Act const result = searchForwardComponent(content); @@ -66,7 +66,7 @@ describe("searchForwardComponent", () => { test("should handle special characters in component names", () => { // Arrange - const content = `o.jsx($Comp_1, props); o.jsx(_Comp$2, otherProps);`; + const content = "o.jsx($Comp_1, props); o.jsx(_Comp$2, otherProps);"; // Act const result = searchForwardComponent(content); @@ -88,7 +88,7 @@ describe("searchForwardComponent", () => { test("should return component names when they are followed by a brace", () => { // Arrange - const content = `o.jsx(ComponentName, {props: true});`; + const content = "o.jsx(ComponentName, {props: true});"; // Act const result = searchForwardComponent(content); @@ -114,7 +114,7 @@ describe("searchForwardComponent", () => { test("should handle content with nested jsx calls", () => { // Arrange - const content = `o.jsx(ParentComponent, {children: o.jsx(ChildComponent, {})})`; + const content = "o.jsx(ParentComponent, {children: o.jsx(ChildComponent, {})})"; // Act const result = searchForwardComponent(content); diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts index 77fbd65..9a43b75 100644 --- a/src/__tests__/utils.test.ts +++ b/src/__tests__/utils.test.ts @@ -12,7 +12,7 @@ import { describe("findContentBetweenMarker", () => { it("should return the correct content between markers", () => { - const content = `123{{4}5{67}8}901{2345678}9`; + const content = "123{{4}5{67}8}901{2345678}9"; const targetStr = '5'; const openSymbol = '{'; const closeSymbol = '}'; @@ -123,15 +123,6 @@ describe("getFilenameFromPath", () => { // Assert expect(result).toBe("my report.pdf"); }); - - test("should throw an error for non-string inputs", () => { - // Arrange - const input: any = null; - - // Act and Assert - expect(() => getFilenameFromPath(input)).toThrow(TypeError); - }); - }); //! ================================ diff --git a/src/handlers/css.ts b/src/handlers/css.ts index 6febdd9..915ef39 100644 --- a/src/handlers/css.ts +++ b/src/handlers/css.ts @@ -1,8 +1,9 @@ import type { obfuscateMode } from "../types"; +import type { ConversionTables, TransformProps } from "css-seasoning"; import path from "node:path"; import fs from "node:fs"; -import { initTransform, transform, type ConversionTables, type TransformProps } from "css-seasoning"; +import { initTransform, transform } from "css-seasoning"; import lightningcssInit, { transform as lightningcssTransform } from "lightningcss-wasm"; // TODO: html failed with . diff --git a/src/handlers/js.ts b/src/handlers/js.ts index 292f415..65d484e 100644 --- a/src/handlers/js.ts +++ b/src/handlers/js.ts @@ -1,3 +1,4 @@ +import type { SelectorConversion } from "../types"; import { log, findContentBetweenMarker, @@ -7,8 +8,6 @@ import { addKeysToRegistery, findClosestSymbolPosition } from "../utils"; - -import { SelectorConversion } from "../types"; import { obfuscateJsWithAst } from "./js-ast"; export const searchForwardComponent = (content: string) => { @@ -61,14 +60,14 @@ export const obfuscateForwardComponentJs = (searchContent: string, wholeContent: }); if (classNameBlocks.length !== obfuscatedClassNameBlocks.length) { - log("error", `Component obfuscation:`, `classNameBlocks.length !== obfuscatedClassNameBlocks.length`); + log("error", "Component obfuscation:", "classNameBlocks.length !== obfuscatedClassNameBlocks.length"); return componentContent; } let obscuredCode = componentContent.code; for (let i = 0; i < classNameBlocks.length; i++) { obscuredCode = replaceFirstMatch(obscuredCode, classNameBlocks[i], obfuscatedClassNameBlocks[i]); } - log("debug", `Obscured keys in component:`, componentContent.name); + log("debug", "Obscured keys in component:", componentContent.name); return { name: componentContent.name, code: obscuredCode From 38d4ac24265c4d18a5d7fcdf0c04d45993174426 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Tue, 22 Apr 2025 00:22:09 +0100 Subject: [PATCH 15/32] chore: Remove version field from package.json --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 5c1f2a4..56926fa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,5 @@ { "name": "next-css-obfuscator", - "version": "3.0.0-b1", "description": "A package deeply inspired by PostCSS-Obfuscator but for Next.js.", "main": "dist/index.js", "type": "commonjs", From 8954a501276c51cb7ef99bde3a69f3236cf599b3 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Tue, 22 Apr 2025 00:35:47 +0100 Subject: [PATCH 16/32] ci: Update semantic release configuration and dependencies --- .github/workflows/release.yml | 2 +- package-lock.json | 2 +- package.json | 4 ++-- release.config.cjs | 1 + 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3f251c5..6cfbfaa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,4 +29,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - run: npm run semantic-release + run: npx semantic-release diff --git a/package-lock.json b/package-lock.json index 90623f5..ccc26c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,6 @@ "packages": { "": { "name": "next-css-obfuscator", - "version": "3.0.0-b1", "license": "MIT", "dependencies": { "@babel/generator": "^7.23.6", @@ -25,6 +24,7 @@ "devDependencies": { "@biomejs/biome": "1.9.4", "@semantic-release/git": "^10.0.1", + "@semantic-release/github": "^11.0.1", "@semantic-release/npm": "^12.0.1", "@types/babel__generator": "^7.6.8", "@types/babel__traverse": "^7.20.5", diff --git a/package.json b/package.json index 56926fa..1a5a8c1 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,7 @@ "dev": "tsc -w", "pub": "npm run build && npm publish", "pub@beta": "npm run build && npm publish --tag beta", - "lint": "biome check .", - "semantic-release": "semantic-release" + "lint": "biome check ." }, "repository": { "type": "git", @@ -37,6 +36,7 @@ "devDependencies": { "@biomejs/biome": "1.9.4", "@semantic-release/git": "^10.0.1", + "@semantic-release/github": "^11.0.1", "@semantic-release/npm": "^12.0.1", "@types/babel__generator": "^7.6.8", "@types/babel__traverse": "^7.20.5", diff --git a/release.config.cjs b/release.config.cjs index 8ec50a2..ac68d26 100644 --- a/release.config.cjs +++ b/release.config.cjs @@ -18,5 +18,6 @@ module.exports = { assets: ["package.json", "CHANGELOG.md"], }, ], + "@semantic-release/github", ], }; From 3002e5c365f9a371389030dac0dfdebe49a0cde2 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Tue, 22 Apr 2025 00:40:18 +0100 Subject: [PATCH 17/32] ci: Update Node.js setup in release workflow and change npm install to npm ci --- .github/workflows/release.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6cfbfaa..087b834 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,8 +19,13 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: "lts/*" + - name: Install dependencies - run: npm install + run: npm ci - name: Build NPM package run: npm run build From 0ec81314b5947119684efa2e8382e9c8f03520d1 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Tue, 22 Apr 2025 00:51:27 +0100 Subject: [PATCH 18/32] ci: Update GitHub Actions workflows to use actions/checkout@v4 and npm install --- .github/workflows/auto_test.yml | 2 +- .github/workflows/release.yml | 7 +------ .releaserc | 0 package.json | 1 + 4 files changed, 3 insertions(+), 7 deletions(-) create mode 100644 .releaserc diff --git a/.github/workflows/auto_test.yml b/.github/workflows/auto_test.yml index c39fa9b..38c6b2d 100644 --- a/.github/workflows/auto_test.yml +++ b/.github/workflows/auto_test.yml @@ -21,7 +21,7 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # Install dependencie using the runners shell - name: Install dependencies diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 087b834..6cfbfaa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,13 +19,8 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: Setup Node.js - uses: actions/setup-node@v3 - with: - node-version: "lts/*" - - name: Install dependencies - run: npm ci + run: npm install - name: Build NPM package run: npm run build diff --git a/.releaserc b/.releaserc new file mode 100644 index 0000000..e69de29 diff --git a/package.json b/package.json index 1a5a8c1..556883a 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "name": "next-css-obfuscator", + "version": "3.0.0-b1", "description": "A package deeply inspired by PostCSS-Obfuscator but for Next.js.", "main": "dist/index.js", "type": "commonjs", From 935dde8d466abd9ea88a0b06f6a0d0bbb49e4759 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Fri, 25 Apr 2025 11:23:52 +0100 Subject: [PATCH 19/32] ci: Update release workflow to handle push and pull request events, add dry run step --- .github/workflows/release.yml | 22 ++++++++++++++++------ .releaserc | 0 package-lock.json | 1 + release.config.cjs | 8 +------- 4 files changed, 18 insertions(+), 13 deletions(-) delete mode 100644 .releaserc diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6cfbfaa..4caadfa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,16 +5,19 @@ on: branches: - main - beta - -permissions: - contents: write # to be able to publish a GitHub release - issues: write # to be able to comment on released issues - pull-requests: write # to be able to comment on released pull requests - id-token: write # to enable use of OIDC for npm provenance + pull_request: + branches: + - main + - beta jobs: release: runs-on: ubuntu-latest + permissions: + contents: write # to be able to publish a GitHub release + issues: write # to be able to comment on released issues + pull-requests: write # to be able to comment on released pull requests + id-token: write # to enable use of OIDC for npm provenance steps: - name: Checkout code uses: actions/checkout@v4 @@ -26,7 +29,14 @@ jobs: run: npm run build - name: Create Release + if: github.event_name == 'push' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: npx semantic-release + + - name: Dry Run Release + if: github.event_name == 'pull_request' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: npx semantic-release --dry-run diff --git a/.releaserc b/.releaserc deleted file mode 100644 index e69de29..0000000 diff --git a/package-lock.json b/package-lock.json index ccc26c6..472ef8a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,6 +6,7 @@ "packages": { "": { "name": "next-css-obfuscator", + "version": "3.0.0-b1", "license": "MIT", "dependencies": { "@babel/generator": "^7.23.6", diff --git a/release.config.cjs b/release.config.cjs index ac68d26..0a39050 100644 --- a/release.config.cjs +++ b/release.config.cjs @@ -3,13 +3,7 @@ module.exports = { plugins: [ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", - [ - "@semantic-release/npm", - { - npmPublish: true, - pkgRoot: "dist", - }, - ], + "@semantic-release/npm", [ "@semantic-release/git", { From 670ce7dae04d19aa15136f5b3377151646963e7b Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 25 Apr 2025 12:53:53 +0000 Subject: [PATCH 20/32] chore(release): 1.0.0-beta.1 [skip ci] # 1.0.0-beta.1 (2025-04-25) ### Bug Fixes * fix html tests ([f517839](https://github.com/soranoo/next-css-obfuscator/commit/f51783907915a9ca11394cd9e8437af069af46de)) * **html:** fix [#57](https://github.com/soranoo/next-css-obfuscator/issues/57) ([a638538](https://github.com/soranoo/next-css-obfuscator/commit/a6385382a43ce497fb233f7b79d6f154fb513cc7)) * **html:** fix incorrect html class name truncation ([7cb985d](https://github.com/soranoo/next-css-obfuscator/commit/7cb985d4c424a4458e2a72db9e76da899c44da63)) * **js-ast:** added support to `MemberExpression` [#45](https://github.com/soranoo/next-css-obfuscator/issues/45) ([8c95ba0](https://github.com/soranoo/next-css-obfuscator/commit/8c95ba07d62608ca77eeb02db78dd4a5afe10a8c)) * **js-ast:** added support to `TemplateLiteral` & `TemplateElement` [#45](https://github.com/soranoo/next-css-obfuscator/issues/45) ([0dd46b3](https://github.com/soranoo/next-css-obfuscator/commit/0dd46b377cca59e59514e3a78b1e6f3b9b92b651)) ### Features * Enables semantic release for automated publishing ([b43a194](https://github.com/soranoo/next-css-obfuscator/commit/b43a19477962fe85e7bf26b890624998648be1ba)) * Upgrades to v3 with new CSS transformation ([0afd780](https://github.com/soranoo/next-css-obfuscator/commit/0afd7808b2c6405b5fbedb4b8ff0e34f813ccd82)) * v3 migration with TailwindCSS 4 support ([668a5f6](https://github.com/soranoo/next-css-obfuscator/commit/668a5f66abdd174bf8ecbd18e9872a9719c73508)) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 556883a..f344c9f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next-css-obfuscator", - "version": "3.0.0-b1", + "version": "1.0.0-beta.1", "description": "A package deeply inspired by PostCSS-Obfuscator but for Next.js.", "main": "dist/index.js", "type": "commonjs", From 2da79e5c84673c13621db7519fe76cd03491ca4a Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Fri, 25 Apr 2025 14:49:24 +0100 Subject: [PATCH 21/32] ci: Update release workflow to edit initial version and use npm scripts for semantic release --- .github/workflows/release.yml | 9 +++++++-- package.json | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4caadfa..c97d178 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,15 +28,20 @@ jobs: - name: Build NPM package run: npm run build + - name: Edit Inital Version + run: sed -i 's/\( FIRST_RELEASE\) = "1.0.0"/\1 = "3.0.0"/' node_modules/semantic-release/lib/definitions/constants.js + - name: Edit Inital Version + run: sed -i 's/\( FIRSTPRERELEASE\) = "1"/\1 = "3"/' node_modules/semantic-release/lib/definitions/constants.js + - name: Create Release if: github.event_name == 'push' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - run: npx semantic-release + run: npm run semantic-release - name: Dry Run Release if: github.event_name == 'pull_request' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: npx semantic-release --dry-run + run: npm run semantic-release --dry-run diff --git a/package.json b/package.json index f344c9f..8019bd4 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dev": "tsc -w", "pub": "npm run build && npm publish", "pub@beta": "npm run build && npm publish --tag beta", + "semantic-release": "semantic-release", "lint": "biome check ." }, "repository": { From 8072e37811dd4b5d876b82751004ed8ebd9e1ce0 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 25 Apr 2025 13:50:08 +0000 Subject: [PATCH 22/32] chore(release): 3.0.0-beta.3 [skip ci] # 3.0.0-beta.3 (2025-04-25) ### Bug Fixes * fix html tests ([f517839](https://github.com/soranoo/next-css-obfuscator/commit/f51783907915a9ca11394cd9e8437af069af46de)) * **html:** fix [#57](https://github.com/soranoo/next-css-obfuscator/issues/57) ([a638538](https://github.com/soranoo/next-css-obfuscator/commit/a6385382a43ce497fb233f7b79d6f154fb513cc7)) * **html:** fix incorrect html class name truncation ([7cb985d](https://github.com/soranoo/next-css-obfuscator/commit/7cb985d4c424a4458e2a72db9e76da899c44da63)) * **js-ast:** added support to `MemberExpression` [#45](https://github.com/soranoo/next-css-obfuscator/issues/45) ([8c95ba0](https://github.com/soranoo/next-css-obfuscator/commit/8c95ba07d62608ca77eeb02db78dd4a5afe10a8c)) * **js-ast:** added support to `TemplateLiteral` & `TemplateElement` [#45](https://github.com/soranoo/next-css-obfuscator/issues/45) ([0dd46b3](https://github.com/soranoo/next-css-obfuscator/commit/0dd46b377cca59e59514e3a78b1e6f3b9b92b651)) ### Features * Enables semantic release for automated publishing ([b43a194](https://github.com/soranoo/next-css-obfuscator/commit/b43a19477962fe85e7bf26b890624998648be1ba)) * Upgrades to v3 with new CSS transformation ([0afd780](https://github.com/soranoo/next-css-obfuscator/commit/0afd7808b2c6405b5fbedb4b8ff0e34f813ccd82)) * v3 migration with TailwindCSS 4 support ([668a5f6](https://github.com/soranoo/next-css-obfuscator/commit/668a5f66abdd174bf8ecbd18e9872a9719c73508)) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8019bd4..f95d044 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next-css-obfuscator", - "version": "1.0.0-beta.1", + "version": "3.0.0-beta.3", "description": "A package deeply inspired by PostCSS-Obfuscator but for Next.js.", "main": "dist/index.js", "type": "commonjs", From 7ccc78c838c94c8ee5612475f55dc423884f0f2b Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Fri, 30 May 2025 17:18:32 +0100 Subject: [PATCH 23/32] chore: update dependencies; disable GitHub release integration --- package-lock.json | 102 ++++----------------------------------------- package.json | 4 +- release.config.cjs | 2 +- 3 files changed, 11 insertions(+), 97 deletions(-) diff --git a/package-lock.json b/package-lock.json index 472ef8a..5652366 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,18 @@ { "name": "next-css-obfuscator", - "version": "3.0.0-b1", + "version": "3.0.0-beta.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "next-css-obfuscator", - "version": "3.0.0-b1", + "version": "3.0.0-beta.3", "license": "MIT", "dependencies": { "@babel/generator": "^7.23.6", "@babel/parser": "^7.23.9", "@babel/traverse": "^7.23.9", - "css-parse": "^2.0.0", - "css-seasoning": "^1.4.1", + "css-seasoning": "^1.8.0", "html-escaper": "^3.0.3", "htmlparser2": "^10.0.0", "lightningcss-wasm": "^1.29.3", @@ -34,7 +33,6 @@ "@types/react": "^18.2.31", "@types/yargs": "^17.0.32", "@vitest/coverage-v8": "^3.0.5", - "prettier": "^3.2.4", "semantic-release": "^24.2.3", "tslib": "^2.6.2", "typescript": "^5.0.2", @@ -2125,17 +2123,6 @@ "node": ">=12" } }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2532,29 +2519,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/css": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", - "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", - "dependencies": { - "inherits": "^2.0.3", - "source-map": "^0.6.1", - "source-map-resolve": "^0.5.2", - "urix": "^0.1.0" - } - }, - "node_modules/css-parse": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", - "integrity": "sha512-UNIFik2RgSbiTwIW1IsFwXWn6vs+bYdq83LKTSOsx7NJR7WII9dxewkHLltfTLVppoUApHV0118a4RZRI9FLwA==", - "dependencies": { - "css": "^2.0.0" - } - }, "node_modules/css-seasoning": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/css-seasoning/-/css-seasoning-1.4.1.tgz", - "integrity": "sha512-m5X8Qu5JWvMJH7KkygW1EzC2Yv504s+rR0AC8VCpabWUdlvzaMDw0BxBgu7+pdreR9BccKDCGd/6QxMrtGs79g==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/css-seasoning/-/css-seasoning-1.8.0.tgz", + "integrity": "sha512-0H/rn7xayUOCIKw1W6jEQQYYhIhk7+y94M/ouMAkMSdQI+lVYWtBdsNn2kqkfkLlYshx/ATtt6WT31T0yYYMyQ==", "dependencies": { "@deno/shim-deno": "~0.18.0", "lightningcss-wasm": "^1.29.1", @@ -2586,14 +2554,6 @@ } } }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "engines": { - "node": ">=0.10" - } - }, "node_modules/deep-eql": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", @@ -3509,7 +3469,8 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/ini": { "version": "1.3.8", @@ -7298,21 +7259,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/prettier": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz", - "integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==", - "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, "node_modules/pretty-ms": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", @@ -7484,12 +7430,6 @@ "node": ">=8" } }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", - "deprecated": "https://github.com/lydell/resolve-url#deprecated" - }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -7816,6 +7756,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -7829,25 +7770,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "node_modules/source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "deprecated": "See https://github.com/lydell/source-map-url#deprecated" - }, "node_modules/spawn-error-forwarder": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", @@ -8327,12 +8249,6 @@ "node": ">= 10.0.0" } }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", - "deprecated": "Please see https://github.com/lydell/urix#deprecated" - }, "node_modules/url-join": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", diff --git a/package.json b/package.json index f95d044..63e278a 100644 --- a/package.json +++ b/package.json @@ -28,8 +28,7 @@ "@babel/generator": "^7.23.6", "@babel/parser": "^7.23.9", "@babel/traverse": "^7.23.9", - "css-parse": "^2.0.0", - "css-seasoning": "^1.4.1", + "css-seasoning": "^1.8.0", "html-escaper": "^3.0.3", "htmlparser2": "^10.0.0", "lightningcss-wasm": "^1.29.3", @@ -47,7 +46,6 @@ "@types/react": "^18.2.31", "@types/yargs": "^17.0.32", "@vitest/coverage-v8": "^3.0.5", - "prettier": "^3.2.4", "semantic-release": "^24.2.3", "tslib": "^2.6.2", "typescript": "^5.0.2", diff --git a/release.config.cjs b/release.config.cjs index 0a39050..0dc9519 100644 --- a/release.config.cjs +++ b/release.config.cjs @@ -12,6 +12,6 @@ module.exports = { assets: ["package.json", "CHANGELOG.md"], }, ], - "@semantic-release/github", + // "@semantic-release/github", ], }; From 32952bfdf678dbb71eb27fb60bc25fd55df66b48 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Fri, 30 May 2025 18:07:06 +0100 Subject: [PATCH 24/32] feat: support ident prefix/suffix options --- .github/workflows/release.yml | 5 +- README.md | 94 ++++++++++++++--------- docs/upgrade-to-v3.md | 6 +- src/config.ts | 10 ++- src/handlers/css.ts | 137 ++++++++++++++++++++-------------- src/index.ts | 69 +++++++++++------ src/types.ts | 11 ++- 7 files changed, 208 insertions(+), 124 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c97d178..377ba19 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,9 +28,10 @@ jobs: - name: Build NPM package run: npm run build - - name: Edit Inital Version + - name: Edit First Release Version run: sed -i 's/\( FIRST_RELEASE\) = "1.0.0"/\1 = "3.0.0"/' node_modules/semantic-release/lib/definitions/constants.js - - name: Edit Inital Version + + - name: Edit First Prerelease Version run: sed -i 's/\( FIRSTPRERELEASE\) = "1"/\1 = "3"/' node_modules/semantic-release/lib/definitions/constants.js - name: Create Release diff --git a/README.md b/README.md index 4729f1d..7fe27e1 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,7 @@ Project starts on 30-10-2023 Visit the [GitHub Page](https://github.com/soranoo/next-css-obfuscator/) for better reading experience and latest docs. 😎 ---- - +--- ### 🎉 Version 3 has NOW been released 🎉 (💥 Breaking Changes) @@ -26,41 +25,43 @@ Visit the [GitHub Page](https://github.com/soranoo/next-css-obfuscator/) for bet >[!TIP]\ > Don't upgrade to this version unless you are using TailwindCSS 4.0.0 or above. "If it works, don't touch it." :) - #### 📌 Feature Changes +#### 📌 Feature Changes - - Support TailwindCSS 4. - - Support nested CSS. - - Support CSS idents obfuscation. +- Support TailwindCSS 4. +- Support nested CSS. +- Support CSS idents obfuscation. - #### 📌 Configuration Changes +#### 📌 Configuration Changes - - `enableJsAst` option is now enabled by default. - - Default `generatorSeed` not longer fixed to `-1`, but a random string. - - `simplify-seedable` mode is not longer supported. Use `random` mode instead. - - Removed `includeAnyMatchRegexes` and `excludeAnyMatchRegexes` options, the `whiteListedFolderPaths` and `blackListedFolderPaths` options will be used instead. - - Deprecated `classLength` option, not longer supported. - - Added `ignorePatterns` option to ignore the class names and idents that match the regexes or strings. - - Not longer preserve TailwindCSS dark mode class names (ie `.dark`). Add the dark mode class name to the `ignorePatterns.selectors` option to preserve it. - - Merge `classIgnore` into `ignorePatterns.selectors` option. - - Renamed `classPrefix` and `classSuffix` to `prefix` and `suffix`. +- `enableJsAst` option is now enabled by default. +- Default `generatorSeed` not longer fixed to `-1`, but a random string. +- `simplify-seedable` mode is not longer supported. Use `random` mode instead. +- Removed `includeAnyMatchRegexes` and `excludeAnyMatchRegexes` options, the `whiteListedFolderPaths` and `blackListedFolderPaths` options will be used instead. +- Deprecated `classLength` option, not longer supported. +- Added `ignorePatterns` option to ignore the class names and idents that match the regexes or strings. +- Not longer preserve TailwindCSS dark mode class names (ie `.dark`). Add the dark mode class name to the `ignorePatterns.selectors` option to preserve it. +- Merge `classIgnore` into `ignorePatterns.selectors` option. +- Renamed `classPrefix` and `classSuffix` to `prefix` and `suffix`. ### 💥 Version 2 (Major Update) + This version is deeply inspired by [PostCSS-Obfuscator](https://github.com/n4j1Br4ch1D/postcss-obfuscator). Shout out to [n4j1Br4ch1D](https://github.com/n4j1Br4ch1D) for creating such a great package and thank you [tremor](https://github.com/tremorlabs) for sponsoring this project. - #### 📌 Changes - - Support basic partial obfuscation - - Support TailwindCSS Dark Mode - - New configuration file `next-css-obfuscator.config.cjs` - - More configuration options - - Now become a independent solution (no need to patch `PostCSS-Obfuscator` anymore) - - More tests - - Better CSS parsing +#### 📌 Changes + +- Support basic partial obfuscation +- Support TailwindCSS Dark Mode +- New configuration file `next-css-obfuscator.config.cjs` +- More configuration options +- Now become a independent solution (no need to patch `PostCSS-Obfuscator` anymore) +- More tests +- Better CSS parsing ### 📚 Migration Guides + - [Migrate from version 1.x to 2.x](docs/upgrade-to-v2.md) - [Migrate from version 2.x to 3.x](docs/upgrade-to-v3.md) - [version 1.x README](https://github.com/soranoo/next-css-obfuscator/tree/v.1.1.0) [version 2.x README](https://github.com/soranoo/next-css-obfuscator/tree/v.2.2.19) @@ -166,7 +167,8 @@ Visit the [npm](https://www.npmjs.com/package/next-css-obfuscator) page. 1. Create and add the following code to `next-css-obfuscator.config.cjs` or `next-css-obfuscator.config.ts`: - ##### Obfuscate all files + ##### Obfuscate all files + ```javascript module.exports = { enable: true, @@ -176,8 +178,9 @@ Visit the [npm](https://www.npmjs.com/package/next-css-obfuscator) page. }; ``` - ##### Partially obfuscate - + + ##### Partially obfuscate + > [!CAUTION]\ > Partially obfuscate can be EXTREMELY buggy. Be cautious when using this feature. @@ -193,7 +196,8 @@ Visit the [npm](https://www.npmjs.com/package/next-css-obfuscator) page. ``` - ##### TypeScript + ##### TypeScript + ```ts import { Options } from "next-css-obfuscator"; @@ -202,8 +206,6 @@ Visit the [npm](https://www.npmjs.com/package/next-css-obfuscator) page. } satisfies Options; ``` - - Feel free to checkout [📖 Config Options Reference](#-config-options-reference) for more options and details. > [!TIP]\ @@ -250,6 +252,7 @@ to make sure the build is always obfuscated and no need to run `obfuscate-build` > It is a good idea to add the `/css-obfuscator` folder to `.gitignore` to prevent the conversion table from being uploaded to the repository. #### Partially obfuscate + To partially obfuscate your project, you have to add the obfuscate marker class to the components you want to obfuscate. ```diff @@ -312,13 +315,15 @@ It may not be the best setting but it works for me. :) |buildFolderPath|string|"./.next"|The folder path to store the build files built by Next.js.| |classConversionJsonFolderPath|string|"./css-obfuscator"|The folder path to store the before obfuscate and after obfuscated classes conversion table.| |refreshClassConversionJson|boolean|false|Refresh the class conversion JSON file(s) at every obfuscation. Good for setting tweaking but not recommended for production.| -|prefix|string|""|The prefix of the obfuscated class and ident name.| -|suffix|string|""|The suffix of the obfuscated class and ident name.| +|prefix.selectors|string|""|The prefix of the obfuscated classname.| +|prefix.idents|string|""|The prefix of the obfuscated ident name.| +|suffix.selectors|string|""|The suffix of the obfuscated classname.| +|suffix.idents|string|""|The suffix of the obfuscated ident name.| |ignorePatterns|{selectors: [], idents: []}|{selectors: [], idents: []}|The patterns to be ignored during obfuscation.| |allowExtensions|string[ ]|[".jsx", ".tsx", ".js", ".ts", ".html", ".rsc"]|The file extensions to be processed.| |contentIgnoreRegexes|RegExp[ ]|[/\.jsxs\)\("\w+"/g]|The regexes to match the content to be ignored during obfuscation.| -|whiteListedFolderPaths|(string \| Regex)[ ]|[ ]|The folder paths/Regex to be processed. Empty array means all folders will be processed.| -|blackListedFolderPaths|(string \| Regex)[ ]|[ ]|The folder paths/Regex to be ignored.| +|whiteListedFolderPaths|[string \| Regex]( )|[ ]|The folder paths/Regex to be processed. Empty array means all folders will be processed.| +|blackListedFolderPaths|[string \| Regex]( )|[ ]|The folder paths/Regex to be ignored.| |enableMarkers|boolean|false|Enable or disable the obfuscation markers.| |markers|string[ ]|[ ]|Classes that indicate component(s) need to obfuscate.| |removeMarkersAfterObfuscated|boolean|true|Remove the obfuscation markers from HTML elements after obfuscation.| @@ -327,6 +332,7 @@ It may not be the best setting but it works for me. :) |logLevel|"debug" \| "info" \| "warn" \| "error" \| "success"| "info"|The log level.| ### Experimental Features Options 🚧 + | Option| Type| Default| Description| Stage | | - | - | - | - | - | |enableJsAst|boolean|true|Whether to obfuscate JS files using abstract syntax tree parser.

`contentIgnoreRegexes` option will be ignored if this option is enabled.|Alpha| @@ -338,12 +344,14 @@ It may not be the best setting but it works for me. :) > [!NOTE]\ > **Stages** - +> > 1. **PoC**: Proof of Concept. The feature is still in the concept stage and is not recommended in production. > 2. **Alpha**: The feature is still in the early stage of development and may not work as expected. > 3. **Beta**: The feature is almost completed and should work as expected but may have some issues. (if no issue is reported in a period, it will be considered stable.) > 4. **Stable**: The feature is in the final stage of development and should work as expected. ### All options in one place 📦 + ```js // next-css-obfuscator.config.cjs @@ -369,8 +377,14 @@ module.exports = { */ classSuffix: "", // Suffix of the obfuscated class name. - prefix: "", // Prefix of the obfuscated class and ident name. - suffix: "", // Suffix of the obfuscated class and ident name. + prefix: { + selectors: "", // Prefix of the obfuscated classname. + idents: "", // Prefix of the obfuscated ident name. + }, + suffix: { + selectors: "", // Suffix of the obfuscated classname. + idents: "", // Suffix of the obfuscated ident name. + }, /** * @deprecated Merged into `ignorePatterns.selectors` from v3.0.0 and will be removed in the next major version. @@ -428,6 +442,7 @@ Your conversion table may be messed up. Try to delete the `classConversionJsonFo In a normal situation, the package will only remove the original CSS that is related to the obfuscation and you should not see any CSS sharing the same declaration block. You are not expected to see this: + ```css /* example.css */ @@ -508,6 +523,7 @@ If you are going to obfuscate the whole site, you will get a way more accurate o Thank you to all the sponsors who support this project. #### Organizations (1) +
@@ -520,6 +536,7 @@ Thank you to all the sponsors who support this project.
#### Individuals (1) +
@@ -532,6 +549,7 @@ Thank you to all the sponsors who support this project.
## 🦾 Special Thanks +
@@ -550,9 +568,11 @@ Contributions are welcome! If you find a bug or have a feature request, please o ## 🏛️ Commercial Usage #### Individual🕺 + Are you using this package for a personal project? That's great! You can support us by starring this repo on Github ⭐🌟⭐. #### Organization 👯‍♂️ + Are you using this package within your organization and generating revenue from it? Fantastic! We depend on your support to continue developing and maintaining the package under an MIT License. You might consider showing your support through [Github Sponsors](https://github.com/sponsors/soranoo). ## 📝 License diff --git a/docs/upgrade-to-v3.md b/docs/upgrade-to-v3.md index a57c426..7dad9ba 100644 --- a/docs/upgrade-to-v3.md +++ b/docs/upgrade-to-v3.md @@ -11,8 +11,10 @@ The following table outlines the changes to the configuration options from versi | `generatorSeed` (default: -1) | `generatorSeed` (default: {random}) | Default seed is now a random string. Provide a fixed string if you need consistent output across builds (e.g., for CDN caching). | | `mode: "simplify-seedable"` | ⛔ (Removed) | Use `mode: "random"` with a fixed `generatorSeed` instead. | | `classLength` | ⛔ (Deprecated) | No longer supported. Will be removed in the next major version. | -| `classPrefix` | `prefix` | Renamed for clarity, now applies to both selectors and idents. `classPrefix` will be removed in the next major version. | -| `classSuffix` | `suffix` | Renamed for clarity, now applies to both selectors and idents. `classSuffix` will be removed in the next major version. | +| `classPrefix` | `prefix.selectors` | Renamed for clarity, now applies to both selectors and idents. `classPrefix` will be removed in the next major version. | +| ➡️ | `prefix.idents` | New option to add specific prefix idents| +| `classSuffix` | `suffix.selectors` | Renamed for clarity, now applies to both selectors and idents. `classSuffix` will be removed in the next major version. | +| ➡️ | `suffix.idents` | New option to add specific suffix idents| | `classIgnore` | `ignorePatterns.selectors` | Merged into the new `ignorePatterns` object. `classIgnore` will be removed in the next major version. | | ➡️ | `ignorePatterns.idents` | New option to ignore specific CSS idents| | `includeAnyMatchRegexes` | ⛔ (Removed) | Use `whiteListedFolderPaths` instead. | diff --git a/src/config.ts b/src/config.ts index 251c929..c6e7208 100644 --- a/src/config.ts +++ b/src/config.ts @@ -22,8 +22,14 @@ export const defaultOptions: Options = { */ classSuffix: "", // Suffix of the obfuscated class name. - prefix: "", // Prefix of the obfuscated class and ident name. - suffix: "", // Suffix of the obfuscated class and ident name. + prefix: { + selectors: "", // Prefix of the obfuscated classname. + idents: "", // Prefix of the obfuscated ident name. + }, + suffix: { + selectors: "", // Suffix of the obfuscated classname. + idents: "", // Suffix of the obfuscated ident name. + }, /** * @deprecated Merged into `ignorePatterns.selectors` from v3.0.0 and will be removed in the next major version. diff --git a/src/handlers/css.ts b/src/handlers/css.ts index 915ef39..b9f06b3 100644 --- a/src/handlers/css.ts +++ b/src/handlers/css.ts @@ -1,22 +1,27 @@ +import type { + ConversionTables, + PrefixSuffixOptions, + TransformProps, +} from "css-seasoning"; import type { obfuscateMode } from "../types"; -import type { ConversionTables, TransformProps } from "css-seasoning"; -import path from "node:path"; import fs from "node:fs"; +import path from "node:path"; import { initTransform, transform } from "css-seasoning"; -import lightningcssInit, { transform as lightningcssTransform } from "lightningcss-wasm"; +import lightningcssInit, { + transform as lightningcssTransform, +} from "lightningcss-wasm"; // TODO: html failed with . import { - log, findAllFilesWithExt, getFilenameFromPath, - stringToNumber, loadConversionTables, + log, + stringToNumber, } from "../utils"; - const obfuscateCss = ({ cssPath, removeOriginalCss, @@ -30,17 +35,17 @@ const obfuscateCss = ({ ignorePatterns, generatorSeed = Math.random().toString().slice(2, 10), // take 8 digits from the random number }: { - cssPath: string, - removeOriginalCss?: boolean, - isFullObfuscation?: boolean, - outCssPath?: string, - conversionTables?: ConversionTables, - - mode?: obfuscateMode, - prefix?: string, - suffix?: string, - ignorePatterns?: TransformProps["ignorePatterns"], - generatorSeed?: string, + cssPath: string; + removeOriginalCss?: boolean; + isFullObfuscation?: boolean; + outCssPath?: string; + conversionTables?: ConversionTables; + + mode?: obfuscateMode; + prefix?: PrefixSuffixOptions; + suffix?: PrefixSuffixOptions; + ignorePatterns?: TransformProps["ignorePatterns"]; + generatorSeed?: string; }) => { if (!outCssPath) { // If no output path is provided, use the input path @@ -52,11 +57,16 @@ const obfuscateCss = ({ const cssContent = fs.readFileSync(cssPath, "utf-8"); - let transformerMode: TransformProps["mode"] = mode === "simplify" ? "minimal" : "hash"; + let transformerMode: TransformProps["mode"] = + mode === "simplify" ? "minimal" : "hash"; if (!transformerMode) { // @ts-expect-error - "simplify-seedable" is deprecated but for backward compatibility if (mode === "simplify-seedable") { - log("warn", "CSS obfuscation", "The 'simplify-seedable' mode is deprecated, please use 'random' or 'simplify' instead. Now will fall back to 'random' mode."); + log( + "warn", + "CSS obfuscation", + "The 'simplify-seedable' mode is deprecated, please use 'random' or 'simplify' instead. Now will fall back to 'random' mode.", + ); transformerMode = "hash"; } log("error", "CSS obfuscation", `Invalid mode: ${mode}`); @@ -64,15 +74,16 @@ const obfuscateCss = ({ } let finCss = ""; - const { css: obfuscatedCss, conversionTables: newConversionTables } = transform({ - css: cssContent, - conversionTables: conversionTables, - mode: transformerMode, - prefix: prefix, - suffix: suffix, - seed: stringToNumber(generatorSeed), - ignorePatterns: ignorePatterns, - }); + const { css: obfuscatedCss, conversionTables: newConversionTables } = + transform({ + css: cssContent, + conversionTables: conversionTables, + mode: transformerMode, + prefix: prefix, + suffix: suffix, + seed: stringToNumber(generatorSeed), + ignorePatterns: ignorePatterns, + }); if (removeOriginalCss) { finCss = obfuscatedCss; @@ -88,28 +99,45 @@ const obfuscateCss = ({ finCss = new TextDecoder().decode(code); } - const totalConversion = Object.keys(newConversionTables.selectors).length + Object.keys(newConversionTables.idents).length; + const totalConversion = + Object.keys(newConversionTables.selectors).length + + Object.keys(newConversionTables.idents).length; if (removeOriginalCss) { - log("info", "CSS rules:", `Modified ${totalConversion} CSS rules to ${getFilenameFromPath(cssPath)}`); + log( + "info", + "CSS rules:", + `Modified ${totalConversion} CSS rules to ${getFilenameFromPath(cssPath)}`, + ); } else { - const oldTotalConversion = conversionTables ? Object.keys(conversionTables.selectors).length + Object.keys(conversionTables.idents).length : 0; - log("info", "CSS rules:", `Added ${totalConversion - oldTotalConversion} new CSS rules to ${getFilenameFromPath(cssPath)}`); + const oldTotalConversion = conversionTables + ? Object.keys(conversionTables.selectors).length + + Object.keys(conversionTables.idents).length + : 0; + log( + "info", + "CSS rules:", + `Added ${totalConversion - oldTotalConversion} new CSS rules to ${getFilenameFromPath(cssPath)}`, + ); } // Save the obfuscated CSS to the output path const sizeBefore = Buffer.byteLength(cssContent, "utf8"); - fs.writeFileSync(outCssPath, obfuscatedCss); - const sizeAfter = Buffer.byteLength(obfuscatedCss, "utf8"); - const percentChange = Math.round(((sizeAfter) / sizeBefore) * 100); - log("success", "CSS obfuscated:", `Size from ${sizeBefore} to ${sizeAfter} bytes (${percentChange}%) in ${getFilenameFromPath(cssPath)}`); + fs.writeFileSync(outCssPath, finCss); + const sizeAfter = Buffer.byteLength(finCss, "utf8"); + const percentChange = Math.round((sizeAfter / sizeBefore) * 100); + log( + "success", + "CSS obfuscated:", + `Size from ${sizeBefore} to ${sizeAfter} bytes (${percentChange}%) in ${getFilenameFromPath(cssPath)}`, + ); return { conversionTables: newConversionTables, - } -} + }; +}; /** - * + * */ export const obfuscateCssFiles = async ({ selectorConversionJsonFolderPath, @@ -123,22 +151,19 @@ export const obfuscateCssFiles = async ({ generatorSeed = new Date().getTime().toString(), removeOriginalCss = false, }: { - selectorConversionJsonFolderPath: string, - buildFolderPath: string, - whiteListedFolderPaths: (string | RegExp)[], - blackListedFolderPaths: (string | RegExp)[], - mode?: obfuscateMode, - prefix?: string, - suffix?: string, - ignorePatterns?: TransformProps["ignorePatterns"], - generatorSeed?: string, - removeOriginalCss?: boolean, + selectorConversionJsonFolderPath: string; + buildFolderPath: string; + whiteListedFolderPaths: (string | RegExp)[]; + blackListedFolderPaths: (string | RegExp)[]; + mode?: obfuscateMode; + prefix?: PrefixSuffixOptions; + suffix?: PrefixSuffixOptions; + ignorePatterns?: TransformProps["ignorePatterns"]; + generatorSeed?: string; + removeOriginalCss?: boolean; }) => { // Initialize nessesary modules - await Promise.all([ - initTransform(), - lightningcssInit(), - ]); + await Promise.all([initTransform(), lightningcssInit()]); // Create the selector conversion JSON folder if it doesn't exist if (!fs.existsSync(selectorConversionJsonFolderPath)) { @@ -146,7 +171,9 @@ export const obfuscateCssFiles = async ({ } // Load and merge all JSON files in the selector conversion folder - const conversionTables = loadConversionTables(selectorConversionJsonFolderPath); + const conversionTables = loadConversionTables( + selectorConversionJsonFolderPath, + ); // Get all CSS files using the unified path filtering function const cssPaths = findAllFilesWithExt(".css", buildFolderPath, { @@ -190,4 +217,4 @@ export const obfuscateCssFiles = async ({ return { conversionTables: tables, }; -} +}; diff --git a/src/index.ts b/src/index.ts index 04252c7..d6b9528 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,17 +1,17 @@ -import type { Options, OptionalOptions } from "./types"; +import type { OptionalOptions, Options } from "./types"; import fs from "node:fs"; import path from "node:path"; import yargs from "yargs"; +import Config from "./config"; +import { obfuscateCssFiles } from "./handlers/css"; import { + findAllFilesWithExt, + getFilenameFromPath, log, replaceJsonKeysInFiles, setLogLevel, - findAllFilesWithExt, - getFilenameFromPath, } from "./utils"; -import { obfuscateCssFiles } from "./handlers/css"; -import Config from "./config"; const obfuscate = async (options: Options) => { setLogLevel(options.logLevel); @@ -21,8 +21,14 @@ const obfuscate = async (options: Options) => { return; } - const classConversionJsonPaths = findAllFilesWithExt(".json", options.classConversionJsonFolderPath); - if (options.refreshClassConversionJson && classConversionJsonPaths.length > 0) { + const classConversionJsonPaths = findAllFilesWithExt( + ".json", + options.classConversionJsonFolderPath, + ); + if ( + options.refreshClassConversionJson && + classConversionJsonPaths.length > 0 + ) { log("info", "Obfuscation", "Refreshing class conversion JSON"); for (const jsonPath of classConversionJsonPaths) { fs.unlinkSync(jsonPath); @@ -32,6 +38,20 @@ const obfuscate = async (options: Options) => { log("info", "Obfuscation", "Creating/Updating class conversion JSON"); + // Reconstruct ignore patterns for backward compatibility + // TODO: Remove in the next major version + let ignorePatterns = options.ignorePatterns; + if (options.classIgnore) { + if (ignorePatterns && !Array.isArray(ignorePatterns)) { + ignorePatterns.selectors?.push(...options.classIgnore); + } else { + ignorePatterns = { + selectors: [...(ignorePatterns || []), ...options.classIgnore], + idents: [...(ignorePatterns || [])], + }; + } + } + // Create conversion tables and obfuscate CSS files const { conversionTables } = await obfuscateCssFiles({ selectorConversionJsonFolderPath: options.classConversionJsonFolderPath, @@ -40,25 +60,27 @@ const obfuscate = async (options: Options) => { blackListedFolderPaths: options.blackListedFolderPaths, mode: options.mode, - prefix: options.prefix || options.classPrefix, - suffix: options.suffix || options.classSuffix, - ignorePatterns: { - ...options.ignorePatterns, - selectors: [ - ...options.ignorePatterns?.selectors || [], - ...(options.classIgnore || []), - ] - }, + prefix: options.prefix || { selectors: options.classPrefix }, + suffix: options.suffix || { selectors: options.classSuffix }, + ignorePatterns: ignorePatterns, generatorSeed: options.generatorSeed, removeOriginalCss: options.removeOriginalCss, }); // Save the conversion table to a JSON file - const jsonPath = path.join(process.cwd(), options.classConversionJsonFolderPath, "conversion.json"); + const jsonPath = path.join( + process.cwd(), + options.classConversionJsonFolderPath, + "conversion.json", + ); console.log({ jsonPath }); fs.writeFileSync(jsonPath, JSON.stringify(conversionTables, null, 2)); - log("success", "CSS obfuscation:", `Saved conversion table to ${getFilenameFromPath(jsonPath)}`); + log( + "success", + "CSS obfuscation:", + `Saved conversion table to ${getFilenameFromPath(jsonPath)}`, + ); // Use the same unified paths for replacing JSON keys in files replaceJsonKeysInFiles({ @@ -72,17 +94,18 @@ const obfuscate = async (options: Options) => { blackListedFolderPaths: options.blackListedFolderPaths, enableObfuscateMarkerClasses: options.enableMarkers, obfuscateMarkerClasses: options.markers, - removeObfuscateMarkerClassesAfterObfuscated: options.removeMarkersAfterObfuscated, + removeObfuscateMarkerClassesAfterObfuscated: + options.removeMarkersAfterObfuscated, enableJsAst: options.enableJsAst, }); -} +}; export const obfuscateCli = async () => { const argv = yargs.option("config", { alias: "c", type: "string", - description: "Path to the config file" + description: "Path to the config file", }).argv; let configPath: string | undefined = undefined; @@ -111,6 +134,6 @@ export const obfuscateCli = async () => { const config = new Config(configPath ? require(configPath) : undefined).get(); await obfuscate(config); log("success", "Obfuscation", "Completed~"); -} +}; -export { type OptionalOptions as Options }; +export type { OptionalOptions as Options }; diff --git a/src/types.ts b/src/types.ts index acdafcf..6a58238 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,8 @@ -import type { ConversionTables, TransformProps } from "css-seasoning"; +import type { + ConversionTables, + PrefixSuffixOptions, + TransformProps, +} from "css-seasoning"; export type LogLevel = "debug" | "info" | "warn" | "error" | "success"; export type obfuscateMode = "random" | "simplify"; @@ -24,8 +28,9 @@ export type Options = { * @deprecated Merged into `suffix` from v3.0.0 and will be removed in the next major version. */ classSuffix: string; - prefix: string; - suffix: string; + + prefix: PrefixSuffixOptions; + suffix: PrefixSuffixOptions; /** * @deprecated Merged into `ignorePatterns.selectors` from v3.0.0 and will be removed in the next major version. From b26b6cddd9245391eb6b50e1f19c0c24e93019b4 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Fri, 30 May 2025 18:19:42 +0100 Subject: [PATCH 25/32] minor: formatting --- src/__tests__/html.test.ts | 100 ++++++--- src/__tests__/js-ast.test.ts | 404 +++++++++++++++++++++-------------- src/__tests__/js.test.ts | 18 +- src/__tests__/utils.test.ts | 22 +- src/handlers/html.ts | 266 +++++++++++------------ src/handlers/js-ast.ts | 95 ++++---- src/handlers/js.ts | 134 +++++++++--- src/utils.ts | 287 ++++++++++++++++--------- 8 files changed, 798 insertions(+), 528 deletions(-) diff --git a/src/__tests__/html.test.ts b/src/__tests__/html.test.ts index 80bf098..242fce5 100644 --- a/src/__tests__/html.test.ts +++ b/src/__tests__/html.test.ts @@ -1,17 +1,14 @@ -import type { SelectorConversion } from '../types'; +import type { SelectorConversion } from "../types"; -import { describe, it, expect } from "vitest"; +import { describe, expect, it } from "vitest"; -import { - obfuscateHtmlClassNames, -} from "../handlers/html"; +import { obfuscateHtmlClassNames } from "../handlers/html"; //! ================================ //! obfuscateHtmlClassNames //! ================================ describe("obfuscateHtmlClassNames", () => { - it("should obfuscate class names correctly", () => { // Arrange const html = `
`; @@ -32,37 +29,61 @@ describe("obfuscateHtmlClassNames", () => { const keyClass = "key"; // Act - const result = obfuscateHtmlClassNames({ html, selectorConversion, obfuscateMarkerClass: keyClass }); + const result = obfuscateHtmlClassNames({ + html, + selectorConversion, + obfuscateMarkerClass: keyClass, + }); // Assert - expect(result.obfuscatedContent).toEqual(`
`); + expect(result.obfuscatedContent).toEqual( + `
`, + ); expect(result.usedKeys).to.deep.equal(["\\.foo"]); }); it("should not obfuscate class names outside of obfuscate class scope", () => { // Arrange const html = `
`; - const selectorConversion: SelectorConversion = { "\\.foo": "\\.a", "\\.bar": "\\.b" }; + const selectorConversion: SelectorConversion = { + "\\.foo": "\\.a", + "\\.bar": "\\.b", + }; const keyClass = "key"; // Act - const result = obfuscateHtmlClassNames({ html, selectorConversion, obfuscateMarkerClass: keyClass }); + const result = obfuscateHtmlClassNames({ + html, + selectorConversion, + obfuscateMarkerClass: keyClass, + }); // Assert - expect(result.obfuscatedContent).toEqual(`
`); + expect(result.obfuscatedContent).toEqual( + `
`, + ); expect(result.usedKeys).to.deep.equal([]); }); it("should handle script tags", () => { // Arrange const html = ``; - const selectorConversion: SelectorConversion = { "\\.fol": "\\.a", "\\.foo": "\\.b" }; + const selectorConversion: SelectorConversion = { + "\\.fol": "\\.a", + "\\.foo": "\\.b", + }; // Act - const result = obfuscateHtmlClassNames({ html, selectorConversion, obfuscateMarkerClass: "" }); + const result = obfuscateHtmlClassNames({ + html, + selectorConversion, + obfuscateMarkerClass: "", + }); // Assert - expect(result.obfuscatedContent).toEqual(``); + expect(result.obfuscatedContent).toEqual( + ``, + ); expect(result.usedKeys).to.deep.equal(["\\.fol", "\\.foo"]); }); @@ -79,7 +100,6 @@ describe("obfuscateHtmlClassNames", () => { expect(result.usedKeys).to.deep.equal(["\\.foo"]); }); - it("should handle comments", () => { // Arrange const html = `
`; @@ -89,7 +109,9 @@ describe("obfuscateHtmlClassNames", () => { const result = obfuscateHtmlClassNames({ html, selectorConversion }); // Assert - expect(result.obfuscatedContent).toEqual(`
`); + expect(result.obfuscatedContent).toEqual( + `
`, + ); expect(result.usedKeys).to.deep.equal(["\\.foo"]); }); @@ -122,7 +144,11 @@ describe("obfuscateHtmlClassNames", () => { it("should handle HTML with multiple classes in one element", () => { // Arrange const html = `
`; - const selectorConversion: SelectorConversion = { "\\.foo": "\\.a", "\\.bar": "\\.b", "\\.baz": "\\.c" }; + const selectorConversion: SelectorConversion = { + "\\.foo": "\\.a", + "\\.bar": "\\.b", + "\\.baz": "\\.c", + }; // Act const result = obfuscateHtmlClassNames({ html, selectorConversion }); @@ -135,13 +161,19 @@ describe("obfuscateHtmlClassNames", () => { it("should handle HTML with nested structures and multiple classes", () => { // Arrange const html = `
`; - const selectorConversion: SelectorConversion = { "\\.foo": "\\.a", "\\.bar": "\\.b", "\\.baz": "\\.c" }; + const selectorConversion: SelectorConversion = { + "\\.foo": "\\.a", + "\\.bar": "\\.b", + "\\.baz": "\\.c", + }; // Act const result = obfuscateHtmlClassNames({ html, selectorConversion }); // Assert - expect(result.obfuscatedContent).toEqual(`
`); + expect(result.obfuscatedContent).toEqual( + `
`, + ); expect(result.usedKeys).to.deep.equal(["\\.foo", "\\.bar", "\\.baz"]); }); @@ -152,21 +184,35 @@ describe("obfuscateHtmlClassNames", () => { const obfuscateMarkerClass = "key"; // Act - const result = obfuscateHtmlClassNames({ html, selectorConversion, obfuscateMarkerClass }); + const result = obfuscateHtmlClassNames({ + html, + selectorConversion, + obfuscateMarkerClass, + }); // Assert - expect(result.obfuscatedContent).toEqual(`
`); + expect(result.obfuscatedContent).toEqual( + `
`, + ); expect(result.usedKeys).to.deep.equal(["\\.foo"]); }); it("should handle HTML with multiple classes and obfuscate marker class", () => { // Arrange const html = `
`; - const selectorConversion: SelectorConversion = { "\\.foo": "\\.a", "\\.bar": "\\.b", "\\.baz": "\\.c" }; + const selectorConversion: SelectorConversion = { + "\\.foo": "\\.a", + "\\.bar": "\\.b", + "\\.baz": "\\.c", + }; const obfuscateMarkerClass = "key"; // Act - const result = obfuscateHtmlClassNames({ html, selectorConversion, obfuscateMarkerClass }); + const result = obfuscateHtmlClassNames({ + html, + selectorConversion, + obfuscateMarkerClass, + }); // Assert expect(result.obfuscatedContent).toEqual(`
`); @@ -182,7 +228,9 @@ describe("obfuscateHtmlClassNames", () => { const result = obfuscateHtmlClassNames({ html, selectorConversion }); // Assert - expect(result.obfuscatedContent).toEqual(`
`); + expect(result.obfuscatedContent).toEqual( + `
`, + ); expect(result.usedKeys).to.deep.equal(["\\.foo"]); }); @@ -198,7 +246,9 @@ describe("obfuscateHtmlClassNames", () => { const result = obfuscateHtmlClassNames({ html, selectorConversion }); // Assert - expect(result.obfuscatedContent).toEqual(`
`); + expect(result.obfuscatedContent).toEqual( + `
`, + ); expect(result.usedKeys).to.deep.equal(["\\.foo"]); }); }); diff --git a/src/__tests__/js-ast.test.ts b/src/__tests__/js-ast.test.ts index a4470b9..2465d53 100644 --- a/src/__tests__/js-ast.test.ts +++ b/src/__tests__/js-ast.test.ts @@ -1,13 +1,13 @@ -import { describe, it, expect } from "vitest"; +import generator from "@babel/generator"; +import * as parser from "@babel/parser"; import traverse, { type NodePath } from "@babel/traverse"; import type * as t from "@babel/types"; -import * as parser from "@babel/parser"; -import generator from "@babel/generator"; -import { searchStringLiterals, obfuscateJsWithAst } from "../handlers/js-ast"; +import { describe, expect, it } from "vitest"; +import { obfuscateJsWithAst, searchStringLiterals } from "../handlers/js-ast"; const stripCode = (code: string) => { return code.replace(/\s/g, ""); -} +}; //! ================================ //! searchStringLiterals @@ -19,7 +19,7 @@ describe("searchStringLiterals", () => { traverse(ast, { FunctionDeclaration(path) { if (path.node.id?.name === "startPoint") { - startPointNode = path.get('body'); + startPointNode = path.get("body"); } }, }); @@ -37,26 +37,28 @@ describe("searchStringLiterals", () => { function startPoint() { return "function"; } - ` + `; const expectedCode = ` const a = "test"; function startPoint() { return "{{found}}"; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["function"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should not handle string literals in comments", () => { @@ -66,25 +68,27 @@ describe("searchStringLiterals", () => { function startPoint() { return "function"; } - ` + `; const expectedCode = ` // const a = "test"; function startPoint() { return "{{found}}"; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["function"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); //? ******************************* @@ -103,7 +107,7 @@ describe("searchStringLiterals", () => { var f = "test6"; return a + b + c + d + e + f; } - ` + `; const expectedCode = ` const a = "{{found}}"; let b = "{{found}}"; @@ -115,18 +119,20 @@ describe("searchStringLiterals", () => { var f = "{{found}}"; return a + b + c + d + e + f; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["test", "test2", "test3", "test4", "test5", "test6"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle variable declarations with multiple variables correctly", () => { @@ -136,25 +142,27 @@ describe("searchStringLiterals", () => { function startPoint() { return a + b; } - ` + `; const expectedCode = ` const a = "{{found}}", b = "{{found}}"; function startPoint() { return a + b; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["test", "test2"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); //? ******************************* @@ -169,7 +177,7 @@ describe("searchStringLiterals", () => { const localVar = "world"; return "hello" + localVar; } - ` + `; const expectedCode = ` const globalVar = "out"; @@ -177,19 +185,21 @@ describe("searchStringLiterals", () => { const localVar = "{{found}}"; return "{{found}}" + localVar; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["hello", "world"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle global variables correctly", () => { @@ -200,7 +210,7 @@ describe("searchStringLiterals", () => { const localVar = "local"; return "hello" + globalVar; } - ` + `; const expectedCode = ` const globalVar = "{{found}}"; @@ -208,19 +218,21 @@ describe("searchStringLiterals", () => { const localVar = "local"; return "{{found}}" + globalVar; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["hello", "world"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); //? ******************************* @@ -236,7 +248,7 @@ describe("searchStringLiterals", () => { const localVar = "again"; return "hello" + (localBool ? globalVar : localVar); } - ` + `; const expectedCode = ` const globalVar = "{{found}}"; @@ -245,19 +257,21 @@ describe("searchStringLiterals", () => { const localVar = "{{found}}"; return "{{found}}" + (localBool ? globalVar : localVar); } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["hello", "world", "again"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle if statements correctly", () => { @@ -272,7 +286,7 @@ describe("searchStringLiterals", () => { } return "hello" + localVar; } - ` + `; const expectedCode = ` const globalVar = "{{found}}"; @@ -284,19 +298,21 @@ describe("searchStringLiterals", () => { } return "{{found}}" + localVar; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["hello", "world", "hello", "again"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); //? ******************************* @@ -314,7 +330,7 @@ describe("searchStringLiterals", () => { function startPoint() { return "hello" + globalVar + call(); } - ` + `; const expectedCode = ` const globalVar = "{{found}}"; @@ -325,19 +341,21 @@ describe("searchStringLiterals", () => { function startPoint() { return "{{found}}" + globalVar + call(); } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["hello", "global", "world"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle function calls with arguments correctly", () => { @@ -351,7 +369,7 @@ describe("searchStringLiterals", () => { function startPoint() { return "hello" + globalVar + call("world"); } - ` + `; const expectedCode = ` const globalVar = "{{found}}"; @@ -362,19 +380,21 @@ describe("searchStringLiterals", () => { function startPoint() { return "{{found}}" + globalVar + call("{{found}}"); } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["hello", "global", "world"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle function calls with multiple arguments correctly", () => { @@ -388,7 +408,7 @@ describe("searchStringLiterals", () => { function startPoint() { return "hello" + globalVar + call("world", "again"); } - ` + `; const expectedCode = ` const globalVar = "{{found}}"; @@ -399,19 +419,21 @@ describe("searchStringLiterals", () => { function startPoint() { return "{{found}}" + globalVar + call("{{found}}", "{{found}}"); } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["hello", "global", "world", "again"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle function calls with nested function calls correctly", () => { @@ -429,7 +451,7 @@ describe("searchStringLiterals", () => { function startPoint() { return "hello" + globalVar + call(call2("world"), "again"); } - ` + `; const expectedCode = ` const globalVar = "{{found}}"; @@ -444,19 +466,21 @@ describe("searchStringLiterals", () => { function startPoint() { return "{{found}}" + globalVar + call(call2("{{found}}"), "{{found}}"); } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["hello", "global", "world", "again"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle function calls with boolean expressions correctly", () => { @@ -475,7 +499,7 @@ describe("searchStringLiterals", () => { const bool = true; return "hello" + globalVar + call(bool ? "world1" : call2("world2"), "again"); } - ` + `; const expectedCode = ` const globalVar = "{{found}}"; @@ -491,19 +515,21 @@ describe("searchStringLiterals", () => { const bool = true; return "{{found}}" + globalVar + call(bool ? "{{found}}" : call2("{{found}}"), "{{found}}"); } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["hello", "global", "world1", "world2", "again"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle arrow function calls correctly", () => { @@ -517,7 +543,7 @@ describe("searchStringLiterals", () => { function startPoint() { return "hello" + globalVar + call("world", "again"); } - ` + `; const expectedCode = ` const globalVar = "{{found}}"; @@ -528,19 +554,21 @@ describe("searchStringLiterals", () => { function startPoint() { return "{{found}}" + globalVar + call("{{found}}", "{{found}}"); } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["hello", "global", "world", "again"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); //? ******************************* @@ -552,24 +580,26 @@ describe("searchStringLiterals", () => { function startPoint() { return document.getElementById("element"); } - ` + `; const expectedCode = ` function startPoint() { return document.getElementById("{{found}}"); } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["element"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle document.{method} with variables correctly", () => { @@ -578,25 +608,27 @@ describe("searchStringLiterals", () => { const id = "element"; return document.getElementById(id); } - ` + `; const expectedCode = ` function startPoint() { const id = "{{found}}"; return document.getElementById(id); } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["element"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); //? ******************************* @@ -611,7 +643,7 @@ describe("searchStringLiterals", () => { }; return obj.key; } - ` + `; const expectedCode = ` function startPoint() { const obj = { @@ -619,19 +651,21 @@ describe("searchStringLiterals", () => { }; return obj.key; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["value"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle object expressions' operations correctly", () => { @@ -644,7 +678,7 @@ describe("searchStringLiterals", () => { obj["key2"] = "another2"; return obj.key; } - ` + `; const expectedCode = ` function startPoint() { const obj = { @@ -654,19 +688,21 @@ describe("searchStringLiterals", () => { obj["key2"] = "{{found}}"; return obj.key; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["another", "another2", "value"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle object expressions with variables correctly", () => { @@ -678,7 +714,7 @@ describe("searchStringLiterals", () => { }; return obj.key; } - ` + `; const expectedCode = ` function startPoint() { const value = "{{found}}"; @@ -687,19 +723,21 @@ describe("searchStringLiterals", () => { }; return obj.key; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["value"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle object expressions with function calls correctly", () => { @@ -714,7 +752,7 @@ describe("searchStringLiterals", () => { }; return obj.key; } - ` + `; const expectedCode = ` function call() { return "{{found}}"; @@ -726,19 +764,21 @@ describe("searchStringLiterals", () => { }; return obj.key; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["call"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle object expressions with function calls with arguments correctly", () => { @@ -753,7 +793,7 @@ describe("searchStringLiterals", () => { }; return obj.key; } - ` + `; const expectedCode = ` function call(arg) { return arg; @@ -765,19 +805,21 @@ describe("searchStringLiterals", () => { }; return obj.key; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["call"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle object expressions with function calls with boolean expressions correctly", () => { @@ -794,7 +836,7 @@ describe("searchStringLiterals", () => { }; return obj.key; } - ` + `; const expectedCode = ` function call(arg) { return arg; @@ -808,19 +850,21 @@ describe("searchStringLiterals", () => { }; return obj.key; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["value", "another"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle object expressions with a variable key correctly", () => { @@ -837,7 +881,7 @@ describe("searchStringLiterals", () => { }; return obj.key; } - ` + `; const expectedCode = ` function call(arg) { return arg; @@ -851,19 +895,21 @@ describe("searchStringLiterals", () => { }; return obj.key; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["value", "another", "{{found}}"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); //? ******************************* @@ -876,25 +922,27 @@ describe("searchStringLiterals", () => { 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); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["element_1", "element_2"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle array concatenation correctly and no duplicates node should be scaned", () => { @@ -906,7 +954,7 @@ describe("searchStringLiterals", () => { const arr4 = [...arr, ...arr3]; return arr4; } - ` + `; const expectedCode = ` function startPoint() { const arr = ["{{found}}"]; @@ -915,19 +963,21 @@ describe("searchStringLiterals", () => { const arr4 = [...arr, ...arr3]; return arr4; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["element_1", "element_2"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle array expressions with variables correctly", () => { @@ -938,7 +988,7 @@ describe("searchStringLiterals", () => { const arr = [element1, element2]; return arr[0]; } - ` + `; const expectedCode = ` function startPoint() { const element1 = "{{found}}"; @@ -946,19 +996,21 @@ describe("searchStringLiterals", () => { const arr = [element1, element2]; return arr[0]; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["element_1", "element_2"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle array expressions with function calls correctly", () => { @@ -971,7 +1023,7 @@ describe("searchStringLiterals", () => { const arr = [call(), "element_2"]; return arr[0]; } - ` + `; const expectedCode = ` function call() { return "{{found}}"; @@ -981,19 +1033,21 @@ describe("searchStringLiterals", () => { const arr = [call(), "{{found}}"]; return arr[0]; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["element_1", "element_2"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle array expressions with function calls with arguments correctly", () => { @@ -1006,7 +1060,7 @@ describe("searchStringLiterals", () => { const arr = [call("element_1"), "element_2"]; return arr[0]; } - ` + `; const expectedCode = ` function call(arg) { return arg; @@ -1016,19 +1070,21 @@ describe("searchStringLiterals", () => { const arr = [call("{{found}}"), "{{found}}"]; return arr[0]; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["element_1", "element_2"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle array expressions with function calls with boolean expressions correctly", () => { @@ -1043,7 +1099,7 @@ describe("searchStringLiterals", () => { const arr = [call(bool ? key : "another"), "element_2"]; return arr[0]; } - ` + `; const expectedCode = ` function call(arg) { return arg; @@ -1055,19 +1111,21 @@ describe("searchStringLiterals", () => { const arr = [call(bool ? key : "{{found}}"), "{{found}}"]; return arr[0]; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["value", "another", "element_2"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); //? ******************************* @@ -1091,7 +1149,7 @@ describe("searchStringLiterals", () => { return "default"; } } - ` + `; const expectedCode = ` function startPoint() { const key = "value"; @@ -1108,19 +1166,21 @@ describe("searchStringLiterals", () => { return "{{found}}"; } } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["another", "default"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); //? ******************************* @@ -1139,7 +1199,7 @@ describe("searchStringLiterals", () => { } return result; } - ` + `; const expectedCode = ` function startPoint() { const arr = ["{{found}}", "{{found}}"]; @@ -1151,19 +1211,21 @@ describe("searchStringLiterals", () => { } return result; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["element_1", "element_2", "result"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle foreach statements correctly", () => { @@ -1177,7 +1239,7 @@ describe("searchStringLiterals", () => { }); return result; } - ` + `; const expectedCode = ` function startPoint() { const arr = ["{{found}}", "{{found}}"]; @@ -1188,19 +1250,21 @@ describe("searchStringLiterals", () => { }); return result; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["element_1", "element_2", "result"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle map statements correctly", () => { @@ -1213,7 +1277,7 @@ describe("searchStringLiterals", () => { }); return result; } - ` + `; const expectedCode = ` function startPoint() { const arr = ["{{found}}", "{{found}}"]; @@ -1223,19 +1287,21 @@ describe("searchStringLiterals", () => { }); return result; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["element_1", "element_2"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); it("should handle while statements correctly", () => { @@ -1252,7 +1318,7 @@ describe("searchStringLiterals", () => { } return result; } - ` + `; const expectedCode = ` function startPoint() { const arr = ["{{found}}", "{{found}}"]; @@ -1266,19 +1332,21 @@ describe("searchStringLiterals", () => { } return result; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); const expected = ["element_1", "element_2", "result"]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); //? ******************************* @@ -1300,7 +1368,7 @@ describe("searchStringLiterals", () => { } return result; } - ` + `; const expectedCode = ` function startPoint() { const arr = ["{{found}}", "{{found}}"]; @@ -1315,19 +1383,27 @@ describe("searchStringLiterals", () => { } return result; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); - const expected = ["element_1", "element_2", "element_3", "element_4", "result"]; + const expected = [ + "element_1", + "element_2", + "element_3", + "element_4", + "result", + ]; expect(result).toEqual(expected); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); //? ******************************* @@ -1339,22 +1415,24 @@ describe("searchStringLiterals", () => { function startPoint() { "className_A".replace("className_A", "className_B"); } - ` + `; const expectedCode = ` function startPoint() { "{{found}}".replace("{{found}}", "{{found}}"); } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" + return "{{found}}"; }); expect(result).toEqual(["className_A", "className_A", "className_B"]); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); //? ******************************* @@ -1367,25 +1445,30 @@ describe("searchStringLiterals", () => { const value = "value"; return \`hello \${value}\`; } - ` + `; const expectedCode = ` function startPoint() { const value = "{{found}}"; return \`{{found}} \${value}\`; } - ` + `; const ast = parser.parse(code); const result: string[] = []; searchStringLiterals(findStartPointNode(ast)!, (str) => { result.push(str); - return "{{found}}" - }) + return "{{found}}"; + }); - expect(result).toEqual(["hello "/* Raw */, "hello " /* Cooked */, "value"]); - expect(stripCode(generator(ast, {}, code).code)).toEqual(stripCode(expectedCode)); + expect(result).toEqual([ + "hello " /* Raw */, + "hello " /* Cooked */, + "value", + ]); + expect(stripCode(generator(ast, {}, code).code)).toEqual( + stripCode(expectedCode), + ); }); - }); //! ================================ @@ -1393,14 +1476,13 @@ describe("searchStringLiterals", () => { //! ================================ describe("obfuscateJsWithAst", () => { - //? ******************************* //? Real World Example //? ******************************* 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 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 } = obfuscateJsWithAst(code, undefined); expect(stripCode(obfuscatedCode)).toEqual(stripCode(expectedCode)); diff --git a/src/__tests__/js.test.ts b/src/__tests__/js.test.ts index ff7c455..6789c63 100644 --- a/src/__tests__/js.test.ts +++ b/src/__tests__/js.test.ts @@ -1,14 +1,11 @@ import { describe, expect, test } from "vitest"; -import { - searchForwardComponent, -} from "../handlers/js"; +import { searchForwardComponent } from "../handlers/js"; //! ================================ //! searchForwardComponent //! ================================ describe("searchForwardComponent", () => { - test("should return component name when jsx format is correct", () => { // Arrange const content = `const element = o.jsx(ComponentName, {data: dataValue, index: "date"});`; @@ -22,7 +19,8 @@ describe("searchForwardComponent", () => { test("should return multiple component names for multiple matches", () => { // Arrange - const content = "o.jsx(FirstComponent, props); o.jsx(SecondComponent, otherProps);"; + const content = + "o.jsx(FirstComponent, props); o.jsx(SecondComponent, otherProps);"; // Act const result = searchForwardComponent(content); @@ -109,12 +107,17 @@ describe("searchForwardComponent", () => { const result = searchForwardComponent(content); // Assert - expect(result).toEqual(["FirstComponent", "SecondComponent", "ThirdComponent"]); + expect(result).toEqual([ + "FirstComponent", + "SecondComponent", + "ThirdComponent", + ]); }); test("should handle content with nested jsx calls", () => { // Arrange - const content = "o.jsx(ParentComponent, {children: o.jsx(ChildComponent, {})})"; + const content = + "o.jsx(ParentComponent, {children: o.jsx(ChildComponent, {})})"; // Act const result = searchForwardComponent(content); @@ -122,5 +125,4 @@ describe("searchForwardComponent", () => { // Assert expect(result).toEqual(["ParentComponent", "ChildComponent"]); }); - }); diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts index 9a43b75..02dfb93 100644 --- a/src/__tests__/utils.test.ts +++ b/src/__tests__/utils.test.ts @@ -1,8 +1,8 @@ -import { describe, it, expect, test, beforeEach } from "vitest"; +import { beforeEach, describe, expect, it, test } from "vitest"; import { + duplicationCheck, findContentBetweenMarker, getFilenameFromPath, - duplicationCheck, } from "../utils"; //! ================================ @@ -10,16 +10,20 @@ import { //! ================================ describe("findContentBetweenMarker", () => { - it("should return the correct content between markers", () => { const content = "123{{4}5{67}8}901{2345678}9"; - const targetStr = '5'; - const openSymbol = '{'; - const closeSymbol = '}'; + const targetStr = "5"; + const openSymbol = "{"; + const closeSymbol = "}"; const expectedOutput = ["{4}5{67}8", "2345678"]; - const result = findContentBetweenMarker(content, targetStr, openSymbol, closeSymbol); + const result = findContentBetweenMarker( + content, + targetStr, + openSymbol, + closeSymbol, + ); expect(result).toEqual(expectedOutput); }); @@ -36,14 +40,11 @@ describe("findContentBetweenMarker", () => { // }); }); - - //! ================================ //! getFilenameFromPath //! ================================ describe("getFilenameFromPath", () => { - test("should extract filename from a Unix-like path", () => { // Act const result = getFilenameFromPath("/home/user/documents/report.pdf"); @@ -130,7 +131,6 @@ describe("getFilenameFromPath", () => { //! ================================ describe("duplicationCheck", () => { - test("should return false for an array with no duplicates", () => { // Arrange const input = ["apple", "banana", "cherry"]; diff --git a/src/handlers/html.ts b/src/handlers/html.ts index dec206b..a7a5910 100644 --- a/src/handlers/html.ts +++ b/src/handlers/html.ts @@ -1,155 +1,155 @@ import type { SelectorConversion } from "../types"; -import * as htmlparser2 from "htmlparser2"; +import { cssUnescape } from "css-seasoning"; import { escape as htmlEscape } from "html-escaper"; +import * as htmlparser2 from "htmlparser2"; import { obfuscateKeys } from "../utils"; import { obfuscateJs } from "./js"; -import { cssUnescape } from "css-seasoning"; export const obfuscateHtmlClassNames = ({ - html, - selectorConversion, - obfuscateMarkerClass = "", - contentIgnoreRegexes = [], + html, + selectorConversion, + obfuscateMarkerClass = "", + contentIgnoreRegexes = [], }: { - html: string; - selectorConversion: SelectorConversion; - obfuscateMarkerClass?: string; - contentIgnoreRegexes?: RegExp[]; + html: string; + selectorConversion: SelectorConversion; + obfuscateMarkerClass?: string; + contentIgnoreRegexes?: RegExp[]; }) => { - const voidTags = [ - "area", - "base", - "br", - "col", - "command", - "embed", - "hr", - "img", - "input", - "keygen", - "link", - "meta", - "param", - "source", - "track", - "wbr", - ]; + 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; // Count of the obfuscate class scope tag (for nested tags with the same class name) - let ObsClassScopeTag = ""; + let modifiedHtml = ""; + let insideObsClassScope = false; + let ObsClassScopeTagCount = 0; // Count of the obfuscate class scope tag (for nested tags with the same class name) + let ObsClassScopeTag = ""; - let scriptContent = ""; - let isScriptTag = false; + let scriptContent = ""; + let isScriptTag = false; - const usedKeys: string[] = []; + const usedKeys: string[] = []; - const parser = new htmlparser2.Parser( - { - onprocessinginstruction(name, data) { - modifiedHtml += `<${data}>`; - }, - onopentag(tagName, attribs) { - if (tagName === "script") { - isScriptTag = true; - scriptContent = ""; // reset script content for a new script tag - } + const parser = new htmlparser2.Parser( + { + onprocessinginstruction(name, data) { + modifiedHtml += `<${data}>`; + }, + onopentag(tagName, attribs) { + if (tagName === "script") { + isScriptTag = true; + scriptContent = ""; // reset script content for a new script tag + } - if (attribs.class) { - // Check if the current tag is within the scope of the obfuscate class - if ( - !insideObsClassScope && - obfuscateMarkerClass && - attribs.class.includes(obfuscateMarkerClass) - ) { - insideObsClassScope = true; - ObsClassScopeTag = tagName; - } + if (attribs.class) { + // Check if the current tag is within the scope of the obfuscate class + if ( + !insideObsClassScope && + obfuscateMarkerClass && + attribs.class.includes(obfuscateMarkerClass) + ) { + insideObsClassScope = true; + ObsClassScopeTag = tagName; + } - if (insideObsClassScope || !obfuscateMarkerClass) { - const { obfuscatedContent, usedKeys: _usedKeys } = obfuscateKeys( - selectorConversion, - attribs.class, - [], - true, - ); - usedKeys.push(..._usedKeys); - // Update the class to the modified class names - attribs.class = obfuscatedContent; - } - } + if (insideObsClassScope || !obfuscateMarkerClass) { + const { obfuscatedContent, usedKeys: _usedKeys } = obfuscateKeys( + selectorConversion, + attribs.class, + [], + true, + ); + usedKeys.push(..._usedKeys); + // Update the class to the modified class names + attribs.class = obfuscatedContent; + } + } - if (insideObsClassScope && tagName === ObsClassScopeTag) { - ObsClassScopeTagCount++; - } + if (insideObsClassScope && tagName === ObsClassScopeTag) { + ObsClassScopeTagCount++; + } - // Reconstruct the tag with the modified class names - modifiedHtml += `<${tagName}`; - for (const key in attribs) { - // modifiedHtml += ` ${key}="${attribs[key]}"`; - modifiedHtml += ` ${key}="${htmlEscape(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; - } + // Reconstruct the tag with the modified class names + modifiedHtml += `<${tagName}`; + for (const key in attribs) { + // modifiedHtml += ` ${key}="${attribs[key]}"`; + modifiedHtml += ` ${key}="${htmlEscape(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 = cssUnescape(key).slice(1); - const obfuscatedJs = obfuscateJs( - obfuscatedScriptContent, - className, - { [key]: selectorConversion[key] }, - "{a HTML file path}", - contentIgnoreRegexes, - ); - if (obfuscatedJs !== obfuscatedScriptContent) { - obfuscatedScriptContent = obfuscatedJs; - usedKeys.push(key); - } - }); - modifiedHtml += `${obfuscatedScriptContent}`; - } - modifiedHtml += ``; + if (tagname === "script" && isScriptTag) { + isScriptTag = false; + let obfuscatedScriptContent = scriptContent; + Object.keys(selectorConversion).forEach((key) => { + const className = cssUnescape(key).slice(1); + const obfuscatedJs = obfuscateJs( + obfuscatedScriptContent, + className, + { [key]: selectorConversion[key] }, + "{a HTML file path}", + contentIgnoreRegexes, + ); + if (obfuscatedJs !== obfuscatedScriptContent) { + obfuscatedScriptContent = obfuscatedJs; + usedKeys.push(key); + } + }); + modifiedHtml += `${obfuscatedScriptContent}`; + } + modifiedHtml += ``; - if (insideObsClassScope && tagname === ObsClassScopeTag) { - ObsClassScopeTagCount--; - } + if (insideObsClassScope && tagname === ObsClassScopeTag) { + ObsClassScopeTagCount--; + } - if (ObsClassScopeTagCount === 0) { - insideObsClassScope = false; - } - }, - }, - { decodeEntities: true }, - ); + if (ObsClassScopeTagCount === 0) { + insideObsClassScope = false; + } + }, + }, + { decodeEntities: true }, + ); - parser.write(html); - parser.end(); + parser.write(html); + parser.end(); - return { - obfuscatedContent: modifiedHtml, - usedKeys: Array.from(new Set(usedKeys)), - }; -} + return { + obfuscatedContent: modifiedHtml, + usedKeys: Array.from(new Set(usedKeys)), + }; +}; diff --git a/src/handlers/js-ast.ts b/src/handlers/js-ast.ts index d0c1354..dbc120e 100644 --- a/src/handlers/js-ast.ts +++ b/src/handlers/js-ast.ts @@ -1,8 +1,8 @@ -import type { SelectorConversion } from "../types"; +import generator from "@babel/generator"; import * as parser from "@babel/parser"; import traverse, { type NodePath } from "@babel/traverse"; import * as t from "@babel/types"; -import generator from "@babel/generator"; +import type { SelectorConversion } from "../types"; import { obfuscateKeys } from "../utils"; @@ -18,7 +18,7 @@ export const obfuscateJsWithAst = ( code: string, selectorConversion: SelectorConversion | undefined, startingKeys: string[] = [], - stripUnnecessarySpace = true + stripUnnecessarySpace = true, ) => { const ast = parser.parse(code, { sourceType: "module", plugins: ["jsx"] }); const usedKeys: Set = new Set(); @@ -38,13 +38,17 @@ export const obfuscateJsWithAst = ( } // strip unnecessary space, e.g. " a b c " => "a b c " - str = stripUnnecessarySpace ? str.replace(/\s+/g, " ").trimStart() //? avoid trimming the end to keep the space between classes + str = stripUnnecessarySpace + ? str + .replace(/\s+/g, " ") + .trimStart() //? avoid trimming the end to keep the space between classes : str; - const { obfuscatedContent, usedKeys: obfuscateUsedKeys } = obfuscateKeys(selectorConversion, str); + const { obfuscatedContent, usedKeys: obfuscateUsedKeys } = + obfuscateKeys(selectorConversion, str); if (obfuscatedContent !== str) { - obfuscateUsedKeys.forEach(key => usedKeys.add(key)); + obfuscateUsedKeys.forEach((key) => usedKeys.add(key)); return obfuscatedContent; } }); @@ -62,11 +66,10 @@ export const obfuscateJsWithAst = ( const obfuscatedCode = generator(ast, options, code); return { obfuscatedCode: obfuscatedCode.code, - usedKeys: usedKeys + usedKeys: usedKeys, }; }; - /** * Search for string literals in the AST and replace them with the result of the callback function * @param path - the starting AST node @@ -74,11 +77,12 @@ export const obfuscateJsWithAst = ( * @param scannedNodes - (for recursion) keep track of scanned nodes to avoid infinite loop * @returns - the modified AST node */ -export const searchStringLiterals = (path: NodePath, +export const searchStringLiterals = ( + path: NodePath, callback: (str: string) => void | string, //? keep track of scanned nodes to avoid infinite loop - scannedNodes: Set = new Set() + scannedNodes: Set = new Set(), ) => { /* Skip this node if it has already been scanned */ if (path.node && scannedNodes.has(path.node)) { @@ -90,7 +94,7 @@ export const searchStringLiterals = (path: NodePath, if (t.isBlockStatement(path.node)) { const body = path.get("body"); if (Array.isArray(body)) { - body.forEach(nodePath => { + body.forEach((nodePath) => { switch (nodePath.node.type) { //? only care about statements that return statements maybe inside //? to avoid scanning all string literals in that block @@ -108,22 +112,20 @@ export const searchStringLiterals = (path: NodePath, } else { searchStringLiterals(body, callback, scannedNodes); } - } + } else if (t.isReturnStatement(path.node)) { /* function return statement */ - else if (t.isReturnStatement(path.node)) { const argument = path.get("argument"); if (argument) { if (!Array.isArray(argument)) { searchStringLiterals(argument, callback, scannedNodes); } else { - argument.forEach(arg => { + argument.forEach((arg) => { searchStringLiterals(arg, callback, scannedNodes); }); } } - } + } else if (t.isBinaryExpression(path.node)) { /* binary expression (e.g. const a = "hello" + "world") */ - else if (t.isBinaryExpression(path.node)) { const left = path.get("left"); const right = path.get("right"); if (left && !Array.isArray(left)) { @@ -132,15 +134,13 @@ export const searchStringLiterals = (path: NodePath, if (right && !Array.isArray(right)) { searchStringLiterals(right, callback, scannedNodes); } - } + } else if (t.isStringLiteral(path.node)) { /* string literal (e.g. "hello"), the string within the quotes */ - else if (t.isStringLiteral(path.node)) { const replacement = callback(path.node.value); if (replacement) { path.replaceWith(t.stringLiteral(replacement)); } - } - else if (t.isIdentifier(path.node)) { + } else if (t.isIdentifier(path.node)) { const variableName = path.node.name; const binding = path.scope.getBinding(variableName); if (binding && t.isVariableDeclarator(binding.path.node)) { @@ -154,16 +154,15 @@ export const searchStringLiterals = (path: NodePath, searchStringLiterals(body, callback, scannedNodes); } } - } + } else if (t.isCallExpression(path.node)) { /* call expression (e.g. const a = call()) */ - 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 => { + args.forEach((arg) => { if (t.isStringLiteral(arg.node)) { const replacement = callback(arg.node.value); if (replacement) { @@ -174,9 +173,8 @@ export const searchStringLiterals = (path: NodePath, } }); } - } + } else if (t.isConditionalExpression(path.node)) { /* conditional expression (e.g. const a = true ? "hello" : "world") */ - else if (t.isConditionalExpression(path.node)) { const test = path.get("test"); const consequent = path.get("consequent"); const alternate = path.get("alternate"); @@ -189,9 +187,8 @@ export const searchStringLiterals = (path: NodePath, if (alternate && !Array.isArray(alternate)) { searchStringLiterals(alternate, callback, scannedNodes); } - } + } else if (t.isIfStatement(path.node)) { /* if statement (e.g. if (true) { "hello" } else { "world" }) */ - else if (t.isIfStatement(path.node)) { const test = path.get("test"); const consequent = path.get("consequent"); const alternate = path.get("alternate"); @@ -204,47 +201,43 @@ export const searchStringLiterals = (path: NodePath, if (alternate && !Array.isArray(alternate)) { searchStringLiterals(alternate, callback, scannedNodes); } - } + } else if (t.isObjectExpression(path.node)) { /* object expression (e.g. const a = { key: "value" }) */ - else if (t.isObjectExpression(path.node)) { const properties = path.get("properties"); if (Array.isArray(properties)) { - properties.forEach(prop => { + properties.forEach((prop) => { searchStringLiterals(prop, callback, scannedNodes); }); } - } + } else if (t.isObjectProperty(path.node)) { /* object property (key and value of an object expression) */ - 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)) { /* array expression (e.g. const a = ["element_1", "element_2"]) */ - else if (t.isArrayExpression(path.node)) { const elements = path.get("elements"); if (Array.isArray(elements)) { - elements.forEach(element => { + elements.forEach((element) => { searchStringLiterals(element, callback, scannedNodes); }); } - } + } else if (t.isSwitchStatement(path.node)) { /* switch statement (e.g. switch (value) { case "1": return "one"; case "2": return "two"; default: return "default"; }) */ - else if (t.isSwitchStatement(path.node)) { const cases = path.get("cases"); if (Array.isArray(cases)) { - cases.forEach(c => { + cases.forEach((c) => { searchStringLiterals(c, callback, scannedNodes); }); } - } + } else if (t.isSwitchCase(path.node)) { /* switch case (e.g. case "1": return "one") */ - else if (t.isSwitchCase(path.node)) { const consequent = path.get("consequent"); if (Array.isArray(consequent)) { - consequent.forEach(c => { - if (t.isReturnStatement(c.node)) { // only care about return statements, if any variable declarations are present, they will be handled in the next iteration + consequent.forEach((c) => { + if (t.isReturnStatement(c.node)) { + // only care about return statements, if any variable declarations are present, they will be handled in the next iteration searchStringLiterals(c, callback, scannedNodes); } }); @@ -296,9 +289,8 @@ export const searchStringLiterals = (path: NodePath, searchStringLiterals(handlerBody, callback, scannedNodes); } } - } + } else if (t.isMemberExpression(path.node)) { /* member expression (e.g. "scroll-top".replace("-", "_")); "scroll-top ".concat("visible"); */ - else if (t.isMemberExpression(path.node)) { const object = path.get("object"); const property = path.get("property"); const argument = path.get("argument"); @@ -311,28 +303,26 @@ export const searchStringLiterals = (path: NodePath, if (argument && !Array.isArray(argument)) { searchStringLiterals(argument, callback, scannedNodes); } else if (Array.isArray(argument)) { - argument.forEach(arg => { + argument.forEach((arg) => { searchStringLiterals(arg, callback, scannedNodes); }); } - } + } else if (t.isTemplateLiteral(path.node)) { /* template literal (e.g. `hello ${name}`) */ - else if (t.isTemplateLiteral(path.node)) { const quasis = path.get("quasis"); const expressions = path.get("expressions"); if (Array.isArray(quasis)) { - quasis.forEach(quasi => { + quasis.forEach((quasi) => { searchStringLiterals(quasi, callback, scannedNodes); }); } if (Array.isArray(expressions)) { - expressions.forEach(expression => { + expressions.forEach((expression) => { searchStringLiterals(expression, callback, scannedNodes); }); } - } + } else if (t.isTemplateElement(path.node)) { /* template element (e.g. `hello ${name}`) */ - else if (t.isTemplateElement(path.node)) { const node = path.node as t.TemplateElement; if (node.value) { @@ -354,8 +344,7 @@ export const searchStringLiterals = (path: NodePath, } } } - } - else { + } else { path.traverse({ Identifier(innerPath) { searchStringLiterals(innerPath, callback, scannedNodes); diff --git a/src/handlers/js.ts b/src/handlers/js.ts index 65d484e..816d1e2 100644 --- a/src/handlers/js.ts +++ b/src/handlers/js.ts @@ -1,12 +1,12 @@ import type { SelectorConversion } from "../types"; import { - log, + addKeysToRegistery, + findClosestSymbolPosition, findContentBetweenMarker, - replaceFirstMatch, + log, normalizePath, obfuscateKeys, - addKeysToRegistery, - findClosestSymbolPosition + replaceFirstMatch, } from "../utils"; import { obfuscateJsWithAst } from "./js-ast"; @@ -25,7 +25,10 @@ export const searchForwardComponent = (content: string) => { }; const searchComponent = (content: string, componentName: string) => { - const componentSearchRegex = new RegExp(`\\b(?:const|let|var)\\s+(${componentName})\\s*=\\s*.*?(\\{)`, "g"); + const componentSearchRegex = new RegExp( + `\\b(?:const|let|var)\\s+(${componentName})\\s*=\\s*.*?(\\{)`, + "g", + ); // eg, let yt=l().forwardRef((e,t)=>{let const match = content.match(componentSearchRegex); let openSymbolPos = -1; @@ -33,81 +36,138 @@ const searchComponent = (content: string, componentName: string) => { openSymbolPos = content.indexOf(match[0]) + match[0].length; } - const closeMarkerPos = findClosestSymbolPosition(content, "{", "}", openSymbolPos, "forward"); + const closeMarkerPos = findClosestSymbolPosition( + content, + "{", + "}", + openSymbolPos, + "forward", + ); const componentContent = content.slice(openSymbolPos, closeMarkerPos); return componentContent; }; -export const obfuscateForwardComponentJs = (searchContent: string, wholeContent: string, selectorConversion: SelectorConversion) => { - const componentNames = searchForwardComponent(searchContent).filter((componentName) => { - return !componentName.includes("."); - }); +export const obfuscateForwardComponentJs = ( + searchContent: string, + wholeContent: string, + selectorConversion: SelectorConversion, +) => { + const componentNames = searchForwardComponent(searchContent).filter( + (componentName) => { + return !componentName.includes("."); + }, + ); - const componentsCode = componentNames.map(componentName => { + const componentsCode = componentNames.map((componentName) => { const componentContent = searchComponent(wholeContent, componentName); return { name: componentName, - code: componentContent - } + code: componentContent, + }; }); const componentsObfuscatedCode = componentsCode.map((componentContent) => { - const classNameBlocks = findContentBetweenMarker(componentContent.code, "className:", "{", "}"); - const obfuscatedClassNameBlocks = classNameBlocks.map(block => { - const { obfuscatedContent, usedKeys } = obfuscateKeys(selectorConversion, block); + const classNameBlocks = findContentBetweenMarker( + componentContent.code, + "className:", + "{", + "}", + ); + const obfuscatedClassNameBlocks = classNameBlocks.map((block) => { + const { obfuscatedContent, usedKeys } = obfuscateKeys( + selectorConversion, + block, + ); addKeysToRegistery(usedKeys); return obfuscatedContent; }); if (classNameBlocks.length !== obfuscatedClassNameBlocks.length) { - log("error", "Component obfuscation:", "classNameBlocks.length !== obfuscatedClassNameBlocks.length"); + log( + "error", + "Component obfuscation:", + "classNameBlocks.length !== obfuscatedClassNameBlocks.length", + ); return componentContent; } let obscuredCode = componentContent.code; for (let i = 0; i < classNameBlocks.length; i++) { - obscuredCode = replaceFirstMatch(obscuredCode, classNameBlocks[i], obfuscatedClassNameBlocks[i]); + obscuredCode = replaceFirstMatch( + obscuredCode, + classNameBlocks[i], + obfuscatedClassNameBlocks[i], + ); } log("debug", "Obscured keys in component:", componentContent.name); return { name: componentContent.name, - code: obscuredCode - } + code: obscuredCode, + }; }); - const componentObfuscatedcomponentCodePairs: { name: string, componentCode: string, componentObfuscatedCode: string }[] = []; + const componentObfuscatedcomponentCodePairs: { + name: string; + componentCode: string; + componentObfuscatedCode: string; + }[] = []; 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 + componentObfuscatedCode: componentsObfuscatedCode[i].code, }); } } for (let i = 0; i < componentsCode.length; i++) { - const childComponentObfuscatedcomponentCodePairs = obfuscateForwardComponentJs(componentsCode[i].code, wholeContent, selectorConversion); - componentObfuscatedcomponentCodePairs.push(...childComponentObfuscatedcomponentCodePairs); + const childComponentObfuscatedcomponentCodePairs = + obfuscateForwardComponentJs( + componentsCode[i].code, + wholeContent, + selectorConversion, + ); + componentObfuscatedcomponentCodePairs.push( + ...childComponentObfuscatedcomponentCodePairs, + ); } return componentObfuscatedcomponentCodePairs; }; -export const obfuscateJs = (content: string, key: string, selectorCoversion: SelectorConversion - , filePath: string, contentIgnoreRegexes: RegExp[] = [], useAst: boolean = false) => { - +export const obfuscateJs = ( + content: string, + key: string, + selectorCoversion: SelectorConversion, + filePath: string, + contentIgnoreRegexes: RegExp[] = [], + useAst = false, +) => { if (useAst) { try { - const { obfuscatedCode, usedKeys } = obfuscateJsWithAst(content, selectorCoversion, key ? [key] : [], true); + const { obfuscatedCode, usedKeys } = obfuscateJsWithAst( + content, + selectorCoversion, + key ? [key] : [], + true, + ); addKeysToRegistery(usedKeys); if (content !== obfuscatedCode) { - log("debug", `Obscured keys with AST and marker "${key}":`, `${normalizePath(filePath)}`); + log( + "debug", + `Obscured keys with AST and marker "${key}":`, + `${normalizePath(filePath)}`, + ); } return obfuscatedCode; } catch (error) { if (error instanceof SyntaxError) { log("warn", "Syntax error ignored:", error); - log("warn", "Obfuscation with AST failed:", "Falling back to regex obfuscation"); + log( + "warn", + "Obfuscation with AST failed:", + "Falling back to regex obfuscation", + ); } else { throw error; // re-throw non-syntax errors } @@ -116,12 +176,20 @@ export const obfuscateJs = (content: string, key: string, selectorCoversion: Sel const truncatedContents = findContentBetweenMarker(content, key, "{", "}"); truncatedContents.forEach((truncatedContent) => { - const { obfuscatedContent, usedKeys } = obfuscateKeys(selectorCoversion, truncatedContent, contentIgnoreRegexes); + const { obfuscatedContent, usedKeys } = obfuscateKeys( + selectorCoversion, + truncatedContent, + contentIgnoreRegexes, + ); addKeysToRegistery(usedKeys); if (truncatedContent !== obfuscatedContent) { content = content.replace(truncatedContent, obfuscatedContent); - log("debug", `Obscured keys with marker "${key}":`, `${normalizePath(filePath)}`); + log( + "debug", + `Obscured keys with marker "${key}":`, + `${normalizePath(filePath)}`, + ); } }); return content; -}; \ No newline at end of file +}; diff --git a/src/utils.ts b/src/utils.ts index f61071d..40025a1 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,17 +1,14 @@ import fs from "node:fs"; import path from "node:path"; -import type { - LogLevel, - SelectorConversion, -} from "./types"; -import { cssEscape, cssUnescape, type ConversionTables } from "css-seasoning"; +import { type ConversionTables, cssEscape, cssUnescape } from "css-seasoning"; import { obfuscateHtmlClassNames } from "./handlers/html"; import { obfuscateJs } from "./handlers/js"; +import type { LogLevel, SelectorConversion } from "./types"; // Add a new function for path filtering /** * Checks if a path should be included based on whitelist and blacklist rules - * + * * @param filePath - The file path to check * @param whiteListedFolderPaths - Paths to include * @param blackListedFolderPaths - Paths to exclude (higher priority than whitelist) @@ -20,7 +17,7 @@ import { obfuscateJs } from "./handlers/js"; const shouldIncludePath = ( filePath: string, whiteListedFolderPaths: (string | RegExp)[] = [], - blackListedFolderPaths: (string | RegExp)[] = [] + blackListedFolderPaths: (string | RegExp)[] = [], ): boolean => { const normalizedPath = normalizePath(filePath); @@ -37,15 +34,17 @@ const shouldIncludePath = ( } // Check if the path is whitelisted (if whitelist is not empty) - const isWhitelisted = whiteListedFolderPaths.length === 0 || whiteListedFolderPaths.some((includePath) => { - if (typeof includePath === "string") { - return normalizedPath.includes(includePath); - } - return includePath.test(normalizedPath); - }); + const isWhitelisted = + whiteListedFolderPaths.length === 0 || + whiteListedFolderPaths.some((includePath) => { + if (typeof includePath === "string") { + return normalizedPath.includes(includePath); + } + return includePath.test(normalizedPath); + }); return isWhitelisted; -} +}; //! ==================== //! Log @@ -65,7 +64,14 @@ export const log = (type: LogLevel, task: string, data: unknown) => { switch (type) { case "debug": - console.debug(mainColor, issuer, "[Debug] \x1b[37m", task, data, "\x1b[0m"); + console.debug( + mainColor, + issuer, + "[Debug] \x1b[37m", + task, + data, + "\x1b[0m", + ); break; case "info": console.info(mainColor, issuer, "🗯️ \x1b[36m", task, data, "\x1b[0m"); @@ -87,7 +93,7 @@ export const log = (type: LogLevel, task: string, data: unknown) => { export const setLogLevel = (level: LogLevel) => { logLevel = level; -} +}; //! ==================== //! Constants @@ -105,46 +111,44 @@ export const setLogLevel = (level: LogLevel) => { // }; //! ==================== -//! +//! //! ==================== export const usedKeyRegistery = new Set(); +export const replaceJsonKeysInFiles = ({ + conversionTables, + targetFolder, + allowExtensions, -export const replaceJsonKeysInFiles = ( - { - conversionTables, - targetFolder, - allowExtensions, + contentIgnoreRegexes, - contentIgnoreRegexes, + whiteListedFolderPaths, + blackListedFolderPaths, + enableObfuscateMarkerClasses, + obfuscateMarkerClasses, + removeObfuscateMarkerClassesAfterObfuscated, - whiteListedFolderPaths, - blackListedFolderPaths, - enableObfuscateMarkerClasses, - obfuscateMarkerClasses, - removeObfuscateMarkerClassesAfterObfuscated, + enableJsAst, +}: { + conversionTables: ConversionTables; + targetFolder: string; + allowExtensions: string[]; - enableJsAst, - }: { - conversionTables: ConversionTables, - targetFolder: string, - allowExtensions: string[], + contentIgnoreRegexes: RegExp[]; - contentIgnoreRegexes: RegExp[], + whiteListedFolderPaths: (string | RegExp)[]; + blackListedFolderPaths: (string | RegExp)[]; + enableObfuscateMarkerClasses: boolean; + obfuscateMarkerClasses: string[]; + removeObfuscateMarkerClassesAfterObfuscated: boolean; - whiteListedFolderPaths: (string | RegExp)[], - blackListedFolderPaths: (string | RegExp)[], - enableObfuscateMarkerClasses: boolean, - obfuscateMarkerClasses: string[], - removeObfuscateMarkerClassesAfterObfuscated: boolean, - - enableJsAst: boolean, - }) => { + enableJsAst: boolean; +}) => { //ref: https://github.com/n4j1Br4ch1D/postcss-obfuscator/blob/main/utils.js if (removeObfuscateMarkerClassesAfterObfuscated) { - obfuscateMarkerClasses.forEach(obfuscateMarkerClass => { + obfuscateMarkerClasses.forEach((obfuscateMarkerClass) => { conversionTables.selectors[cssEscape(`.${obfuscateMarkerClass}`)] = ""; }); } @@ -159,11 +163,15 @@ export const replaceJsonKeysInFiles = ( fs.readdirSync(filePath).forEach((subFilePath) => { replaceJsonKeysInFile(path.join(filePath, subFilePath)); }); - } else if ( - allowExtensions.includes(fileExt) - ) { + } else if (allowExtensions.includes(fileExt)) { // Use the unified path filtering function - if (!shouldIncludePath(filePath, whiteListedFolderPaths, blackListedFolderPaths)) { + if ( + !shouldIncludePath( + filePath, + whiteListedFolderPaths, + blackListedFolderPaths, + ) + ) { return; } @@ -172,12 +180,16 @@ export const replaceJsonKeysInFiles = ( const fileContentOriginal = fileContent; if (enableObfuscateMarkerClasses) { - obfuscateMarkerClasses.forEach(obfuscateMarkerClass => { + obfuscateMarkerClasses.forEach((obfuscateMarkerClass) => { const isHtml = [".html"].includes(fileExt); if (isHtml) { // filter all html // ref: https://stackoverflow.com/a/56102604 - const htmlRegex = new RegExp(`(<(.*)>(.*)<\/([^br][A-Za-z0-9]+)>)`, 'g'); + // biome-ignore lint/complexity/useRegexLiterals: using regex literals herer will cause typing issues + const htmlRegex = new RegExp( + "(<(.*)>(.*))", + "g", + ); const htmlMatch = fileContent.match(htmlRegex); if (htmlMatch) { const htmlOriginal = htmlMatch[0]; @@ -189,23 +201,30 @@ export const replaceJsonKeysInFiles = ( }); addKeysToRegistery(usedKeys); if (htmlOriginal !== obfuscatedContent) { - fileContent = fileContent.replace(htmlOriginal, obfuscatedContent); + fileContent = fileContent.replace( + htmlOriginal, + obfuscatedContent, + ); } } } else { - const obfuscateScriptContent = obfuscateJs(fileContent, + const obfuscateScriptContent = obfuscateJs( + fileContent, obfuscateMarkerClass, conversionTables.selectors, filePath, contentIgnoreRegexes, - enableJsAst + enableJsAst, ); if (fileContent !== obfuscateScriptContent) { fileContent = obfuscateScriptContent; - log("debug", "Obscured keys in JS like content file:", normalizePath(filePath)); + log( + "debug", + "Obscured keys in JS like content file:", + normalizePath(filePath), + ); } } - }); } else { /* Handle Full Obfuscation */ @@ -217,11 +236,15 @@ export const replaceJsonKeysInFiles = ( conversionTables.selectors, filePath, contentIgnoreRegexes, - enableJsAst + enableJsAst, ); if (fileContent !== obfuscateScriptContent) { fileContent = obfuscateScriptContent; - log("debug", "Obscured keys in JSX related file:", normalizePath(filePath)); + log( + "debug", + "Obscured keys in JSX related file:", + normalizePath(filePath), + ); } } else if ([".html"].includes(fileExt)) { //! NEW @@ -237,10 +260,9 @@ export const replaceJsonKeysInFiles = ( const { obfuscatedContent, usedKeys } = obfuscateKeys( conversionTables.selectors, fileContent, - contentIgnoreRegexes + contentIgnoreRegexes, ); - fileContent = obfuscatedContent; addKeysToRegistery(usedKeys); } @@ -250,11 +272,10 @@ export const replaceJsonKeysInFiles = ( log("success", "Data obfuscated:", normalizePath(filePath)); fs.writeFileSync(filePath, fileContent); } - } else if (fileExt === ".css") { cssPaths.push(filePath); } - } + }; // Process all files in the directory excluding .css files replaceJsonKeysInFile(targetFolder); @@ -263,13 +284,13 @@ export const replaceJsonKeysInFiles = ( // cssPaths.forEach(async (cssPath) => { // await obfuscateCss(classConversion, cssPath, removeOriginalCss, !enableObfuscateMarkerClasses); // }); -} +}; export const obfuscateKeys = ( selectorConversion: SelectorConversion, fileContent: string, contentIgnoreRegexes: RegExp[] = [], - useHtmlEntity = false + useHtmlEntity = false, ) => { //ref: https://github.com/n4j1Br4ch1D/postcss-obfuscator/blob/main/utils.js @@ -282,14 +303,20 @@ export const obfuscateKeys = ( keyUse = escapeRegExp(keyUse.replace(/\\/g, "")); // escape the key //? sample: "text-sm w-full\n text-right\n p-2 flex gap-2 hover:bg-gray-100 dark:hover:bg-red-700 text-right" - let exactMatchRegex = new RegExp(`([\\s"'\\\`]|^)(${keyUse})(?=$|[\\s"'\\\`]|\\\\n|\\\\",|\\\\"})`, 'g'); // match exact wording & avoid ` ' "" + const exactMatchRegex = new RegExp( + `([\\s"'\\\`]|^)(${keyUse})(?=$|[\\s"'\\\`]|\\\\n|\\\\",|\\\\"})`, + "g", + ); // match exact wording & avoid ` ' "" // exactMatchRegex = new RegExp(`([\\s"'\\\`]|^)(${keyUse})(?=$|[\\s"'\\\`])`, 'g'); // match exact wording & avoid ` ' "" const replacement = `$1${selectorConversion[key].slice(1).replace(/\\/g, "").slice(1)}`; const matches = fileContent.match(exactMatchRegex); const originalObscuredContentPairs = matches?.map((match) => { - return { originalContent: match, obscuredContent: match.replace(exactMatchRegex, replacement) }; + return { + originalContent: match, + obscuredContent: match.replace(exactMatchRegex, replacement), + }; }); fileContent = fileContent.replace(exactMatchRegex, replacement); // capture preceding space @@ -299,26 +326,39 @@ export const obfuscateKeys = ( originalContentFragments?.map((originalContentFragment) => { originalObscuredContentPairs?.map((pair) => { - if (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 ( + 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 }; -} +}; const escapeRegExp = (str: string) => { //ref: https://github.com/n4j1Br4ch1D/postcss-obfuscator/blob/main/utils.js - return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string + return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string }; /** @@ -328,19 +368,19 @@ const escapeRegExp = (str: string) => { */ export const getFilenameFromPath = (filePath: string) => { //ref: https://github.com/n4j1Br4ch1D/postcss-obfuscator/blob/main/utils.js - return filePath.replace(/^.*[\\/]/, ''); + return filePath.replace(/^.*[\\/]/, ""); }; /** * Normalizes a file path by replacing backslashes with forward slashes. - * + * * @param filePath - The file path to normalize. * @returns The normalized file path. - * + * * @example * // Returns: 'c:/Users/next-css-obfuscator/src/utils.ts' * normalizePath('c:\\Users\\next-css-obfuscator\\src\\utils.ts'); - * + * * @example * // Returns: 'path/to/file' * normalizePath('path\\to\\file'); @@ -350,23 +390,34 @@ export const normalizePath = (filePath: string) => { }; /** - * - * @param content - * @param openMarker - * @param closeMarker - * @param startPosition + * + * @param content + * @param openMarker + * @param closeMarker + * @param startPosition * @param direction - if "forward", the function will search the closest closeMarker after the startPosition, if "backward", the function will search the closest openMarker before the startPosition - * @returns + * @returns */ -export const findClosestSymbolPosition = (content: string, openMarker: string, closeMarker: string, startPosition = 0, direction: "forward" | "backward" = "backward") => { +export const findClosestSymbolPosition = ( + content: string, + openMarker: string, + closeMarker: string, + startPosition = 0, + direction: "forward" | "backward" = "backward", +) => { let level = 0; let currentPos = startPosition; if (direction === "backward") { while (currentPos >= 0 && level >= 0) { - if (content.slice(currentPos, currentPos + openMarker.length) === openMarker) { + if ( + content.slice(currentPos, currentPos + openMarker.length) === openMarker + ) { level--; - } else if (content.slice(currentPos, currentPos + closeMarker.length) === closeMarker) { + } else if ( + content.slice(currentPos, currentPos + closeMarker.length) === + closeMarker + ) { level++; } currentPos--; @@ -377,9 +428,14 @@ export const findClosestSymbolPosition = (content: string, openMarker: string, c } } else { while (currentPos < content.length && level >= 0) { - if (content.slice(currentPos, currentPos + openMarker.length) === openMarker) { + if ( + content.slice(currentPos, currentPos + openMarker.length) === openMarker + ) { level++; - } else if (content.slice(currentPos, currentPos + closeMarker.length) === closeMarker) { + } else if ( + content.slice(currentPos, currentPos + closeMarker.length) === + closeMarker + ) { level--; } currentPos++; @@ -393,7 +449,12 @@ export const findClosestSymbolPosition = (content: string, openMarker: string, c return currentPos; }; -export const findContentBetweenMarker = (content: string, targetStr: string, openMarker: string, closeMarker: string) => { +export const findContentBetweenMarker = ( + content: string, + targetStr: string, + openMarker: string, + closeMarker: string, +) => { if (openMarker === closeMarker) { throw new Error("openMarker and closeMarker can not be the same"); } @@ -401,8 +462,20 @@ export const findContentBetweenMarker = (content: string, targetStr: string, ope let targetStrPosition = content.indexOf(targetStr); const truncatedContents: string[] = []; while (targetStrPosition !== -1 && targetStrPosition < content.length) { - const openPos = findClosestSymbolPosition(content, openMarker, closeMarker, targetStrPosition, "backward"); - const closePos = findClosestSymbolPosition(content, openMarker, closeMarker, targetStrPosition, "forward"); + const openPos = findClosestSymbolPosition( + content, + openMarker, + closeMarker, + targetStrPosition, + "backward", + ); + const closePos = findClosestSymbolPosition( + content, + openMarker, + closeMarker, + targetStrPosition, + "forward", + ); if (openPos === -1 && closePos === -1) { break; @@ -428,7 +501,7 @@ export const addKeysToRegistery = (usedKeys: Set | string[]) => { /** * Find all files with the specified extension in the build folder. - * + * * @param ext - the extension of the files to find (e.g. .css) "." is required. * @param targetFolderPath - the path to the folder to start searching from. * @param options - optional parameters. @@ -437,11 +510,12 @@ export const addKeysToRegistery = (usedKeys: Set | string[]) => { * @returns - an array of file relative paths. */ export const findAllFilesWithExt = ( - ext: string, targetFolderPath: string, + ext: string, + targetFolderPath: string, options?: { - whiteListedFolderPaths?: (string | RegExp)[], - blackListedFolderPaths?: (string | RegExp)[], - } + whiteListedFolderPaths?: (string | RegExp)[]; + blackListedFolderPaths?: (string | RegExp)[]; + }, ): string[] => { if (!fs.existsSync(targetFolderPath)) { return []; @@ -471,15 +545,19 @@ export const findAllFilesWithExt = ( } } }); - } + }; // start searching for files from the specified directory findFiles(targetFolderPath); return targetExtFiles; -} +}; -export const replaceFirstMatch = (source: string, find: string, replace: string): string => { +export const replaceFirstMatch = ( + source: string, + find: string, + replace: string, +): string => { const index = source.indexOf(find); if (index !== -1) { return source.slice(0, index) + replace + source.slice(index + find.length); @@ -499,10 +577,10 @@ export const duplicationCheck = (arr: string[]) => { /** * Convert a string to a number by summing the char codes of each character - * + * * @param str - The string to convert * @returns The sum of the char codes of each character in the string - * + * * @example * stringToNumber("abc") // returns 294 (97 + 98 + 99) * stringToNumber("hello") // returns 532 (104 + 101 + 108 + 108 + 111) @@ -511,12 +589,11 @@ export const stringToNumber = (str: string) => { return str.split("").reduce((acc, char) => { return acc + char.charCodeAt(0); }, 0); -} - +}; /** * Loads and merges all JSON files (Conversion Tables) in the specified folder. - * + * * @param folderPath - The folder path to load the conversion tables from. * @returns ConversionTables - The merged conversion tables. */ @@ -530,7 +607,10 @@ export const loadConversionTables = (folderPath: string): ConversionTables => { const filePath = path.join(folderPath, file); const fileData = JSON.parse(fs.readFileSync(filePath, "utf-8")); - if (Object.keys(fileData).includes("ident") && Object.keys(fileData).includes("selector")) { + if ( + Object.keys(fileData).includes("ident") && + Object.keys(fileData).includes("selector") + ) { Object.assign(tables.idents, fileData.ident); Object.assign(tables.selectors, fileData.selector); } else { @@ -541,5 +621,4 @@ export const loadConversionTables = (folderPath: string): ConversionTables => { }); return tables; -} - +}; From cf7f33aee78c9030ca5b59eba01a30c5e9d6daf5 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Fri, 30 May 2025 18:38:54 +0100 Subject: [PATCH 26/32] minor: CRLF to LF --- .github/workflows/auto_test.yml | 5 ++--- .github/workflows/release.yml | 2 +- biome.json | 1 - src/handlers/html.ts | 3 +-- src/handlers/js-ast.ts | 36 ++++++++++++++++----------------- src/handlers/js.ts | 9 +++++---- src/utils.ts | 1 - 7 files changed, 27 insertions(+), 30 deletions(-) diff --git a/.github/workflows/auto_test.yml b/.github/workflows/auto_test.yml index 38c6b2d..d169c09 100644 --- a/.github/workflows/auto_test.yml +++ b/.github/workflows/auto_test.yml @@ -4,9 +4,9 @@ name: Tests on: # Triggers the workflow on push or pull request events but only for the "main" branch push: - branches: [ "main" ] + branches: ["main"] pull_request: - branches: [ "main" ] + branches: ["main"] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -30,4 +30,3 @@ jobs: # Runs test using the runners shell - name: Test run: npm run test - \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 377ba19..8a4725e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,7 +30,7 @@ jobs: - name: Edit First Release Version run: sed -i 's/\( FIRST_RELEASE\) = "1.0.0"/\1 = "3.0.0"/' node_modules/semantic-release/lib/definitions/constants.js - + - name: Edit First Prerelease Version run: sed -i 's/\( FIRSTPRERELEASE\) = "1"/\1 = "3"/' node_modules/semantic-release/lib/definitions/constants.js diff --git a/biome.json b/biome.json index 74c13be..923f464 100644 --- a/biome.json +++ b/biome.json @@ -53,7 +53,6 @@ }, "suspicious": { "noDebugger": "info", - "noConsoleLog": "info", "noEmptyBlockStatements": "error", "noExplicitAny": "error", "noExtraNonNullAssertion": "error", diff --git a/src/handlers/html.ts b/src/handlers/html.ts index a7a5910..91b5665 100644 --- a/src/handlers/html.ts +++ b/src/handlers/html.ts @@ -48,7 +48,7 @@ export const obfuscateHtmlClassNames = ({ const parser = new htmlparser2.Parser( { - onprocessinginstruction(name, data) { + onprocessinginstruction(_, data) { modifiedHtml += `<${data}>`; }, onopentag(tagName, attribs) { @@ -73,7 +73,6 @@ export const obfuscateHtmlClassNames = ({ selectorConversion, attribs.class, [], - true, ); usedKeys.push(..._usedKeys); // Update the class to the modified class names diff --git a/src/handlers/js-ast.ts b/src/handlers/js-ast.ts index dbc120e..b533429 100644 --- a/src/handlers/js-ast.ts +++ b/src/handlers/js-ast.ts @@ -38,16 +38,16 @@ export const obfuscateJsWithAst = ( } // strip unnecessary space, e.g. " a b c " => "a b c " - str = stripUnnecessarySpace + const newStr = stripUnnecessarySpace ? str .replace(/\s+/g, " ") .trimStart() //? avoid trimming the end to keep the space between classes : str; const { obfuscatedContent, usedKeys: obfuscateUsedKeys } = - obfuscateKeys(selectorConversion, str); + obfuscateKeys(selectorConversion, newStr); - if (obfuscatedContent !== str) { + if (obfuscatedContent !== newStr) { obfuscateUsedKeys.forEach((key) => usedKeys.add(key)); return obfuscatedContent; } @@ -79,7 +79,7 @@ export const obfuscateJsWithAst = ( */ export const searchStringLiterals = ( path: NodePath, - callback: (str: string) => void | string, + callback: (str: string) => undefined | string, //? keep track of scanned nodes to avoid infinite loop scannedNodes: Set = new Set(), @@ -113,7 +113,7 @@ export const searchStringLiterals = ( searchStringLiterals(body, callback, scannedNodes); } } else if (t.isReturnStatement(path.node)) { - /* function return statement */ + /* function return statement */ const argument = path.get("argument"); if (argument) { if (!Array.isArray(argument)) { @@ -125,7 +125,7 @@ export const searchStringLiterals = ( } } } else if (t.isBinaryExpression(path.node)) { - /* binary expression (e.g. const a = "hello" + "world") */ + /* binary expression (e.g. const a = "hello" + "world") */ const left = path.get("left"); const right = path.get("right"); if (left && !Array.isArray(left)) { @@ -135,7 +135,7 @@ export const searchStringLiterals = ( searchStringLiterals(right, callback, scannedNodes); } } else if (t.isStringLiteral(path.node)) { - /* string literal (e.g. "hello"), the string within the quotes */ + /* string literal (e.g. "hello"), the string within the quotes */ const replacement = callback(path.node.value); if (replacement) { path.replaceWith(t.stringLiteral(replacement)); @@ -155,7 +155,7 @@ export const searchStringLiterals = ( } } } else if (t.isCallExpression(path.node)) { - /* call expression (e.g. const a = call()) */ + /* call expression (e.g. const a = call()) */ const callee = path.get("callee"); if (callee && !Array.isArray(callee)) { searchStringLiterals(callee, callback, scannedNodes); @@ -174,7 +174,7 @@ export const searchStringLiterals = ( }); } } else if (t.isConditionalExpression(path.node)) { - /* conditional expression (e.g. const a = true ? "hello" : "world") */ + /* conditional expression (e.g. const a = true ? "hello" : "world") */ const test = path.get("test"); const consequent = path.get("consequent"); const alternate = path.get("alternate"); @@ -188,7 +188,7 @@ export const searchStringLiterals = ( searchStringLiterals(alternate, callback, scannedNodes); } } else if (t.isIfStatement(path.node)) { - /* if statement (e.g. if (true) { "hello" } else { "world" }) */ + /* if statement (e.g. if (true) { "hello" } else { "world" }) */ const test = path.get("test"); const consequent = path.get("consequent"); const alternate = path.get("alternate"); @@ -202,7 +202,7 @@ export const searchStringLiterals = ( searchStringLiterals(alternate, callback, scannedNodes); } } else if (t.isObjectExpression(path.node)) { - /* object expression (e.g. const a = { key: "value" }) */ + /* object expression (e.g. const a = { key: "value" }) */ const properties = path.get("properties"); if (Array.isArray(properties)) { properties.forEach((prop) => { @@ -210,13 +210,13 @@ export const searchStringLiterals = ( }); } } else if (t.isObjectProperty(path.node)) { - /* object property (key and value of an object expression) */ + /* object property (key and value of an object expression) */ const value = path.get("value"); if (value && !Array.isArray(value)) { searchStringLiterals(value, callback, scannedNodes); } } else if (t.isArrayExpression(path.node)) { - /* array expression (e.g. const a = ["element_1", "element_2"]) */ + /* array expression (e.g. const a = ["element_1", "element_2"]) */ const elements = path.get("elements"); if (Array.isArray(elements)) { elements.forEach((element) => { @@ -224,7 +224,7 @@ export const searchStringLiterals = ( }); } } else if (t.isSwitchStatement(path.node)) { - /* switch statement (e.g. switch (value) { case "1": return "one"; case "2": return "two"; default: return "default"; }) */ + /* switch statement (e.g. switch (value) { case "1": return "one"; case "2": return "two"; default: return "default"; }) */ const cases = path.get("cases"); if (Array.isArray(cases)) { cases.forEach((c) => { @@ -232,7 +232,7 @@ export const searchStringLiterals = ( }); } } else if (t.isSwitchCase(path.node)) { - /* switch case (e.g. case "1": return "one") */ + /* switch case (e.g. case "1": return "one") */ const consequent = path.get("consequent"); if (Array.isArray(consequent)) { consequent.forEach((c) => { @@ -290,7 +290,7 @@ export const searchStringLiterals = ( } } } else if (t.isMemberExpression(path.node)) { - /* member expression (e.g. "scroll-top".replace("-", "_")); "scroll-top ".concat("visible"); */ + /* member expression (e.g. "scroll-top".replace("-", "_")); "scroll-top ".concat("visible"); */ const object = path.get("object"); const property = path.get("property"); const argument = path.get("argument"); @@ -308,7 +308,7 @@ export const searchStringLiterals = ( }); } } else if (t.isTemplateLiteral(path.node)) { - /* template literal (e.g. `hello ${name}`) */ + /* template literal (e.g. `hello ${name}`) */ const quasis = path.get("quasis"); const expressions = path.get("expressions"); if (Array.isArray(quasis)) { @@ -322,7 +322,7 @@ export const searchStringLiterals = ( }); } } else if (t.isTemplateElement(path.node)) { - /* template element (e.g. `hello ${name}`) */ + /* template element (e.g. `hello ${name}`) */ const node = path.node as t.TemplateElement; if (node.value) { diff --git a/src/handlers/js.ts b/src/handlers/js.ts index 816d1e2..ba5d839 100644 --- a/src/handlers/js.ts +++ b/src/handlers/js.ts @@ -120,10 +120,10 @@ export const obfuscateForwardComponentJs = ( } } - for (let i = 0; i < componentsCode.length; i++) { + for (const component of componentsCode) { const childComponentObfuscatedcomponentCodePairs = obfuscateForwardComponentJs( - componentsCode[i].code, + component.code, wholeContent, selectorConversion, ); @@ -175,6 +175,7 @@ export const obfuscateJs = ( } const truncatedContents = findContentBetweenMarker(content, key, "{", "}"); + let newContent = content; truncatedContents.forEach((truncatedContent) => { const { obfuscatedContent, usedKeys } = obfuscateKeys( selectorCoversion, @@ -183,7 +184,7 @@ export const obfuscateJs = ( ); addKeysToRegistery(usedKeys); if (truncatedContent !== obfuscatedContent) { - content = content.replace(truncatedContent, obfuscatedContent); + newContent = newContent.replace(truncatedContent, obfuscatedContent); log( "debug", `Obscured keys with marker "${key}":`, @@ -191,5 +192,5 @@ export const obfuscateJs = ( ); } }); - return content; + return newContent; }; diff --git a/src/utils.ts b/src/utils.ts index 40025a1..229bd13 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -290,7 +290,6 @@ export const obfuscateKeys = ( selectorConversion: SelectorConversion, fileContent: string, contentIgnoreRegexes: RegExp[] = [], - useHtmlEntity = false, ) => { //ref: https://github.com/n4j1Br4ch1D/postcss-obfuscator/blob/main/utils.js From 63800ef2f2b67fb0255ba3736bf6e44aa85b9581 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 30 May 2025 17:39:49 +0000 Subject: [PATCH 27/32] chore(release): 3.0.0-beta.4 [skip ci] # [3.0.0-beta.4](https://github.com/soranoo/next-css-obfuscator/compare/v3.0.0-beta.3...v3.0.0-beta.4) (2025-05-30) ### Features * support ident prefix/suffix options ([32952bf](https://github.com/soranoo/next-css-obfuscator/commit/32952bfdf678dbb71eb27fb60bc25fd55df66b48)) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 63e278a..ca340e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next-css-obfuscator", - "version": "3.0.0-beta.3", + "version": "3.0.0-beta.4", "description": "A package deeply inspired by PostCSS-Obfuscator but for Next.js.", "main": "dist/index.js", "type": "commonjs", From 3d6035d56374dbbfa74536f6c7475f3a5ff97001 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Fri, 30 May 2025 19:24:47 +0100 Subject: [PATCH 28/32] fix: update key names in loadConversionTables for consistency --- README.md | 21 +++++++-------------- src/utils.ts | 8 ++++---- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 7fe27e1..a747b48 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,7 @@ Visit the [npm](https://www.npmjs.com/package/next-css-obfuscator) page. ##### Obfuscate all files ```javascript + /** @type {import("next-css-obfuscator").Options} */ module.exports = { enable: true, mode: "random", // random | simplify @@ -181,10 +182,11 @@ Visit the [npm](https://www.npmjs.com/package/next-css-obfuscator) page. ##### Partially obfuscate - > [!CAUTION]\ - > Partially obfuscate can be EXTREMELY buggy. Be cautious when using this feature. +> [!CAUTION]\ +> Partially obfuscate can be EXTREMELY buggy. Be cautious when using this feature. ```javascript + /** @type {import("next-css-obfuscator").Options} */ module.exports = { enable: true, mode: "random", // random | simplify @@ -196,20 +198,10 @@ Visit the [npm](https://www.npmjs.com/package/next-css-obfuscator) page. ``` - ##### TypeScript - - ```ts - import { Options } from "next-css-obfuscator"; - - module.exports = { - // other options ... - } satisfies Options; - ``` - Feel free to checkout [📖 Config Options Reference](#-config-options-reference) for more options and details. - > [!TIP]\ - > The obfuscation will never work as expected, tweak the options with your own needs. +> [!TIP]\ +> The obfuscation will never work as expected, tweak the options with your own needs. 2. Add the following code to `package.json`: @@ -286,6 +278,7 @@ If you are interested in my setting, here it is ```javascript // next-css-obfuscator.config.cjs +/** @type {import("next-css-obfuscator").Options} */ module.exports = { enable: true, mode: "random", // random | simplify diff --git a/src/utils.ts b/src/utils.ts index 229bd13..84e6bfd 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -607,11 +607,11 @@ export const loadConversionTables = (folderPath: string): ConversionTables => { const fileData = JSON.parse(fs.readFileSync(filePath, "utf-8")); if ( - Object.keys(fileData).includes("ident") && - Object.keys(fileData).includes("selector") + Object.keys(fileData).includes("idents") && + Object.keys(fileData).includes("selectors") ) { - Object.assign(tables.idents, fileData.ident); - Object.assign(tables.selectors, fileData.selector); + Object.assign(tables.idents, fileData.idents); + Object.assign(tables.selectors, fileData.selectors); } else { // if the file doesn't have ident, it should be selector //? For backward compatibility From 88c2a54f9f1f964d09c0223ff3f3ed30f35ab439 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 30 May 2025 18:25:31 +0000 Subject: [PATCH 29/32] chore(release): 3.0.0-beta.5 [skip ci] # [3.0.0-beta.5](https://github.com/soranoo/next-css-obfuscator/compare/v3.0.0-beta.4...v3.0.0-beta.5) (2025-05-30) ### Bug Fixes * update key names in loadConversionTables for consistency ([3d6035d](https://github.com/soranoo/next-css-obfuscator/commit/3d6035d56374dbbfa74536f6c7475f3a5ff97001)) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ca340e3..05aae5a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next-css-obfuscator", - "version": "3.0.0-beta.4", + "version": "3.0.0-beta.5", "description": "A package deeply inspired by PostCSS-Obfuscator but for Next.js.", "main": "dist/index.js", "type": "commonjs", From 0d6ebebec5aa5f980a7103c0a82dea345b832be1 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Fri, 30 May 2025 21:02:04 +0100 Subject: [PATCH 30/32] deps: update css-seasoning dependency to version 1.9.0 --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5652366..6e90343 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,18 @@ { "name": "next-css-obfuscator", - "version": "3.0.0-beta.3", + "version": "3.0.0-beta.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "next-css-obfuscator", - "version": "3.0.0-beta.3", + "version": "3.0.0-beta.4", "license": "MIT", "dependencies": { "@babel/generator": "^7.23.6", "@babel/parser": "^7.23.9", "@babel/traverse": "^7.23.9", - "css-seasoning": "^1.8.0", + "css-seasoning": "^1.9.0", "html-escaper": "^3.0.3", "htmlparser2": "^10.0.0", "lightningcss-wasm": "^1.29.3", @@ -2520,9 +2520,9 @@ } }, "node_modules/css-seasoning": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/css-seasoning/-/css-seasoning-1.8.0.tgz", - "integrity": "sha512-0H/rn7xayUOCIKw1W6jEQQYYhIhk7+y94M/ouMAkMSdQI+lVYWtBdsNn2kqkfkLlYshx/ATtt6WT31T0yYYMyQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/css-seasoning/-/css-seasoning-1.9.0.tgz", + "integrity": "sha512-pj0G5jzID0VZrb1Dk+ZljDxjrGmEN2iSM3AEJ1g6pIktFmwjyUkxbxQFEmHi3/cgWOwFNfzSyGTLy85h4g9KiQ==", "dependencies": { "@deno/shim-deno": "~0.18.0", "lightningcss-wasm": "^1.29.1", diff --git a/package.json b/package.json index ca340e3..a91ce2a 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "@babel/generator": "^7.23.6", "@babel/parser": "^7.23.9", "@babel/traverse": "^7.23.9", - "css-seasoning": "^1.8.0", + "css-seasoning": "^1.9.0", "html-escaper": "^3.0.3", "htmlparser2": "^10.0.0", "lightningcss-wasm": "^1.29.3", From 7a54d901af4bdaec24ee95b2782b1afbe553e4d8 Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Sat, 31 May 2025 11:03:22 +0100 Subject: [PATCH 31/32] minor(demo): update demo to version 3.0.0-beta.5 and add type annotation in config --- .../next-css-obfuscator.config.cjs | 1 + demos/next14-app-router/package-lock.json | 456 +++++++++++++----- demos/next14-app-router/package.json | 2 +- 3 files changed, 349 insertions(+), 110 deletions(-) diff --git a/demos/next14-app-router/next-css-obfuscator.config.cjs b/demos/next14-app-router/next-css-obfuscator.config.cjs index 6267c92..14b4b20 100644 --- a/demos/next14-app-router/next-css-obfuscator.config.cjs +++ b/demos/next14-app-router/next-css-obfuscator.config.cjs @@ -1,3 +1,4 @@ +/** @type {import("next-css-obfuscator").Options} */ module.exports = { enable: true, mode: "random", // random | simplify diff --git a/demos/next14-app-router/package-lock.json b/demos/next14-app-router/package-lock.json index 329af0e..5e5ec3b 100644 --- a/demos/next14-app-router/package-lock.json +++ b/demos/next14-app-router/package-lock.json @@ -25,7 +25,7 @@ "cross-env": "^7.0.3", "eslint": "^8.54.0", "eslint-config-next": "^14.0.4", - "next-css-obfuscator": "^2.1.0-beta2", + "next-css-obfuscator": "^3.0.0-beta.5", "postcss": "^8.4.32", "postcss-cli": "^11.0.0", "prettier": "^3.1.0", @@ -55,6 +55,69 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.3.tgz", + "integrity": "sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.27.3", + "@babel/types": "^7.27.3", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.4.tgz", + "integrity": "sha512-BRmLHGwpUqLFR2jzx9orBuX/ABDkj2jLKOXrHDTN2aOKL+jFDDKaRNo9nyYsIl9h/UE/7lMKdDjKQQyxKKDZ7g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.3" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/runtime": { "version": "7.23.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.7.tgz", @@ -67,6 +130,100 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", + "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz", + "integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@deno/shim-deno": { + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/@deno/shim-deno/-/shim-deno-0.18.2.tgz", + "integrity": "sha512-oQ0CVmOio63wlhwQF75zA4ioolPvOwAoK0yuzcS5bDC1JUvH3y1GS8xPh8EOpcoDQRU4FTG8OQfxhpR+c6DrzA==", + "dev": true, + "dependencies": { + "@deno/shim-deno-test": "^0.5.0", + "which": "^4.0.0" + } + }, + "node_modules/@deno/shim-deno-test": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@deno/shim-deno-test/-/shim-deno-test-0.5.0.tgz", + "integrity": "sha512-4nMhecpGlPi0cSzT67L+Tm+GOJqvuk8gqHBziqcUQOarnuIax1z96/gJHCSIz2Z0zhxE6Rzwb3IZXPtFh51j+w==", + "dev": true + }, + "node_modules/@deno/shim-deno/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/@deno/shim-deno/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -245,47 +402,47 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1078,18 +1235,6 @@ "has-symbols": "^1.0.3" } }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true, - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, "node_modules/autoprefixer": { "version": "10.4.16", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", @@ -1465,25 +1610,18 @@ "node": ">= 8" } }, - "node_modules/css": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", - "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "source-map": "^0.6.1", - "source-map-resolve": "^0.5.2", - "urix": "^0.1.0" - } - }, - "node_modules/css-parse": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", - "integrity": "sha512-UNIFik2RgSbiTwIW1IsFwXWn6vs+bYdq83LKTSOsx7NJR7WII9dxewkHLltfTLVppoUApHV0118a4RZRI9FLwA==", + "node_modules/css-seasoning": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/css-seasoning/-/css-seasoning-1.9.0.tgz", + "integrity": "sha512-pj0G5jzID0VZrb1Dk+ZljDxjrGmEN2iSM3AEJ1g6pIktFmwjyUkxbxQFEmHi3/cgWOwFNfzSyGTLy85h4g9KiQ==", "dev": true, "dependencies": { - "css": "^2.0.0" + "@deno/shim-deno": "~0.18.0", + "lightningcss-wasm": "^1.29.1", + "xxhash-wasm": "^1.1.0" + }, + "bin": { + "css-seasoning": "bin/cli.mjs" } }, "node_modules/cssesc": { @@ -1527,15 +1665,6 @@ } } }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -1627,6 +1756,73 @@ "node": ">=6.0.0" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -1658,6 +1854,18 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", + "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-abstract": { "version": "1.22.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", @@ -2788,6 +2996,31 @@ "node": ">= 0.4" } }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "dev": true + }, + "node_modules/htmlparser2": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.1", + "entities": "^6.0.0" + } + }, "node_modules/ignore": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", @@ -3257,6 +3490,18 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -3354,6 +3599,31 @@ "node": ">= 0.8.0" } }, + "node_modules/lightningcss-wasm": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-wasm/-/lightningcss-wasm-1.30.1.tgz", + "integrity": "sha512-KJTnKEn0REV6DoJzxG0m5EKVEFA1CVE1isDYpXjsuqWXwLKFPJtA9Z9BSzPZJwAZFN2KaUzy+IWGP59p5bm2sA==", + "bundleDependencies": [ + "napi-wasm" + ], + "dev": true, + "dependencies": { + "napi-wasm": "^1.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-wasm/node_modules/napi-wasm": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -3559,12 +3829,18 @@ } }, "node_modules/next-css-obfuscator": { - "version": "2.1.0-beta2", - "resolved": "https://registry.npmjs.org/next-css-obfuscator/-/next-css-obfuscator-2.1.0-beta2.tgz", - "integrity": "sha512-w9+nNi/9dGCubqgCDOBOTNPZEKGpjl+xYnc3zjTm0ho91vTiIhFO3pvnPwAoJ0HhAsIJ+3N1nSCudctlaVo/kQ==", - "dev": true, - "dependencies": { - "css-parse": "^2.0.0", + "version": "3.0.0-beta.5", + "resolved": "https://registry.npmjs.org/next-css-obfuscator/-/next-css-obfuscator-3.0.0-beta.5.tgz", + "integrity": "sha512-HrmPrwj//A60u6r+7Nlg6WB2JdtU+z4DImiK31jtPwFatbyqRySyBCEGlUngTDeY8mEocUTobuaM+/amPM8iJw==", + "dev": true, + "dependencies": { + "@babel/generator": "^7.23.6", + "@babel/parser": "^7.23.9", + "@babel/traverse": "^7.23.9", + "css-seasoning": "^1.8.0", + "html-escaper": "^3.0.3", + "htmlparser2": "^10.0.0", + "lightningcss-wasm": "^1.29.3", "yargs": "^17.7.2" }, "bin": { @@ -3885,9 +4161,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -4481,13 +4757,6 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", - "deprecated": "https://github.com/lydell/resolve-url#deprecated", - "dev": true - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -4676,15 +4945,6 @@ "node": ">=8" } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -4693,27 +4953,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", - "dev": true, - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "node_modules/source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "deprecated": "See https://github.com/lydell/source-map-url#deprecated", - "dev": true - }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -5299,13 +5538,6 @@ "punycode": "^2.1.0" } }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", - "deprecated": "Please see https://github.com/lydell/urix#deprecated", - "dev": true - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -5503,6 +5735,12 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/xxhash-wasm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", + "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", + "dev": true + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/demos/next14-app-router/package.json b/demos/next14-app-router/package.json index e4e671a..4c77b12 100644 --- a/demos/next14-app-router/package.json +++ b/demos/next14-app-router/package.json @@ -28,7 +28,7 @@ "cross-env": "^7.0.3", "eslint": "^8.54.0", "eslint-config-next": "^14.0.4", - "next-css-obfuscator": "^2.1.0-beta2", + "next-css-obfuscator": "^3.0.0-beta.5", "postcss": "^8.4.32", "postcss-cli": "^11.0.0", "prettier": "^3.1.0", From 82e440e0095cd36783e6d392c929bce629e9139e Mon Sep 17 00:00:00 2001 From: Freeman <46896789+soranoo@users.noreply.github.com> Date: Sat, 31 May 2025 11:03:40 +0100 Subject: [PATCH 32/32] docs: remove caution note about TailwindCSS 4 support from README --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index a747b48..32beb60 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,6 @@ Project starts on 30-10-2023 [![npm version](https://img.shields.io/npm/v/next-css-obfuscator?color=red&style=flat)](https://www.npmjs.com/package/next-css-obfuscator) [![npm downloads](https://img.shields.io/npm/dt/next-css-obfuscator?color=blue&style=flat)](https://www.npmjs.com/package/next-css-obfuscator) -> [!CAUTION]\ -> This package has no support for TailwindCSS 4 yet, but is under development, and hopefully will be able to release within 1-2 months. - --- Visit the [GitHub Page](https://github.com/soranoo/next-css-obfuscator/) for better reading experience and latest docs. 😎