Skip to content

Commit

Permalink
v1.4.0 - Improve Performance and Modularize Code (#21)
Browse files Browse the repository at this point in the history
* Add missing block property

* Modularize: break generateFlatAST into smaller pieces.
Refactor getParentKey to improve performance

* Adjust functionality tests to change in options - removed dependency between detailed and includeSrc

* Update eslint to 8.43.0

* Refactor Arborist for performance by sidestepping the need to iterate over the entire tree for replacement and deletion, and also removing src prints that might slow down execution.

* 1.4.0
  • Loading branch information
ctrl-escp authored Jun 18, 2023
1 parent 554fb34 commit 8235b5a
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 174 deletions.
20 changes: 10 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "flast",
"version": "1.3.5",
"version": "1.4.0",
"description": "Flatten JS AST",
"main": "src/index.js",
"scripts": {
Expand Down Expand Up @@ -30,7 +30,7 @@
"estraverse": "^5.3.0"
},
"devDependencies": {
"eslint": "^8.42.0",
"eslint": "^8.43.0",
"husky": "^8.0.3"
}
}
104 changes: 39 additions & 65 deletions src/arborist.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
const estraverse = require('estraverse');
const {generateCode, generateFlatAST,} = require(__dirname + '/flast');

const Arborist = class {
Expand All @@ -10,10 +9,9 @@ const Arborist = class {
this.script = '';
this.ast = [];
this.log = logFunc || (() => true);
this.maxLogLength = 60; // Max length of logged strings.
this.markedForDeletion = []; // Array of node ids.
this.markedForReplacement = {}; // nodeId: replacementNode pairs.
this.appliedCounter = 0; // Track the number of times changes were applied.
this.replacements = [];
if (typeof scriptOrFlatAstArr === 'string') {
this.script = scriptOrFlatAstArr;
this.ast = generateFlatAST(scriptOrFlatAstArr);
Expand All @@ -38,27 +36,12 @@ const Arborist = class {
return currentNode;
}

/**
* @param {string} src
* @param {boolean} padEnd Pad end with spaces to the maxLogLength if true.
* @returns {string} A parsed string fit for a log.
* @private
*/
_parseSrcForLog(src, padEnd = false) {
const output = src
.replace(/\n/g, ' ')
.substring(0, this.maxLogLength)
.replace(/([\n\r])/g, ' ')
.replace(/\s{2,}/g, ' ');
return padEnd ? output.padEnd(this.maxLogLength, ' ') : output;
}

/**
*
* @returns {number} The number of changes to be applied.
*/
getNumberOfChanges() {
return Object.keys(this.markedForReplacement).length + this.markedForDeletion.length;
return this.replacements.length + this.markedForDeletion.length;
}

/**
Expand All @@ -70,7 +53,7 @@ const Arborist = class {
markNode(targetNode, replacementNode) {
if (!targetNode.isMarked) {
if (replacementNode) { // Mark for replacement
this.markedForReplacement[targetNode.nodeId] = replacementNode;
this.replacements.push([targetNode, replacementNode]);
targetNode.isMarked = true;
} else { // Mark for deletion
targetNode = this._getCorrectTargetForDeletion(targetNode);
Expand All @@ -79,7 +62,6 @@ const Arborist = class {
targetNode.isMarked = true;
}
}
this.ast = this.ast.filter(n => n.nodeId !== targetNode.nodeId);
}
}

Expand All @@ -92,61 +74,53 @@ const Arborist = class {
let changesCounter = 0;
try {
const that = this;
const replacementNodeIds = Object.keys(this.markedForReplacement).map(nid => parseInt(nid));
if (this.getNumberOfChanges() > 0) {
let rootNode = this.ast[0];
if (replacementNodeIds.includes(0)) {
const rootNodeReplacement = this.replacements.find(n => n[0].nodeId === 0);
if (rootNodeReplacement) {
++changesCounter;
this.log(`[+] Applying changes to the root node...`);
rootNode = this.markedForReplacement[0];
rootNode = rootNodeReplacement[1];
} else {
const removalLogCache = []; // Prevents multiple printing of similar changes to the log
const replacementLogCache = [];
const badReplacements = [];
estraverse.replace(rootNode, {
enter(node) {
try {
if (replacementNodeIds.includes(node.nodeId) && node.isMarked) {
if (node.src) {
try {
if (badReplacements.includes(node.src)) return;
const nsrc = that._parseSrcForLog(node.src, true);
if (!replacementLogCache.includes(nsrc)) {
const tsrc = that._parseSrcForLog(generateCode(that.markedForReplacement[node.nodeId]));
that.log(`\t\t[+] Replacing\t${nsrc}\t--with--\t${tsrc}`, 2);
replacementLogCache.push(nsrc);
}
} catch {
that.log(`\t\t[+] Replacing ${that._parseSrcForLog('N/A')}\t--with\t${that._parseSrcForLog('N/A')}`);
}
}
for (const targetNodeId of this.markedForDeletion) {
try {
const targetNode = this.ast.find(n => n.nodeId === targetNodeId);
if (targetNode) {
const parent = targetNode.parentNode;
if (parent[targetNode.parentKey] === targetNode) {
parent[targetNode.parentKey] = undefined;
++changesCounter;
} else if (Array.isArray(parent[targetNode.parentKey])) {
const idx = parent[targetNode.parentKey].indexOf(targetNode);
parent[targetNode.parentKey][idx] = undefined;
parent[targetNode.parentKey] = parent[targetNode.parentKey].filter(n => n);
++changesCounter;
return that.markedForReplacement[node.nodeId];
} else if (that.markedForDeletion.includes(node.nodeId) && node.isMarked) {
if (node.src) {
try {
const ns = that._parseSrcForLog(node.src);
if (!removalLogCache.includes(ns)) {
that.log(`\t\t[+] Removing\t${ns}`, 2);
removalLogCache.push(ns);
}
} catch {
that.log(`\t\t[+] Removing\tN/A`, 2);
}
}
this.remove();
}
}
} catch (e) {
that.log(`[-] Unable to delete node: ${e}`);
}
}
for (const [targetNode, replacementNode] of this.replacements) {
try {
if (targetNode) {
const parent = targetNode.parentNode;
if (parent[targetNode.parentKey] === targetNode) {
parent[targetNode.parentKey] = replacementNode;
++changesCounter;
} else if (Array.isArray(parent[targetNode.parentKey])) {
const idx = parent[targetNode.parentKey].indexOf(targetNode);
parent[targetNode.parentKey][idx] = replacementNode;
++changesCounter;
return null;
}
} catch (e) {
that.log(`[-] Unable to replace/delete node: ${e}`);
badReplacements.push(node.src);
}
},
});
} catch (e) {
that.log(`[-] Unable to replace node: ${e}`);
}
}
}
if (changesCounter) {
this.markedForReplacement = {};
this.replacements.length = 0;
this.markedForDeletion.length = 0;
// If any of the changes made will break the script the next line will fail and the
// script will remain the same. If it doesn't break, the changes are valid and the script can be marked as modified.
Expand Down
Loading

0 comments on commit 8235b5a

Please sign in to comment.