esbuild-javascript-obfuscator 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2026 Patchix
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # esbuild-javascript-obfuscator
2
+
3
+ This project obfuscates your code using the javascript-obfuscator NPM package.
4
+
5
+ This completely serves a plugin for esbuild.
6
+
7
+ Licensed with MIT.
@@ -0,0 +1,3 @@
1
+ import { JSObfuscatorPlugin } from './plugin/plugin';
2
+ export { JSObfuscatorPlugin };
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import { JSObfuscatorPlugin } from './plugin/plugin';
2
+ export { JSObfuscatorPlugin };
@@ -0,0 +1,3 @@
1
+ import type { JSObfuscatorOptions } from './types/JSObfuscatorOptions';
2
+ export declare function CreateLogger(pluginOptions: JSObfuscatorOptions): (...data: any[]) => void;
3
+ //# sourceMappingURL=logging.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../src/logging.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAEvE,wBAAgB,YAAY,CAAC,aAAa,EAAE,mBAAmB,aAEhC,GAAG,EAAE,UAInC"}
@@ -0,0 +1,6 @@
1
+ export function CreateLogger(pluginOptions) {
2
+ if (!pluginOptions.VerboseLogging) {
3
+ return function log(...data) { };
4
+ }
5
+ return console.log.bind(null, '[esbuild-javascript-obfuscator]');
6
+ }
@@ -0,0 +1,8 @@
1
+ import type { JSObfuscatorOptions } from '../types/JSObfuscatorOptions';
2
+ /**
3
+ * Validates a given JSObfuscatorOptions object.
4
+ * @param options The options to validate
5
+ * @returns Array where first element is if it's OK or not, and the second being an optional message
6
+ */
7
+ export declare function ValidateOptions(options: JSObfuscatorOptions): [boolean, string?];
8
+ //# sourceMappingURL=optionValidator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optionValidator.d.ts","sourceRoot":"","sources":["../../src/plugin/optionValidator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAWxE;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,mBAAmB,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CA+ChF"}
@@ -0,0 +1,43 @@
1
+ import assert from 'node:assert';
2
+ /**
3
+ * Checks if a value is an object.
4
+ * @param value The value to check
5
+ * @returns True if the value is an object, false otherwise
6
+ */
7
+ function IsObject(value) {
8
+ return typeof value === 'object' && value !== null;
9
+ }
10
+ /**
11
+ * Validates a given JSObfuscatorOptions object.
12
+ * @param options The options to validate
13
+ * @returns Array where first element is if it's OK or not, and the second being an optional message
14
+ */
15
+ export function ValidateOptions(options) {
16
+ try {
17
+ /* Simple Type Checks */
18
+ assert(IsObject(options), 'Options must be an object');
19
+ // - VMProtection
20
+ assert(IsObject(options.VMProtection), 'VMProtection must be an object');
21
+ assert(typeof options.VMProtection.Enabled === 'boolean', 'VMProtection.Enabled must be a boolean');
22
+ if (options.VMProtection.Enabled) {
23
+ assert(typeof options.VMProtection.ApiKey === 'string', 'VMProtection.ApiKey must be a string when VMProtection.Enabled is true');
24
+ }
25
+ // - ObfuscatorOptions
26
+ assert(IsObject(options.ObfuscatorOptions), 'ObfuscatorOptions must be an object');
27
+ // - VerboseLogging
28
+ assert(typeof options.VerboseLogging === 'boolean', 'VerboseLogging must be a boolean');
29
+ // - ObfuscateAllFiles
30
+ if (options.ObfuscateAllFiles !== undefined) {
31
+ assert(typeof options.ObfuscateAllFiles === 'boolean', 'ObfuscateAllFiles must be a boolean if provided');
32
+ }
33
+ // - ObfuscateFilesWhitelist
34
+ if (options.ObfuscateFilesWhitelist !== undefined) {
35
+ assert(Array.isArray(options.ObfuscateFilesWhitelist), 'ObfuscateFilesWhitelist must be an array if provided');
36
+ assert(options.ObfuscateFilesWhitelist.every((file) => typeof file === 'string'), 'ObfuscateFilesWhitelist must be an array of strings if provided');
37
+ }
38
+ }
39
+ catch (err) {
40
+ return [false, `Assertion error: ${err.message}`];
41
+ }
42
+ return [true];
43
+ }
@@ -0,0 +1,4 @@
1
+ import * as esbuild from 'esbuild';
2
+ import type { JSObfuscatorOptions } from '../types/JSObfuscatorOptions';
3
+ export declare function JSObfuscatorPlugin(options: JSObfuscatorOptions): esbuild.Plugin;
4
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/plugin/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAMnC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAwDxE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GA+DzD,OAAO,CAAC,MAAM,CACnB"}
@@ -0,0 +1,86 @@
1
+ import * as esbuild from 'esbuild';
2
+ import * as JsObf from 'javascript-obfuscator';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { ValidateOptions } from './optionValidator';
6
+ import { CreateLogger } from '../logging';
7
+ async function ObfuscateFile(pluginOptions, finalized, file) {
8
+ const logger = CreateLogger(pluginOptions);
9
+ if (!file.path.endsWith('.js')) {
10
+ logger('file does not end with .js, skipping obfuscation: ', file.path);
11
+ return false;
12
+ }
13
+ if (pluginOptions.VMProtection.Enabled) {
14
+ const { ApiKey, Version } = pluginOptions.VMProtection;
15
+ logger('obfuscating with VM Protection ... ');
16
+ const proApiOptions = {
17
+ apiToken: ApiKey /* must exist because of optionValidator */,
18
+ };
19
+ if (Version) {
20
+ Reflect.set(proApiOptions, 'version', Version);
21
+ }
22
+ const obfuscateResult = await JsObf.obfuscatePro(new TextDecoder().decode(file.contents), pluginOptions.ObfuscatorOptions, proApiOptions, logger);
23
+ finalized.push({ fileName: file.path, outputCode: obfuscateResult.getObfuscatedCode().toString() });
24
+ logger('obfuscation with VM Protection completed for file: ', file.path);
25
+ return true;
26
+ }
27
+ logger('obfusacting with no VM protection...');
28
+ const obfuscateResult = JsObf.obfuscate(new TextDecoder().decode(file.contents), pluginOptions.ObfuscatorOptions);
29
+ finalized.push({ fileName: file.path, outputCode: obfuscateResult.getObfuscatedCode().toString() });
30
+ console.log(finalized);
31
+ logger('obfuscation completed for file: ', file.path);
32
+ return true;
33
+ }
34
+ export function JSObfuscatorPlugin(options) {
35
+ const log = CreateLogger(options);
36
+ const finalized = [];
37
+ log('JSObfuscatorPlugin initialized with options:', options);
38
+ const [isValid, errorMsg] = ValidateOptions(options);
39
+ log('isValid: ', isValid, ' errorMsg: ', errorMsg);
40
+ if (!isValid) {
41
+ throw new Error(`Invalid JSObfuscatorPlugin options: ${errorMsg || 'no error message provided'}`);
42
+ }
43
+ log('creating plugin');
44
+ const obfuscateFile = ObfuscateFile.bind(null, options, finalized);
45
+ return {
46
+ name: 'esbuild-javascript-obfuscator',
47
+ setup(build) {
48
+ if (build.initialOptions.write) {
49
+ throw new Error('esbuild-javascript-obfuscator plugin requires write: false in build options');
50
+ }
51
+ build.onEnd(async ({ errors, outputFiles }) => {
52
+ if (errors.length) {
53
+ log('build completed with errors, skipping obfuscation (errors: %f)', errors.length);
54
+ return;
55
+ }
56
+ if (!outputFiles) {
57
+ log('No output files found, skipping obfuscation');
58
+ return;
59
+ }
60
+ if (outputFiles.length > 1 && !options.ObfuscateAllFiles) {
61
+ throw new Error('outputFiles.length > 1 but ObfuscateAllFiles not explicitly set');
62
+ }
63
+ if (outputFiles.length > 1 && options.ObfuscateAllFiles) {
64
+ for (const file of outputFiles) {
65
+ const success = await obfuscateFile(file);
66
+ if (!success) {
67
+ log('failed to obfuscate file: ', file.path);
68
+ }
69
+ }
70
+ }
71
+ else {
72
+ const file = outputFiles[0];
73
+ const success = await obfuscateFile(file);
74
+ if (!success) {
75
+ log('failed to obfuscate file: ', file.path);
76
+ }
77
+ }
78
+ /* write pass */
79
+ for (const outputFile of finalized) {
80
+ fs.mkdirSync(path.dirname(outputFile.fileName), { recursive: true });
81
+ fs.writeFileSync(outputFile.fileName, outputFile.outputCode);
82
+ }
83
+ });
84
+ },
85
+ };
86
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=optionValidator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optionValidator.test.d.ts","sourceRoot":"","sources":["../../src/tests/optionValidator.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,93 @@
1
+ import { test, expect } from 'bun:test';
2
+ import { ValidateOptions } from '../plugin/optionValidator';
3
+ const fakeObject = null;
4
+ test('return false if not an object', () => {
5
+ const [isValid, errorMsg] = ValidateOptions(fakeObject);
6
+ expect(isValid).toBe(false);
7
+ expect(errorMsg).toContain('must be an object');
8
+ });
9
+ test('return false if VMProtection is not an object', () => {
10
+ const [isValid, errorMsg] = ValidateOptions({
11
+ VMProtection: null,
12
+ ObfuscatorOptions: {},
13
+ VerboseLogging: true,
14
+ });
15
+ expect(isValid).toBe(false);
16
+ expect(errorMsg).toContain('VMProtection must be an object');
17
+ });
18
+ test('return false if VMProtection.Enabled is not a boolean', () => {
19
+ const [isValid, errorMsg] = ValidateOptions({
20
+ VMProtection: { Enabled: {} },
21
+ ObfuscatorOptions: {},
22
+ VerboseLogging: true,
23
+ });
24
+ expect(isValid).toBe(false);
25
+ expect(errorMsg).toContain('VMProtection.Enabled must be a boolean');
26
+ });
27
+ test('return false if VMProtection.Enabled is true but ApiKey is not a string', () => {
28
+ const [isValid, errorMsg] = ValidateOptions({
29
+ VMProtection: { Enabled: true, ApiKey: 123 },
30
+ ObfuscatorOptions: {},
31
+ VerboseLogging: true,
32
+ });
33
+ expect(isValid).toBe(false);
34
+ expect(errorMsg).toContain('VMProtection.ApiKey must be a string when VMProtection.Enabled is true');
35
+ });
36
+ test('return false if ObfuscatorOptions is not an object', () => {
37
+ const [isValid, errorMsg] = ValidateOptions({
38
+ VMProtection: { Enabled: false },
39
+ ObfuscatorOptions: null,
40
+ VerboseLogging: true,
41
+ });
42
+ expect(isValid).toBe(false);
43
+ expect(errorMsg).toContain('ObfuscatorOptions must be an object');
44
+ });
45
+ test('return false if VerboseLogging is not a boolean', () => {
46
+ const [isValid, errorMsg] = ValidateOptions({
47
+ VMProtection: { Enabled: false },
48
+ ObfuscatorOptions: {},
49
+ VerboseLogging: 'true',
50
+ });
51
+ expect(isValid).toBe(false);
52
+ expect(errorMsg).toContain('VerboseLogging must be a boolean');
53
+ });
54
+ test('return false if ObfuscateAllFiles is provided but not a boolean', () => {
55
+ const [isValid, errorMsg] = ValidateOptions({
56
+ VMProtection: { Enabled: false },
57
+ ObfuscatorOptions: {},
58
+ VerboseLogging: true,
59
+ ObfuscateAllFiles: 'true',
60
+ });
61
+ expect(isValid).toBe(false);
62
+ expect(errorMsg).toContain('ObfuscateAllFiles must be a boolean if provided');
63
+ });
64
+ test('return false if ObfuscateFilesWhitelist is provided but not an array', () => {
65
+ const [isValid, errorMsg] = ValidateOptions({
66
+ VMProtection: { Enabled: false },
67
+ ObfuscatorOptions: {},
68
+ VerboseLogging: true,
69
+ ObfuscateFilesWhitelist: 'file.js',
70
+ });
71
+ expect(isValid).toBe(false);
72
+ expect(errorMsg).toContain('ObfuscateFilesWhitelist must be an array if provided');
73
+ });
74
+ test('return false if ObfuscateFilesWhitelist is provided but not an array of strings', () => {
75
+ const [isValid, errorMsg] = ValidateOptions({
76
+ VMProtection: { Enabled: false },
77
+ ObfuscatorOptions: {},
78
+ VerboseLogging: true,
79
+ ObfuscateFilesWhitelist: ['file.js', 123],
80
+ });
81
+ expect(isValid).toBe(false);
82
+ expect(errorMsg).toContain('ObfuscateFilesWhitelist must be an array of strings');
83
+ });
84
+ test('return true if all options are valid', () => {
85
+ const [isValid, errorMsg] = ValidateOptions({
86
+ VMProtection: { Enabled: false },
87
+ ObfuscatorOptions: {},
88
+ VerboseLogging: true,
89
+ ObfuscateAllFiles: true,
90
+ });
91
+ expect(isValid).toBe(true);
92
+ expect(errorMsg).toBeUndefined();
93
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=plugin.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.test.d.ts","sourceRoot":"","sources":["../../src/tests/plugin.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,50 @@
1
+ import { test, expect } from 'bun:test';
2
+ import esbuild from 'esbuild';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { JSObfuscatorPlugin } from '../plugin/plugin';
6
+ const ObfuscationOptions = {
7
+ VMProtection: {
8
+ Enabled: false,
9
+ },
10
+ ObfuscatorOptions: {
11
+ compact: true,
12
+ stringArray: true,
13
+ },
14
+ VerboseLogging: false,
15
+ ObfuscateAllFiles: false,
16
+ };
17
+ function CreateDummyFiles() {
18
+ fs.mkdirSync(path.join(__dirname, 'temp'));
19
+ fs.writeFileSync(path.join(__dirname, 'temp', 'index.ts'), `
20
+ console.log("Hello World")
21
+ `);
22
+ return function cleanup() {
23
+ fs.rmSync(path.join(__dirname, 'temp'), { recursive: true, force: true });
24
+ };
25
+ }
26
+ test('plugin should throw error when write: true', async () => {
27
+ const cleanup = CreateDummyFiles();
28
+ expect(esbuild.build({
29
+ entryPoints: [path.join(__dirname, 'temp', 'index.ts')],
30
+ bundle: true,
31
+ write: true,
32
+ minify: true,
33
+ plugins: [JSObfuscatorPlugin(ObfuscationOptions)],
34
+ })).rejects.toThrow(`esbuild-javascript-obfuscator plugin requires write: false in build options`);
35
+ cleanup();
36
+ });
37
+ test('plugin should successfully obfuscate a single file', async () => {
38
+ const cleanup = CreateDummyFiles();
39
+ await esbuild.build({
40
+ entryPoints: [path.join(__dirname, 'temp', 'index.ts')],
41
+ bundle: true,
42
+ outfile: path.join(__dirname, 'temp', 'out.js'),
43
+ write: false,
44
+ minify: true,
45
+ plugins: [JSObfuscatorPlugin(ObfuscationOptions)],
46
+ });
47
+ const obfuscatedCode = fs.readFileSync(path.join(__dirname, 'temp', 'out.js'), 'utf-8');
48
+ cleanup();
49
+ expect(obfuscatedCode).toContain('parseInt');
50
+ });
@@ -0,0 +1,51 @@
1
+ import JsObf from 'javascript-obfuscator';
2
+ export interface JSObfuscatorOptions {
3
+ /**
4
+ * The options for VM protection. If Enabled is true, ApiKey must be provided.
5
+ */
6
+ VMProtection: {
7
+ /**
8
+ * Whether you would like to enable VM protection or not. (requires subscription)
9
+ */
10
+ Enabled: boolean;
11
+ /**
12
+ * The API key from your obfuscator.io dashboard. Visit API Keys link to get your API key.
13
+ *
14
+ * This must be provided if VMProtection.Enabled is true.
15
+ *
16
+ * Links:
17
+ * - [API Keys](https://obfuscator.io/dashboard/settings/api-keys)
18
+ */
19
+ ApiKey?: string;
20
+ /**
21
+ * The version of the VM protection to use. If not provided, the latest version will be used.
22
+ */
23
+ Version?: string;
24
+ };
25
+ /**
26
+ * The options for the JavaScript obfuscator.
27
+ *
28
+ * You can go to the dashboard, press the braces button (to the left of the preset option) and export the options as JSON and paste it here (requires subscription).
29
+ *
30
+ * Links:
31
+ * - [Dashboard](https://obfuscator.io/dashboard)
32
+ */
33
+ ObfuscatorOptions: JsObf.ObfuscatorOptions;
34
+ /**
35
+ * Whether you would like to enable verbose logging or not.
36
+ */
37
+ VerboseLogging: boolean;
38
+ /**
39
+ * Whether you would like to obfuscate every single output file (provided it is .js).
40
+ *
41
+ * If this is not explicitly provided and there are multiple output files, the plugin will error.
42
+ */
43
+ ObfuscateAllFiles?: boolean;
44
+ /**
45
+ * An optional whitelist of files to obfuscate.
46
+ *
47
+ * This must be provided if `ObfuscateAllFiles` is truthy and must be an array of filenames.
48
+ */
49
+ ObfuscateFilesWhitelist?: string[];
50
+ }
51
+ //# sourceMappingURL=JSObfuscatorOptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JSObfuscatorOptions.d.ts","sourceRoot":"","sources":["../../src/types/JSObfuscatorOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,uBAAuB,CAAC;AAE1C,MAAM,WAAW,mBAAmB;IACnC;;OAEG;IACH,YAAY,EAAE;QACb;;WAEG;QACH,OAAO,EAAE,OAAO,CAAC;QAEjB;;;;;;;WAOG;QACH,MAAM,CAAC,EAAE,MAAM,CAAC;QAEhB;;WAEG;QACH,OAAO,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IAEF;;;;;;;OAOG;IACH,iBAAiB,EAAE,KAAK,CAAC,iBAAiB,CAAC;IAE3C;;OAEG;IACH,cAAc,EAAE,OAAO,CAAC;IAExB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC;CACnC"}
@@ -0,0 +1 @@
1
+ import JsObf from 'javascript-obfuscator';
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "esbuild-javascript-obfuscator",
3
+ "version": "1.0.0",
4
+ "description": "An esbuild plugin to obfuscate JavaScript code.",
5
+ "author": {
6
+ "name": "Patchix"
7
+ },
8
+ "license": "MIT",
9
+ "keywords": [
10
+ "esbuild",
11
+ "esbuild-plugin",
12
+ "javascript-obfuscator",
13
+ "obfuscator"
14
+ ],
15
+ "type": "module",
16
+ "main": "./dist/index.js",
17
+ "module": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "scripts": {
23
+ "clean": "rm -rf dist",
24
+ "build:js": "bun build ./src/index.ts --outdir ./dist --target node",
25
+ "build:types": "tsc",
26
+ "build": "bun run clean && bun run build:js && bun run build:types",
27
+ "prettier": "prettier --write .",
28
+ "prepublishOnly": "bun run build"
29
+ },
30
+ "dependencies": {
31
+ "esbuild": "^0.28.1",
32
+ "javascript-obfuscator": "^5.4.5"
33
+ },
34
+ "devDependencies": {
35
+ "@types/bun": "latest",
36
+ "prettier": "^3.9.4"
37
+ },
38
+ "peerDependencies": {
39
+ "typescript": "^5"
40
+ }
41
+ }