luau-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.
@@ -0,0 +1,143 @@
1
+ import type {
2
+ Identifier, StringLiteral, NumberLiteral, BinaryExpression, UnaryExpression,
3
+ CallExpression, MemberExpression, IndexExpression,
4
+ ParenthesizedExpression, Expression, TableExpression, TableField,
5
+ LocalStatement, LocalFunctionStatement, FunctionBody, FunctionParameter,
6
+ TypedIdentifier, Block, Statement, AssignmentStatement, NumericForStatement,
7
+ ReturnStatement, DoStatement, IfStatement, IfClause, VarargExpression,
8
+ FunctionExpression,
9
+ } from "luau-parser"
10
+
11
+ /** 합성 노드는 실제 소스 위치가 없으므로 0으로 채움(printer는 구조만 봄). */
12
+ const POS = { start: 0, end: 0 }
13
+
14
+ export function identifier(name: string): Identifier {
15
+ return { type: "Identifier", name, line: POS, column: POS }
16
+ }
17
+
18
+ export function typedIdentifier(name: string): TypedIdentifier {
19
+ return { type: "TypedIdentifier", name, line: POS, column: POS }
20
+ }
21
+
22
+ export function stringLiteral(value: string): StringLiteral {
23
+ return { type: "StringLiteral", value, raw: JSON.stringify(value), line: POS, column: POS }
24
+ }
25
+
26
+ export function numberLiteral(value: number): NumberLiteral {
27
+ return { type: "NumberLiteral", value, raw: String(value), line: POS, column: POS }
28
+ }
29
+
30
+ export function binary(
31
+ operator: BinaryExpression["operator"],
32
+ left: Expression,
33
+ right: Expression,
34
+ ): BinaryExpression {
35
+ return { type: "BinaryExpression", operator, left, right, line: POS, column: POS }
36
+ }
37
+
38
+ export function unary(operator: UnaryExpression["operator"], argument: Expression): UnaryExpression {
39
+ return { type: "UnaryExpression", operator, argument, line: POS, column: POS }
40
+ }
41
+
42
+ export function call(callee: Expression, args: Expression[]): CallExpression {
43
+ return { type: "CallExpression", callee, arguments: args, line: POS, column: POS }
44
+ }
45
+
46
+ export function member(object: Expression, property: string): MemberExpression {
47
+ return { type: "MemberExpression", object, property: identifier(property), line: POS, column: POS }
48
+ }
49
+
50
+ export function index(object: Expression, key: Expression): IndexExpression {
51
+ return { type: "IndexExpression", object, index: key, line: POS, column: POS }
52
+ }
53
+
54
+ export function paren(expression: Expression): ParenthesizedExpression {
55
+ return { type: "ParenthesizedExpression", expression, line: POS, column: POS }
56
+ }
57
+
58
+ export function table(fields: TableField[]): TableExpression {
59
+ return { type: "TableExpression", fields, line: POS, column: POS }
60
+ }
61
+
62
+ export function computedField(key: Expression, value: Expression): TableField {
63
+ return { type: "TableFieldComputed", key, value }
64
+ }
65
+
66
+ export function positionalField(value: Expression): TableField {
67
+ return { type: "TableFieldPositional", value }
68
+ }
69
+
70
+ export function localStatement(name: string, init: Expression): LocalStatement {
71
+ return {
72
+ type: "LocalStatement",
73
+ names: [typedIdentifier(name)],
74
+ init: [init],
75
+ line: POS,
76
+ column: POS,
77
+ }
78
+ }
79
+
80
+ export function block(statements: Statement[]): Block {
81
+ return { type: "Block", statements, line: POS, column: POS }
82
+ }
83
+
84
+ export function assignmentStatement(targets: Expression[], values: Expression[]): AssignmentStatement {
85
+ return { type: "AssignmentStatement", targets, values, line: POS, column: POS }
86
+ }
87
+
88
+ export function returnStatement(args: Expression[]): ReturnStatement {
89
+ return { type: "ReturnStatement", arguments: args, line: POS, column: POS }
90
+ }
91
+
92
+ export function numericForStatement(
93
+ variableName: string,
94
+ start: Expression,
95
+ end: Expression,
96
+ body: Block,
97
+ ): NumericForStatement {
98
+ return {
99
+ type: "NumericForStatement",
100
+ variable: typedIdentifier(variableName),
101
+ start,
102
+ end,
103
+ body,
104
+ line: POS,
105
+ column: POS,
106
+ }
107
+ }
108
+
109
+ export function functionParam(name: string): FunctionParameter {
110
+ return { type: "FunctionParameter", name, line: POS, column: POS }
111
+ }
112
+
113
+ export function functionBody(params: FunctionParameter[], body: Block, hasVarargs = false): FunctionBody {
114
+ return {
115
+ type: "FunctionBody", generics: [], params, hasVarargs, body, line: POS, column: POS,
116
+ }
117
+ }
118
+
119
+ export function functionExpression(func: FunctionBody): FunctionExpression {
120
+ return { type: "FunctionExpression", func, line: POS, column: POS }
121
+ }
122
+
123
+ export function localFunctionStatement(name: string, func: FunctionBody): LocalFunctionStatement {
124
+ return {
125
+ type: "LocalFunctionStatement", name: identifier(name), func, line: POS, column: POS,
126
+ }
127
+ }
128
+
129
+ export function doStatement(body: Block): DoStatement {
130
+ return { type: "DoStatement", body, line: POS, column: POS }
131
+ }
132
+
133
+ export function ifClause(condition: Expression, body: Block): IfClause {
134
+ return { type: "IfClause", condition, body, line: POS, column: POS }
135
+ }
136
+
137
+ export function ifStatement(clauses: IfClause[], alternate?: Block): IfStatement {
138
+ return { type: "IfStatement", clauses, alternate, line: POS, column: POS }
139
+ }
140
+
141
+ export function vararg(): VarargExpression {
142
+ return { type: "VarargExpression", line: POS, column: POS }
143
+ }
@@ -0,0 +1,173 @@
1
+ import type {
2
+ Program, Block, Statement, Expression, FunctionBody,
3
+ } from "luau-parser"
4
+
5
+ export type ExpressionVisitor = (expr: Expression) => Expression | undefined | void
6
+
7
+ function visitArray(arr: Expression[], visitor: ExpressionVisitor): void {
8
+ for (let i = 0; i < arr.length; i++) {
9
+ arr[i] = mapExpression(arr[i], visitor)
10
+ }
11
+ }
12
+
13
+ export function mapExpression(expr: Expression, visitor: ExpressionVisitor): Expression {
14
+ switch (expr.type) {
15
+ case "InterpolatedStringExpression":
16
+ for (const part of expr.parts) {
17
+ if (part.kind === "expression") {
18
+ part.expression = mapExpression(part.expression, visitor)
19
+ }
20
+ }
21
+ break
22
+
23
+ case "FunctionExpression":
24
+ walkFunctionBody(expr.func, visitor)
25
+ break
26
+
27
+ case "TableExpression":
28
+ for (const field of expr.fields) {
29
+ if (field.type === "TableFieldPositional") {
30
+ field.value = mapExpression(field.value, visitor)
31
+ } else if (field.type === "TableFieldNamed") {
32
+ field.value = mapExpression(field.value, visitor)
33
+ } else if (field.type === "TableFieldComputed") {
34
+ field.key = mapExpression(field.key, visitor)
35
+ field.value = mapExpression(field.value, visitor)
36
+ }
37
+ }
38
+ break
39
+
40
+ case "BinaryExpression":
41
+ expr.left = mapExpression(expr.left, visitor)
42
+ expr.right = mapExpression(expr.right, visitor)
43
+ break
44
+
45
+ case "UnaryExpression":
46
+ expr.argument = mapExpression(expr.argument, visitor)
47
+ break
48
+
49
+ case "MemberExpression":
50
+ expr.object = mapExpression(expr.object, visitor)
51
+ break
52
+
53
+ case "IndexExpression":
54
+ expr.object = mapExpression(expr.object, visitor)
55
+ expr.index = mapExpression(expr.index, visitor)
56
+ break
57
+
58
+ case "CallExpression":
59
+ expr.callee = mapExpression(expr.callee, visitor)
60
+ visitArray(expr.arguments, visitor)
61
+ break
62
+
63
+ case "MethodCallExpression":
64
+ expr.object = mapExpression(expr.object, visitor)
65
+ visitArray(expr.arguments, visitor)
66
+ break
67
+
68
+ case "ParenthesizedExpression":
69
+ expr.expression = mapExpression(expr.expression, visitor)
70
+ break
71
+
72
+ case "TypeAssertionExpression":
73
+ expr.expression = mapExpression(expr.expression, visitor)
74
+ break
75
+
76
+ case "IfElseExpression":
77
+ for (const clause of expr.clauses) {
78
+ clause.condition = mapExpression(clause.condition, visitor)
79
+ clause.body = mapExpression(clause.body, visitor)
80
+ }
81
+ expr.alternate = mapExpression(expr.alternate, visitor)
82
+ break
83
+ }
84
+
85
+ return visitor(expr) ?? expr
86
+ }
87
+
88
+ function walkFunctionBody(func: FunctionBody, visitor: ExpressionVisitor): void {
89
+ walkBlock(func.body, visitor)
90
+ }
91
+
92
+ function walkBlock(block: Block, visitor: ExpressionVisitor): void {
93
+ for (const stmt of block.statements) walkStatement(stmt, visitor)
94
+ }
95
+
96
+ function walkStatement(stmt: Statement, visitor: ExpressionVisitor): void {
97
+ switch (stmt.type) {
98
+ case "LocalStatement":
99
+ visitArray(stmt.init, visitor)
100
+ return
101
+
102
+ case "LocalFunctionStatement":
103
+ walkFunctionBody(stmt.func, visitor)
104
+ return
105
+
106
+ case "FunctionDeclarationStatement":
107
+ walkFunctionBody(stmt.func, visitor)
108
+ return
109
+
110
+ case "AssignmentStatement":
111
+ visitArray(stmt.targets, visitor)
112
+ visitArray(stmt.values, visitor)
113
+ return
114
+
115
+ case "CompoundAssignmentStatement":
116
+ stmt.target = mapExpression(stmt.target, visitor)
117
+ stmt.value = mapExpression(stmt.value, visitor)
118
+ return
119
+
120
+ case "CallStatement":
121
+ stmt.expression = mapExpression(stmt.expression, visitor) as typeof stmt.expression
122
+ return
123
+
124
+ case "DoStatement":
125
+ walkBlock(stmt.body, visitor)
126
+ return
127
+
128
+ case "WhileStatement":
129
+ stmt.condition = mapExpression(stmt.condition, visitor)
130
+ walkBlock(stmt.body, visitor)
131
+ return
132
+
133
+ case "RepeatStatement":
134
+ walkBlock(stmt.body, visitor)
135
+ stmt.condition = mapExpression(stmt.condition, visitor)
136
+ return
137
+
138
+ case "IfStatement":
139
+ for (const clause of stmt.clauses) {
140
+ clause.condition = mapExpression(clause.condition, visitor)
141
+ walkBlock(clause.body, visitor)
142
+ }
143
+ if (stmt.alternate) walkBlock(stmt.alternate, visitor)
144
+ return
145
+
146
+ case "NumericForStatement":
147
+ stmt.start = mapExpression(stmt.start, visitor)
148
+ stmt.end = mapExpression(stmt.end, visitor)
149
+ if (stmt.step) stmt.step = mapExpression(stmt.step, visitor)
150
+ walkBlock(stmt.body, visitor)
151
+ return
152
+
153
+ case "GenericForStatement":
154
+ visitArray(stmt.iterators, visitor)
155
+ walkBlock(stmt.body, visitor)
156
+ return
157
+
158
+ case "ReturnStatement":
159
+ visitArray(stmt.arguments, visitor)
160
+ return
161
+
162
+ case "BreakStatement":
163
+ case "ContinueStatement":
164
+ case "TypeAliasStatement":
165
+ case "ExportTypeAliasStatement":
166
+ return
167
+ }
168
+ }
169
+
170
+ /** Program 전체를 순회하며 모든 Expression 노드에 visitor를 적용(제자리 변형). */
171
+ export function transformExpressions(program: Program, visitor: ExpressionVisitor): void {
172
+ walkBlock(program.body, visitor)
173
+ }
@@ -0,0 +1,58 @@
1
+ import type { Program } from "luau-parser"
2
+ import type { ObfuscateConfig } from "./config"
3
+ import { runStripTypes } from "./passes/StripTypes"
4
+ import { runStringsToExpressions } from "./passes/StringsToExpressions"
5
+ import { runNumbersToExpressions } from "./passes/NumbersToExpressions"
6
+ import { runRenameVariables } from "./passes/RenameVariables"
7
+ import { runGlobalMapping } from "./passes/GlobalMapping"
8
+ import { runConstantArray } from "./passes/ConstantArray"
9
+ import { runEncryptStrings } from "./passes/EncryptStrings"
10
+ import { runEncryptNumbers } from "./passes/EncryptNumbers"
11
+ import { runInsertJunk } from "./passes/InsertJunk"
12
+ import { runWrapInFunction } from "./passes/WrapInFunction"
13
+
14
+ type PassFn = (program: Program, options: any) => void
15
+
16
+ export const PASS_ORDER: (keyof ObfuscateConfig)[] = [
17
+ "GlobalMapping",
18
+ "StringsToExpressions",
19
+ "NumbersToExpressions",
20
+ "RenameVariables",
21
+ "ConstantArray",
22
+ "EncryptStrings",
23
+ "EncryptNumbers",
24
+ "InsertJunk",
25
+ "Vmify",
26
+ "WrapInFunction",
27
+ "Minify",
28
+ ]
29
+
30
+ export const PASS_MAP: Partial<Record<keyof ObfuscateConfig, PassFn>> = {
31
+ GlobalMapping: runGlobalMapping,
32
+ StringsToExpressions: runStringsToExpressions,
33
+ NumbersToExpressions: runNumbersToExpressions,
34
+ RenameVariables: runRenameVariables,
35
+ ConstantArray: runConstantArray,
36
+ EncryptStrings: runEncryptStrings,
37
+ EncryptNumbers: runEncryptNumbers,
38
+ InsertJunk: runInsertJunk,
39
+ WrapInFunction: runWrapInFunction,
40
+ }
41
+
42
+ export function runPipeline(program: Program, config: ObfuscateConfig): void {
43
+ // 타입 주석/별칭은 값 트리와 독립적으로 원본 이름을 참조하고 있어서
44
+ // (예: typeof(x)) 어떤 pass보다도 먼저 지워야 함. 끄면 무조건 깨지므로
45
+ // config로 조절 가능한 옵션이 아니라 파이프라인 진입 시 항상 실행.
46
+ runStripTypes(program)
47
+
48
+ for (const key of PASS_ORDER) {
49
+ const feature = config[key]
50
+ if (!feature.active) continue
51
+
52
+ const pass = PASS_MAP[key]
53
+ if (!pass) continue
54
+
55
+ const { active, ...options } = feature as { active: true } & Record<string, unknown>
56
+ pass(program, options)
57
+ }
58
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
6
+ "declaration": false,
7
+ "declarationMap": false,
8
+ "sourceMap": false,
9
+ "outDir": "./dist",
10
+ "rootDir": "./src",
11
+ "moduleResolution": "bundler",
12
+ "allowSyntheticDefaultImports": true,
13
+ "esModuleInterop": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+ "strict": true,
16
+ "skipLibCheck": true,
17
+ "stripInternal": true,
18
+ },
19
+ "include": ["src/**/*"],
20
+ "exclude": ["node_modules", "dist"]
21
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: ["src/index.ts"],
5
+ format: ["esm", "cjs"],
6
+ dts: true,
7
+ clean: true,
8
+ sourcemap: false,
9
+ target: "esnext",
10
+ });