Skip to content

Instantly share code, notes, and snippets.

@victor-homyakov
Created April 21, 2023 15:56
Show Gist options
  • Save victor-homyakov/1db6159f492f9460bdbf656dec70a2fa to your computer and use it in GitHub Desktop.
Save victor-homyakov/1db6159f492f9460bdbf656dec70a2fa to your computer and use it in GitHub Desktop.
Sort object properties depending on their usage frequency: place most used first.
const estraverse = require('estraverse');
const espree = require('espree');
const escodegen = require('escodegen');
const fs = require('fs');
const path = require('path');
function getNameProp(prop) {
if (prop.type === 'SpreadElement') {
return prop;
}
if (prop.key.type === 'Identifier') {
return prop.key.name;
} else if (prop.key.type === 'MemberExpression') {
return prop.key.object.name;
}
return prop.key.value;
}
function changeCode(ast, sortedProps) {
try {
estraverse.traverse(ast, {
enter(node) {
if (node.type === 'ObjectExpression') {
let start = -1;
let end = 0;
let has = false;
for (end = 0; end < node.properties.length; end++) {
const x = node.properties[end];
let value = sortedProps.get(getNameProp(x));
value = (value == undefined) ? 0 : value;
if (value != 0) {
has = true;
if (start === -1) {
start = end;
}
} else if (has) {
sortProperties(node, start, end, sortedProps);
start = -1;
has = false;
}
}
if (start !== -1) {
sortProperties(node, start, end, sortedProps);
}
}
},
});
return escodegen.generate(ast);
} catch (e) {
console.log(`Error: ${e}`);
return ast;
}
}
function sortProperties(node, start, end, sortedProps) {
const arr = node.properties.slice(start, end);
arr.sort((a, b) => {
const valueB = sortedProps.get(getNameProp(b));
const valueA = sortedProps.get(getNameProp(a));
return valueB - valueA;
});
node.properties.splice(start, end - start, ...arr);
}
const propAccessCount = new Map();
function createMapAndSort(source) {
try {
const ast = espree.parse(source, {ecmaVersion: 11});
estraverse.traverse(ast, {
enter(node) {
if (node.type === 'ObjectExpression') {
for (const prop of node.properties) {
let name;
if (prop.type === 'SpreadElement') {
propAccessCount.set(prop, 0);
} else {
if (prop.key.type === 'Identifier') {
name = prop.key.name;
} else if (prop.key.type === 'MemberExpression') {
name = prop.key.object.name;
} else {
name = prop.key.value;
}
if (propAccessCount.has(name)) {
propAccessCount.set(name, propAccessCount.get(name) + 1);
} else {
propAccessCount.set(name, 1);
}
}
}
}
},
});
return ast;
} catch (e) {
return source;
}
}
const filesContent = new Map();
function visitFolders(dir) {
const files = fs.readdirSync(dir, {withFileTypes: true});
for (const file of files) {
if (file.isDirectory()) {
if (file.name !== 'node_modules') {
visitFolders(path.join(dir, file.name));
}
} else if (file.isFile()) {
if (path.extname(file.name) === '.js') {
const name = path.join(dir, file.name);
const fileContent = fs.readFileSync(name);
const ast = createMapAndSort(fileContent);
filesContent.set(name, ast);
}
}
}
}
const folderName = process.argv[2];
visitFolders(folderName);
const props = Array.from(propAccessCount);
props.sort((a, b) => b[1] - a[1]);
const sortedProps = new Map(props);
for (const fileName of filesContent.keys()) {
const newCode = changeCode(filesContent.get(fileName), sortedProps);
fs.writeFileSync(fileName, newCode);
}
console.log('Done');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment