Advanced AST transformation capabilities for custom JavaScript code modifications and optimizations using UglifyJS's transformation infrastructure.
Primary utility for transforming Abstract Syntax Trees with before/after processing hooks.
/**
* Creates a TreeTransformer for modifying AST nodes
* @param before - Function called before processing child nodes
* @param after - Function called after processing child nodes
* @constructor
*/
function TreeTransformer(before, after);
interface TreeTransformer extends TreeWalker {
/** Transformation applied before processing child nodes */
before?: (node: AST_Node, descend: Function) => AST_Node;
/** Transformation applied after processing child nodes */
after?: (node: AST_Node) => AST_Node;
}Usage Examples:
const UglifyJS = require("uglify-js");
// Remove all console.log statements
const removeConsole = new UglifyJS.TreeTransformer(function(node) {
if (node instanceof UglifyJS.AST_SimpleStatement &&
node.body instanceof UglifyJS.AST_Call &&
node.body.expression instanceof UglifyJS.AST_Dot &&
node.body.expression.expression instanceof UglifyJS.AST_Symbol &&
node.body.expression.expression.name === "console" &&
node.body.expression.property === "log") {
// Return empty statement to remove console.log
return new UglifyJS.AST_EmptyStatement();
}
});
const ast = UglifyJS.parse('console.log("test"); var x = 1;');
const transformed = ast.transform(removeConsole);Array transformation utility with special operation support for AST manipulation.
/**
* Transform arrays with support for special operations
* @param array - Input array to transform
* @param fn - Transformation function that can return special operations
* @returns Transformed array with operations applied
*/
function List(array, fn);
interface ListStatic {
/** Check if value is a special List operation */
is_op(value: any): boolean;
}
/** Special operation to skip an element during List transformation */
const skip: unique symbol;
/** Special operation to splice multiple elements into a List */
class Splice {
constructor(elements: any[]);
v: any[];
}Usage Examples:
const UglifyJS = require("uglify-js");
// Transform statement list, removing empty statements
const statements = [
new UglifyJS.AST_SimpleStatement({ body: new UglifyJS.AST_Number({ value: 1 }) }),
new UglifyJS.AST_EmptyStatement(),
new UglifyJS.AST_SimpleStatement({ body: new UglifyJS.AST_Number({ value: 2 }) })
];
const filtered = UglifyJS.List(statements, function(stmt) {
if (stmt instanceof UglifyJS.AST_EmptyStatement) {
return UglifyJS.List.skip; // Remove empty statements
}
return stmt;
});
// Using Splice to insert multiple elements
const withLogging = UglifyJS.List(statements, function(stmt, index) {
if (stmt instanceof UglifyJS.AST_Function) {
// Insert logging before function
const logStmt = new UglifyJS.AST_SimpleStatement({
body: new UglifyJS.AST_Call({
expression: new UglifyJS.AST_Dot({
expression: new UglifyJS.AST_Symbol({ name: "console" }),
property: "log"
}),
args: [new UglifyJS.AST_String({ value: "Entering function" })]
})
});
return new UglifyJS.List.Splice([logStmt, stmt]);
}
return stmt;
});const transformer = new UglifyJS.TreeTransformer(
// Before hook: modify node before processing children
function(node, descend) {
if (node instanceof UglifyJS.AST_Function) {
console.log("Entering function:", node.name);
}
return node;
},
// After hook: modify node after processing children
function(node) {
if (node instanceof UglifyJS.AST_Function) {
console.log("Exiting function:", node.name);
}
return node;
}
);const conditionalTransform = new UglifyJS.TreeTransformer(function(node) {
// Transform specific patterns
if (node instanceof UglifyJS.AST_Binary &&
node.operator === "+" &&
node.left instanceof UglifyJS.AST_String &&
node.right instanceof UglifyJS.AST_String) {
// Concatenate string literals at compile time
return new UglifyJS.AST_String({
value: node.left.value + node.right.value,
quote: node.left.quote
});
}
return node;
});Add unique elements to arrays during transformation.
/**
* Add element to array if not already present
* @param array - Target array to modify
* @param element - Element to add if unique
* @returns true if element was added, false if already present
*/
function push_uniq(array, element);Usage Examples:
const UglifyJS = require("uglify-js");
const dependencies = [];
const transformer = new UglifyJS.TreeTransformer(function(node) {
if (node instanceof UglifyJS.AST_Call &&
node.expression instanceof UglifyJS.AST_Symbol &&
node.expression.name === "require" &&
node.args.length === 1 &&
node.args[0] instanceof UglifyJS.AST_String) {
// Collect unique require() calls
UglifyJS.push_uniq(dependencies, node.args[0].value);
}
return node;
});
const ast = UglifyJS.parse('require("fs"); require("path"); require("fs");');
ast.transform(transformer);
console.log(dependencies); // ["fs", "path"]const renameVariables = new UglifyJS.TreeTransformer(function(node) {
if (node instanceof UglifyJS.AST_Symbol && node.name === "oldName") {
return new UglifyJS.AST_Symbol({
name: "newName",
start: node.start,
end: node.end
});
}
return node;
});const inlineFunctions = new UglifyJS.TreeTransformer(function(node) {
if (node instanceof UglifyJS.AST_Call &&
node.expression instanceof UglifyJS.AST_Symbol &&
knownFunctions[node.expression.name]) {
// Replace function call with inlined body
const fnBody = knownFunctions[node.expression.name];
return fnBody.clone();
}
return node;
});const removeDeadCode = new UglifyJS.TreeTransformer(function(node) {
if (node instanceof UglifyJS.AST_If &&
node.condition instanceof UglifyJS.AST_Boolean) {
if (node.condition.value) {
// Condition is always true, replace with then branch
return node.body;
} else {
// Condition is always false, replace with else branch or remove
return node.alternative || new UglifyJS.AST_EmptyStatement();
}
}
return node;
});// Create a new variable declaration
const varDecl = new UglifyJS.AST_Var({
definitions: [
new UglifyJS.AST_VarDef({
name: new UglifyJS.AST_Symbol({
name: "newVar"
}),
value: new UglifyJS.AST_Number({
value: 42
})
})
]
});
// Create a new function call
const funcCall = new UglifyJS.AST_Call({
expression: new UglifyJS.AST_Symbol({
name: "console.log"
}),
args: [
new UglifyJS.AST_String({
value: "Hello World",
quote: '"'
})
]
});// Deep clone a node
const clonedNode = originalNode.clone(true);
// Shallow clone a node
const shallowClone = originalNode.clone(false);// Chain multiple transformations
const pipeline = [
removeDeadCode,
inlineFunctions,
renameVariables
];
let ast = UglifyJS.parse(sourceCode);
for (const transformer of pipeline) {
ast = ast.transform(transformer);
}
// Generate final output
const result = UglifyJS.minify(ast);Transformations can be applied before minification for custom optimizations:
const UglifyJS = require("uglify-js");
// Custom transformation
const customTransform = new UglifyJS.TreeTransformer(function(node) {
// Your transformation logic here
return node;
});
// Parse, transform, then minify
const ast = UglifyJS.parse(sourceCode);
const transformed = ast.transform(customTransform);
const result = UglifyJS.minify(transformed, {
compress: {},
mangle: {}
});const safeTransform = new UglifyJS.TreeTransformer(function(node) {
try {
// Risky transformation
if (canTransform(node)) {
return transformNode(node);
}
} catch (error) {
console.warn("Transformation failed:", error.message);
// Return original node if transformation fails
}
return node;
});