"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { buildDebugOptions: () => buildDebugOptions, buildParserOptions: () => buildParserOptions, buildRuntimeOptions: () => buildRuntimeOptions, buildTransformationOptions: () => buildTransformationOptions, obfuscate: () => obfuscate, startCLI: () => startCLI, stats: () => stats }); module.exports = __toCommonJS(src_exports); // package.json var version = "3.2.0"; // src/core/utils/constants.ts var PROJECT_KEBAB_CASE_NAME = "codefend"; var PROJECT_DISPLAY_NAME = "Codefend"; var OPTIONS_FILE_NAME = ".codefendrc.json"; var OPTIONS_FILE_PATH = `./${OPTIONS_FILE_NAME}`; var VERSION = version; var RC_VERSION = "0.0.1"; var DEFAULT_PROJECT_NAME = "project"; var DEFAULT_PREFIX = "Ox"; var VALID_VAR_REGEX = /^[a-zA-Z_$][a-zA-Z$0-9]*$/; var MIN_POOL_ITEM_LENGTH = 3; var HELP_COMMAND_INTRO = `Usage: ${PROJECT_KEBAB_CASE_NAME} [options] Protect Any Source Code. Options:`; var DEFAULT_CLI_OPTION = "-h"; var CLI_OPTIONS = [ { short: "-i", long: "--init", description: `Create the config file (${OPTIONS_FILE_NAME})` }, { short: "-c", long: "--check", description: "Obfuscate the project" }, { short: "-o", long: "--obfuscate", description: "Check the config file for potential warnings/errors" }, { short: "-v", long: "--version", description: "Output the version number" }, { short: "-h", long: "--help", description: "Display help for command" } ]; var DEFAULT_REGEX_LIST = [ { name: "main", regex: new RegExp("([a-zA-Z]+(_[a-zA-Z0-9]+)+)", "g") }, { name: "file", regex: new RegExp("((pkg|cmp|lib|file|folder|module|style|main)+(-[a-zA-Z0-9]+)+)", "g") } ]; var PARSER_NAMES = { default: "default", fileOnly: "fileOnly", codeOnly: "codeOnly", Parser_A: "Parser_A" }; var PARSERS = { [PARSER_NAMES.default]: { regexList: DEFAULT_REGEX_LIST }, [PARSER_NAMES.fileOnly]: { regexList: DEFAULT_REGEX_LIST.filter((e) => e.name === "file") }, [PARSER_NAMES.codeOnly]: { regexList: DEFAULT_REGEX_LIST.filter((e) => e.name === "main") }, [PARSER_NAMES.Parser_A]: { regexList: DEFAULT_REGEX_LIST } }; var DEFAULT_PARSER_NAME = PARSER_NAMES.default; var CUSTOM_PARSER_NAME = "custom"; var CODEFEND_CHECK_WARNING = { version: { code: "@meta/version-warning", message: `${OPTIONS_FILE_NAME} was generated in an older version of ${PROJECT_DISPLAY_NAME}.` }, deprecatedDefaultParser: { code: "@parser/deprecated-default-parser-warning", message: `${PARSER_NAMES.Parser_A} has been deprecated. Please rename '${PARSER_NAMES.Parser_A}' to '${DEFAULT_PARSER_NAME}' in your ${OPTIONS_FILE_NAME}. This change will not affect functionality; it's only a name update. For more information, please refer to this issue: https://github.com/Codefend/core/issues/182.` }, ignoreMissingPackageLock: { code: "@generation/ignore-missing-package-lock-warning", message: `The 'package-lock.json' entry was not found in the 'ignore' list under 'generation' in your ${OPTIONS_FILE_NAME}. It is recommended to add it to avoid potential issues.For more information, please refer to this issue: https://github.com/Codefend/core/issues/183.` } }; var CODEFEND_CHECK_ERROR = { configurationFileNotFound: { code: "@config/file-not-found-error", message: `${OPTIONS_FILE_NAME} not found. Please run ${PROJECT_KEBAB_CASE_NAME} -i first to create it.` }, configurationFileInvalidJSON: { code: "@config/invalid-json-error", message: `${OPTIONS_FILE_NAME} contains an invalid JSON format` }, transformationInvalidPrefix: { code: "@transformation/invalid-prefix-error", message: `Invalid 'prefix' in ${OPTIONS_FILE_NAME}.` }, transformationInvalidPool: { code: "@transformation/invalid-pool-error", message: "" } }; var CHECK_MAX_TABLE_COLUMN_WIDTH = 100; // src/core/options/options.ts function buildDefaultOptions(projectName) { const options = { generation: { inputDir: ".", outputDir: "codefend-output", ignore: [ "codefend-output", ".codefendrc.json", "node_modules", ".git", ".github", ".gitignore", ".vscode", "build", "dist", "README.md", "package-lock.json" ] }, transformation: { prefix: DEFAULT_PREFIX, static: [], ignore: ["node_modules"], pool: [] }, debug: { stats: true, ignoredWarnings: [] }, parser: { name: DEFAULT_PARSER_NAME }, __meta: { projectName: projectName ?? DEFAULT_PROJECT_NAME, rc: { version: RC_VERSION, generatedBy: `${PROJECT_KEBAB_CASE_NAME}@${VERSION}`, generatedAt: (/* @__PURE__ */ new Date()).toISOString() } } }; return options; } function buildTransformationOptions(transformationOptions) { return { prefix: transformationOptions?.prefix ?? DEFAULT_PREFIX, static: transformationOptions?.static ?? [], ignore: transformationOptions?.ignore ?? [], pool: buildTransformationPoolOption(transformationOptions?.pool) }; } function buildTransformationPoolOption(pool) { if (!pool) { return []; } const poolWords = typeof pool === "string" ? pool.split(" ") : pool; const parsedPoolWords = []; let parsedPoolWord = ""; for (const poolWord of poolWords) { parsedPoolWord = poolWord.replace(/([^\w\s]|_|^[0-9]+|\n)/gi, ""); if (parsedPoolWord.length >= MIN_POOL_ITEM_LENGTH) { parsedPoolWords.push(parsedPoolWord.toLowerCase()); } } return Array.from(new Set(parsedPoolWords)); } function buildParserOptions(parser) { const ret = {}; if (!parser || !parser?.name) { return { data: { name: DEFAULT_PARSER_NAME, regexList: PARSERS[DEFAULT_PARSER_NAME].regexList } }; } if (parser.name in PARSER_NAMES) { return { data: { name: parser.name, regexList: PARSERS[parser.name].regexList } }; } if (parser.name.toLowerCase() === CUSTOM_PARSER_NAME.toLowerCase()) { if (!parser?.regexList?.length) { return { error: { errorCode: "UO-0000", errorDescription: "CUSTOM Parser should have at least 1 regex item in regex list" } }; } ret.data = { name: CUSTOM_PARSER_NAME, regexList: [] }; parser.regexList.forEach((e) => { ret.data.regexList.push({ name: e.name, regex: new RegExp(e.value, "g") }); }); return ret; } if (PARSERS[parser.name] === void 0) { return { error: { errorCode: "UO-0000", errorDescription: `Parser ${parser?.name} not found` } }; } return { data: { name: parser.name, regexList: PARSERS[parser.name].regexList } }; } function buildGenerationOptions(options) { return { inputDir: options.generation.inputDir, outputDir: options.generation.outputDir, ignore: buildGenerationIgnoreOptions(options) }; } function buildGenerationIgnoreOptions(options) { const ret = options.generation.ignore ?? []; if (!ret.includes(options.generation.outputDir)) { ret.push(options.generation.outputDir); } return ret; } function buildDebugOptions(debugOptions) { return { stats: typeof debugOptions?.stats === "boolean" ? debugOptions.stats : true, ignoredWarnings: debugOptions?.ignoredWarnings === void 0 ? [] : debugOptions?.ignoredWarnings }; } // src/cli/commands/check.ts var import_cli_table3 = __toESM(require("cli-table3"), 1); // src/core/generation/read.ts var import_fs = __toESM(require("fs"), 1); var import_path = __toESM(require("path"), 1); function readFile(path3, flag = "r") { try { return import_fs.default.readFileSync(path3, { encoding: "utf8", flag }); } catch (error) { console.error(error); return null; } } function tryParse(json) { try { return JSON.parse(json); } catch (error) { console.error(error); return null; } } function getAllFileNamesInDir(dirName) { let files = []; const items = import_fs.default.readdirSync(dirName, { withFileTypes: true }); for (const item of items) { if (item.isDirectory()) { files = [...files, ...getAllFileNamesInDir(`${dirName}/${item.name}`)]; } else { files.push(`${dirName}/${item.name}`); } } return files; } function getCurrentDirectoryName() { return import_path.default.basename(import_path.default.resolve(process.cwd())); } // src/cli/commands/check.ts var RED = "\x1B[31m"; var ORANGE = "\x1B[33m"; var WHITE = "\x1B[37m"; var RESET = "\x1B[0m"; function wrapText(text, width) { const lines = []; const words = text.split(" "); let line = words.shift() || ""; for (const word of words) { if (line.length + word.length + 1 <= width) { line += " " + word; } else { lines.push(line); line = word; } } lines.push(line); return lines.join("\n"); } function checkCommand() { const checkResults = initializeCheckResults(); const file = loadOptionsFile(); if (!file) { onFileNotFoundError(checkResults); return null; } const options = parseOptions(file); if (!options) { onInvalidJsonError(checkResults); return null; } validateMetaVersion(checkResults, options); validateDeprecatedParser(checkResults, options); validateGenerationPackageLock(checkResults, options); validateTransformationPrefix(checkResults, options); validateTransformationPool(checkResults, options); const success = !checkResults.errors.length; printCheckResults(checkResults, options); return success ? options : null; } function initializeCheckResults() { return { errors: [], warnings: [] }; } function loadOptionsFile() { return readFile(OPTIONS_FILE_PATH); } function parseOptions(file) { return tryParse(file); } function onFileNotFoundError(checkResults) { checkResults.errors.push(CODEFEND_CHECK_ERROR.configurationFileNotFound); printCheckResults(checkResults, null); } function onInvalidJsonError(checkResults) { checkResults.errors.push(CODEFEND_CHECK_ERROR.configurationFileInvalidJSON); printCheckResults(checkResults, null); } function validateMetaVersion(checkResults, options) { if (options.debug?.ignoredWarnings?.includes(CODEFEND_CHECK_WARNING.version.code)) { return; } if (options.__meta?.rc?.version && options.__meta.rc.version !== RC_VERSION) { checkResults.warnings.push(CODEFEND_CHECK_WARNING.version); } } function validateGenerationPackageLock(checkResults, options) { if (options.generation?.ignore == null) { return; } if (options.debug?.ignoredWarnings?.includes(CODEFEND_CHECK_WARNING.ignoreMissingPackageLock.code)) { return; } if (!options.generation?.ignore?.includes("package-lock.json")) { checkResults.warnings.push(CODEFEND_CHECK_WARNING.ignoreMissingPackageLock); } } function validateDeprecatedParser(checkResults, options) { if (options.debug?.ignoredWarnings?.includes(CODEFEND_CHECK_WARNING.deprecatedDefaultParser.code)) { return; } if (options.parser?.name === PARSER_NAMES.Parser_A) { checkResults.warnings.push(CODEFEND_CHECK_WARNING.deprecatedDefaultParser); } } function validateTransformationPrefix(checkResults, options) { if (!VALID_VAR_REGEX.test(options.transformation?.prefix ?? DEFAULT_PREFIX)) { checkResults.errors.push(CODEFEND_CHECK_ERROR.transformationInvalidPrefix); } } function validateTransformationPool(checkResults, options) { if (!options.transformation?.pool) { return; } const poolArray = typeof options.transformation.pool === "string" ? options.transformation.pool.split(" ") : options.transformation.pool; const poolSet = new Set(poolArray); poolSet.forEach((word) => { if (!VALID_VAR_REGEX.test(word)) { checkResults.errors.push({ code: CODEFEND_CHECK_ERROR.transformationInvalidPool.code, message: `Invalid 'pool' in ${OPTIONS_FILE_NAME}. word:"${word}"` }); } }); } function printCheckResults(checkResults, options) { const hasIgnoredAllWarnings = options?.debug?.ignoredWarnings == "all"; const errorRows = checkResults.errors.map((error) => [ `${RED}${error.code}${RESET}`, wrapText(`${WHITE}${error.message}${RESET}`, CHECK_MAX_TABLE_COLUMN_WIDTH) ]); const warningRows = !hasIgnoredAllWarnings ? checkResults.warnings.map((warning) => [ `${ORANGE}${warning.code}${RESET}`, wrapText(`${WHITE}${warning.message}${RESET}`, CHECK_MAX_TABLE_COLUMN_WIDTH) ]) : []; const errorTable = new import_cli_table3.default({ head: ["Error Code", "Error Message"], colWidths: [20, CHECK_MAX_TABLE_COLUMN_WIDTH], style: { "padding-left": 1, "padding-right": 1 } }); const warningTable = new import_cli_table3.default({ head: [`${ORANGE}Warning Code${RESET}`, `${ORANGE}Warning Message${RESET}`], colWidths: [20, CHECK_MAX_TABLE_COLUMN_WIDTH], style: { "padding-left": 1, "padding-right": 1 } }); errorRows.forEach((row) => errorTable.push(row)); warningRows.forEach((row) => warningTable.push(row)); const message = `Check completed. ${checkResults.errors.length} error(s) ${hasIgnoredAllWarnings ? 0 : checkResults.warnings.length} warning(s) `; console.warn(message); if (checkResults.errors.length > 0) { console.warn(errorTable.toString()); } if (!hasIgnoredAllWarnings && checkResults.warnings.length > 0) { console.warn(warningTable.toString()); console.warn(printIgnoreWarningSentence("N/A")); } } function printIgnoreWarningSentence(warningCode) { return `To turn off this warning, add '${warningCode}' to the 'ignoredWarnings' list or set 'ignoredWarnings':'all' in your ${OPTIONS_FILE_NAME}. For more information, visit this link: https://codefend.github.io/docs/references/configuration#debug.`; } // src/cli/commands/help.ts function helpCommand() { const longest = Math.max(...CLI_OPTIONS.map((el) => el.long.length)); const offset = 2; let ret = HELP_COMMAND_INTRO; CLI_OPTIONS.forEach((e) => { ret += ` ${e.short}, ${e.long.padEnd(longest + offset, " ")}${e.description}`; }); console.log(ret); } // src/core/generation/write.ts var import_fs2 = __toESM(require("fs"), 1); function writeFile(path3, data) { import_fs2.default.writeFileSync(path3, data); } // src/cli/commands/init.ts function initCommand() { const options = buildDefaultOptions(getCurrentDirectoryName()); writeFile(OPTIONS_FILE_PATH, JSON.stringify(options, null, 4)); console.log(`${OPTIONS_FILE_NAME} created.`); } // src/core/process/runtime.ts function buildRuntimeOptions() { return { map: {}, processed: { map: {} } }; } // src/core/debug/stats.ts var import_cli_table32 = __toESM(require("cli-table3"), 1); function stats(options, runtimeOptions) { if (!options.stats) { return; } const table = new import_cli_table32.default({ head: ["Transformation", "From", "To", "Count"], colWidths: [17, 35, 35, 10] }); for (const key in runtimeOptions.processed.map) { const item = runtimeOptions.processed.map[key]; const count = item?.count || 0; switch (item?.type) { case 1 /* ignore */: table.push(["Ignored", key, `${key}`, count]); break; case 2 /* static */: table.push(["Static", key, `${item.target}`, count]); break; case 3 /* pool */: table.push(["Pool", key, `${item.target}`, count]); break; case 0 /* default */: case null: case void 0: table.push(["Prefix", key, `${item?.target || ""}`, count]); break; default: console.warn(`Unknown type for key ${key}`); } } console.log("Obfuscation stats:"); console.log(table.toString()); console.log(""); } function getObfuscatedWordsCount(runtimeOptions) { let ret = Object.keys(runtimeOptions.processed.map).length; for (const key in runtimeOptions.processed.map) { if (runtimeOptions.processed.map[key].count == 0) { ret--; } } return ret; } // src/core/generation/copy.ts var import_fs3 = __toESM(require("fs"), 1); var import_path2 = __toESM(require("path"), 1); // src/core/mapper/mapper.ts function buildMap(options, runtimeOptions) { options.words.forEach((word) => { if (runtimeOptions.map[word.value]) { runtimeOptions.processed.map[word.value].count++; return; } runtimeOptions.map[word.value] = ""; runtimeOptions.processed.map[word.value] = { count: 1, target: "", type: 0 /* default */ }; }); } function sortMap(runtimeOptions) { runtimeOptions.map = Object.keys(runtimeOptions.map).sort((a, b) => { return b.length - a.length; }).reduce((obj, key) => { obj[key] = runtimeOptions.map[key] ?? ""; return obj; }, {}); } // src/core/parser/parser.ts function parse(options) { const words = []; let matches; options.parserOptions.regexList.forEach((regexListOption) => { matches = options.code.match(regexListOption.regex); if (!matches) return; matches.forEach((word) => { words.push({ value: word, fromRegex: regexListOption.name }); }); }); return words; } // src/core/replacer/replacer.ts function replace(options, runtimeOptions) { const words = Object.keys(runtimeOptions.map); if (!words.length) return options.code; const regex = new RegExp(words.join("|"), "gi"); return options.code.replace(regex, (matched) => runtimeOptions.map[matched] ?? ""); } // src/core/transformation/default.ts function mapDefaultWords(options, runtimeOptions) { let sequence = 0; let word; for (const key in runtimeOptions.processed.map) { if (runtimeOptions.processed.map[key].type == 0 /* default */) { word = generateWord(sequence, options); if (!word) continue; runtimeOptions.map[key] = word; runtimeOptions.processed.map[key].target = word; sequence++; } } } function generateWord(sequence, options) { return `${options.prefix}${sequence}`; } // src/core/transformation/ignore.ts function mapIgnoredWords(options, runtimeOptions) { options.ignore.forEach((word) => { runtimeOptions.map[word] = word; if (word in runtimeOptions.processed.map) { runtimeOptions.processed.map[word].type = 1 /* ignore */; } else { runtimeOptions.processed.map[word] = { count: 0, target: word, type: 1 /* ignore */ }; } }); } // src/core/transformation/pool.ts function mapPoolWords(options, runtimeOptions) { let sequence = 0; let word; for (const key in runtimeOptions.processed.map) { if (runtimeOptions.processed.map[key].type == 0 /* default */) { word = generateWord2(sequence, options); if (!word) continue; runtimeOptions.map[key] = word; runtimeOptions.processed.map[key].target = word; runtimeOptions.processed.map[key].type = 3 /* pool */; sequence++; } } } function generateWord2(sequence, options) { if (options.pool.length <= sequence) { return; } let index = 0; for (const entry of options.pool) { if (index === sequence) return entry; index++; } } // src/core/transformation/static.ts function mapStaticWords(options, runtimeOptions) { options.static.forEach((staticWord) => { runtimeOptions.map[staticWord.from] = staticWord.to; const element = runtimeOptions.processed.map[staticWord.from]; if (element) { element.target = staticWord.to; element.type = 2 /* static */; } else { runtimeOptions.processed.map[staticWord.from] = { count: 0, target: staticWord.to, type: 2 /* static */ }; } }); } // src/core/process/obfuscate.ts function obfuscate(code, transformationOptions, parserOptions, runtimeOptions) { const words = parse({ code, parserOptions }); buildMap({ words }, runtimeOptions); sortMap(runtimeOptions); mapStaticWords({ static: transformationOptions.static }, runtimeOptions); mapIgnoredWords({ ignore: transformationOptions.ignore }, runtimeOptions); mapPoolWords({ prefix: transformationOptions.prefix, pool: transformationOptions.pool }, runtimeOptions); mapDefaultWords({ prefix: transformationOptions.prefix }, runtimeOptions); return replace({ code }, runtimeOptions); } // src/core/generation/copy.ts function copyFolder(from, to, ignoredFilesInGeneration, transformationOptions, parserOptions, runtimeOptions) { if (!import_fs3.default.existsSync(to)) import_fs3.default.mkdirSync(to); import_fs3.default.readdirSync(from).forEach((element) => { if (ignoredFilesInGeneration.includes(element)) return; if (import_fs3.default.lstatSync(import_path2.default.join(from, element)).isFile()) { import_fs3.default.copyFileSync( import_path2.default.join(from, element), import_path2.default.join( obfuscate(to, transformationOptions, parserOptions, runtimeOptions), obfuscate(element, transformationOptions, parserOptions, runtimeOptions) ) ); } else { copyFolder( import_path2.default.join(from, element), import_path2.default.join( obfuscate(to, transformationOptions, parserOptions, runtimeOptions), obfuscate(element, transformationOptions, parserOptions, runtimeOptions) ), ignoredFilesInGeneration, transformationOptions, parserOptions, runtimeOptions ); } }); } // src/core/generation/remove.ts var import_fs4 = __toESM(require("fs"), 1); function removeFolder(path3) { import_fs4.default.rmSync(path3, { recursive: true, force: true }); } // src/cli/commands/obfuscate.ts function obfuscateCommand(options) { if (!options) { console.warn("Obfuscation process canceled due to unresolved errors."); return; } const generationOptions = buildGenerationOptions(options); const transformationOptions = buildTransformationOptions(options.transformation); const parserOptionsResponse = buildParserOptions(options.parser); if (parserOptionsResponse.error) { console.warn(parserOptionsResponse.error.errorDescription); return; } const parserOptions = parserOptionsResponse.data; const debugOptions = buildDebugOptions(options.debug); const runtimeOptions = buildRuntimeOptions(); removeFolder(generationOptions.outputDir); copyFolder( generationOptions.inputDir, generationOptions.outputDir, generationOptions.ignore, transformationOptions, parserOptions, runtimeOptions ); const fileNames = getAllFileNamesInDir(generationOptions.outputDir); console.warn(`Obfuscated ${fileNames.length} file(s) `); let fileCode; fileNames.forEach((fileName) => { fileCode = readFile(fileName); writeFile(fileName, obfuscate(fileCode ?? "", transformationOptions, parserOptions, runtimeOptions)); }); stats({ stats: debugOptions.stats }, runtimeOptions); console.warn(`Obfuscated ${getObfuscatedWordsCount(runtimeOptions)} word(s)`); console.warn(`Obfuscation completed.`); } // src/cli/commands/unknown.ts function unknownCommand(option) { console.log(`error: unknown option '${option}'`); } // src/cli/commands/version.ts function versionCommand() { console.log(VERSION); } // src/cli/index.ts function startCLI() { const inputArgs = process.argv.splice(2); const selectedArg = inputArgs.length > 0 ? inputArgs[0] ?? DEFAULT_CLI_OPTION : DEFAULT_CLI_OPTION; const option = CLI_OPTIONS.find((e) => [e.short, e.long].includes(selectedArg)); switch (option?.short) { case "-h": helpCommand(); break; case "-v": versionCommand(); break; case "-i": initCommand(); break; case "-c": checkCommand(); break; case "-o": console.warn(`${PROJECT_DISPLAY_NAME} v${VERSION}`); obfuscateCommand(checkCommand()); break; default: unknownCommand(selectedArg); break; } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { buildDebugOptions, buildParserOptions, buildRuntimeOptions, buildTransformationOptions, obfuscate, startCLI, stats }); //# sourceMappingURL=index.cjs.map