Skip to content

Instantly share code, notes, and snippets.

@mootari
Last active October 6, 2016 17:39
Show Gist options
  • Save mootari/65c6e37037b358232b82d463993115de to your computer and use it in GitHub Desktop.
Save mootari/65c6e37037b358232b82d463993115de to your computer and use it in GitHub Desktop.
fs = require('fs');
execSync = require('child_process').execSync;
options = {
dir: 'd3-zoom',
file: 'README.md',
prefix: 'https://github.com/d3/d3-zoom/blob/master/',
delimiter: '#L',
suffix: ' ',
updateFile: true
};
function findSourceLinks(data, prefix, delimiter, suffix) {
function esc(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
var pattern, lines, matches = [];
pattern = new RegExp(esc(prefix) + '(.+?)' + esc(delimiter) + '([0-9]+)' + esc(suffix), 'g');
lines = data.split('\n');
lines.forEach(function(str, index) {
var match;
pattern.lastIndex = 0;
while(match = pattern.exec(str)) {
matches.push({
lineContent: str,
lineIndex: index,
linkIndex: match.index,
targetUrl: match[0],
targetFile: match[1],
targetLine: +match[2]
});
}
});
return matches;
}
/**
* Finds the first commit that introduced a string into a file.
*
* @param {string} gitCmd - Base git command.
* @param {string} str - Search string.
* @param {string} file - File path.
* @returns {string}|{null} First commit ID or null if none was found.
*/
function gitStringOrigin(gitCmd, str, file) {
var result, match;
result = execSync(gitCmd + ' log --reverse --oneline -S "' + str + '" -- "' + file + '"');
match = result.toString().match(/^[a-f0-9]{5,}/);
return match ? match[0] : null;
}
/**
* Returns the content of a file's line in a specific commit.
*
* @param {string} gitCmd - Base git command.
* @param {string} commit - Commit ID.
* @param {string} file - File path.
* @param {string} line - Line number.
* @returns {string}|{null} - Either the line content or null if not found.
*/
function stringGitSource(gitCmd, commit, file, line) {
var result, lines;
result = execSync(gitCmd + ' show ' + commit + ':"' + file + '"');
lines = result.toString().split('\n');
return lines.length >= line ? lines[line - 1] : null;
}
/**
* Renders a formatted table.
* @param {string[]} headers - Header labels.
* @param {string[][]} rows - Row data.
* @returns {string} - Rendered table.
*/
function renderTable(headers, rows) {
/**
* Returns column widths for a set of rows.
* @param {string[][]} rows - Rows with column data.
* @returns {number[]} - Maximum width of each column.
*/
function colWidths(rows) {
return rows.reduce(function(sizes, row) {
return row.map(function(str, i) {
str += '';
return sizes[i] && sizes[i] > str.length ? sizes[i] : str.length;
});
}, []);
}
/**
* Extends a string to a given length.
* @param {string} str
* @param {number} length - Target length.
* @param {string} char - Character to append.
* @returns {string}
*/
function fill(str, length, char) {
str += '';
while(str.length < length) {
str += char;
}
return str;
}
/**
* Renders a single row.
* @param {number[]} widths - Column sizes.
* @param {string[]} items - Column data.
* @param {string} fillChar - Character to fill up short columns with.
* @param {string} delimiter - Column delimiter.
* @returns {string} - Rendered column.
*/
function renderRow(widths, items, fillChar, delimiter) {
return items.map(function(str, i) {
return fill(str, widths[i], fillChar);
}).join(delimiter);
};
var widths = colWidths([headers].concat(rows));
var output = [];
output.push(renderRow(widths, headers, ' ', ' | '));
output.push(renderRow(widths, headers.fill(''), '=', ' | '));
rows.forEach(function(row) {
output.push(renderRow(widths, row, ' ', ' | '));
});
return output.join('\n');
}
targetFile = options.dir + '/' + options.file;
fs.readFile(targetFile, 'UTF-8', function(err, data) {
var matches, lines, cmd, changes = [];
matches = findSourceLinks(data, options.prefix, options.delimiter, options.suffix);
cmd = 'git --git-dir="' + options.dir + '/.git" --work-tree="' + options.dir + '"';
matches.forEach(function(match) {
var commit = gitStringOrigin(cmd, match.targetUrl, options.file);
if(commit === null) {
return;
}
match.linkCommit = commit;
match.targetSource = stringGitSource(cmd, match.linkCommit, match.targetFile, match.targetLine);
filedata = fs.readFileSync(options.dir + '/' + match.targetFile).toString().split('\n');
filedata.some(function(line, index) {
if(line.indexOf(match.targetSource) >= 0) {
match.targetLineNew = index + 1;
return true;
}
});
});
lines = data.split('\n');
matches.forEach(function(match) {
var parts, updated;
if(match.targetLineNew !== match.targetLine) {
parts = match.targetUrl.split(options.delimiter, 2);
updated = parts[0] + options.delimiter + match.targetLineNew + options.suffix;
lines[match.lineIndex] = lines[match.lineIndex].replace(match.targetUrl, updated);
changes.push([
match.targetSource,
match.targetLine,
match.targetLineNew
]);
}
});
if(changes.length) {
if(options.updateFile) {
console.log('Updating ' + targetFile);
fs.writeFile(targetFile, lines.join('\n'), function(err) {
if(err) {
console.log(err);
}
});
}
console.log(renderTable(['Source', 'Old', 'New'], changes));
}
else {
console.log('No changes found.');
}
});
Updating d3-zoom/README.md
Source | Old | New
==================================================== | === | ===
function zoom(selection) { | 50 | 51
zoom.transform = function(collection, transform) { | 62 | 63
zoom.translateBy = function(selection, x, y) { | 96 | 97
zoom.scaleBy = function(selection, k) { | 77 | 78
zoom.scaleTo = function(selection, k) { | 85 | 86
zoom.filter = function(_) { | 336 | 350
zoom.extent = function(_) { | 340 | 354
zoom.scaleExtent = function(_) { | 344 | 358
zoom.translateExtent = function(_) { | 348 | 362
zoom.duration = function(_) { | 352 | 366
zoom.interpolate = function(_) { | 357 | 370
zoom.on = function() { | 361 | 374
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment