Skip to content

Instantly share code, notes, and snippets.

@chjj
Created August 18, 2017 02:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chjj/bd026c503add3c5387e798cf24eaa3b1 to your computer and use it in GitHub Desktop.
Save chjj/bd026c503add3c5387e798cf24eaa3b1 to your computer and use it in GitHub Desktop.
'use strict';
const path = require('path');
const fs = require('./lib/utils/fs');
async function findFiles(dir, match, ignore, files) {
const list = await fs.readdir(dir);
for (const name of list) {
if (ignore.has(name))
continue;
const file = path.join(dir, name);
const stat = await fs.lstat(file);
if (stat.isSymbolicLink())
continue;
if (stat.isDirectory()) {
await findFiles(file, match, ignore, files);
continue;
}
if (!match.test(name))
continue;
files.push(file);
}
return files;
}
async function getFiles(dir, ignore, match) {
const set = new Set();
if (!ignore)
ignore = [];
for (const name of ignore)
set.add(name);
if (!match)
match = /^[^\0]+\.js$/;
return await findFiles(dir, match, set, []);
}
function indentLines(text, spaces) {
const spacing = Array(spaces + 1).join(' ');
const lines = text.split('\n');
const indent = [];
for (let line of lines) {
if (line.length !== 0)
line = spacing + line;
indent.push(line);
}
return indent.join('\n');
}
function processFile(data) {
let mod = false;
const fnToClass =
/\n(\/\*\*(?:\n \*[^\n]*)+?\n \*\/\s+)?function ([A-Z]\w*)\(([^)]*)\) \{([^\0]+?\n)\};?(?:\s*Object\.setPrototypeOf\([^,]+, ([^.]+)\.prototype\);\n)?/g;
data = data.replace(fnToClass, (_, comment, name, args, body, parent) => {
mod = true;
if (!comment)
comment = '';
if (!parent)
parent = '';
comment = indentLines(comment, 1);
body = indentLines(body, 1);
if (parent)
parent = ` extends ${parent}`;
return `\nclass ${name}${parent} {\n${comment} constructor(${args}) {${body} }\n}`;
});
const methodToClass =
/\n(\/\*\*(?:\n \*[^\n]*)+?\n \*\/\s+)?[A-Z]\w*(\.prototype)?\.\w+ = (async |)function ([^{]+)\{([^\0]*?\n)\};/g;
data = data.replace(methodToClass, (_, comment, proto, asyn, nameargs, body) => {
mod = true;
if (!comment)
comment = '';
comment = indentLines(comment, 1);
body = indentLines(body, 1);
let stat = '';
if (!proto)
stat = 'static ';
return `\n${comment} ${stat}${asyn}${nameargs}{${body} }`;
});
const ctorToSuper = /[A-Z]\w*\.call\(this(?:, ([^)]+))?\);/g;
data = data.replace(ctorToSuper, (_, args) => {
mod = true;
if (!args)
args = '';
return `super(${args});`;
});
const wrapper =
/\n *if \(!\(this instanceof ([A-Z]\w*)\)\)\n *return new \1[^\n]+\n/g;
data = data.replace(wrapper, () => {
mod = true;
return '';
});
if (!mod)
return null;
return data;
}
async function rewriteFile(filename) {
let data = await fs.readFile(filename, 'utf8');
data = processFile(data);
if (data === null)
return;
await fs.writeFile(filename, data);
}
async function rewriteFiles(files) {
const jobs = files.map(file => rewriteFile(file));
return await Promise.all(jobs);
}
(async () => {
const target = process.argv[2] || '.';
const cwd = path.resolve(process.cwd(), target);
const ignore = ['node_modules', 'vendor'];
const files = await getFiles(cwd, ignore);
await rewriteFiles(files);
})().catch((err) => {
console.error(err.stack);
process.exit(1);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment