Skip to content

Instantly share code, notes, and snippets.

@Darker
Created August 23, 2017 08:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Darker/14ed6e38641945af09e12a759d25c836 to your computer and use it in GitHub Desktop.
Save Darker/14ed6e38641945af09e12a759d25c836 to your computer and use it in GitHub Desktop.
console.log("This script changes cached resources' path in GreaseMonkey addon folder to force refresh.");
console.log("Press any key to start adding files.");
/*process.stdin.setRawMode(true);
process.stdin.resume();
process.stdin.on('data', ()=>{getFiles(updateLoop);}); */
var fs=require("fs");
var path = require("path");
/*var prompt = require('prompt-sync');
prompt.question = function(q) {
process.stdout.write(q+"\n");
return this.prompt();
} */
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.choose = function(question, yes, no, thisArg) {
rl.question(question+" [y/n]", (a) => {
if(a.toLowerCase()=="y")
yes.apply(thisArg, params);
else
no.apply(thisArg, params);
});
var params = removeArguments(arguments, 4);
}
function removeArguments(args, count) {
var a = [];
a.push.apply(a, args);
return a.splice(0, count);
}
function getFiles(callback) {
//process.stdin.setRawMode(false);
console.log("Input the reference files - if those change, cached will be reloaded.");
console.log("To terminate adding of files, enter \"*\" without the quotes.");
var files = [];
var looper = (path) => {
if(path=="*") {
rl.choose("Stop adding files?",
()=>{callback(files)},
()=>{getFilePath(looper);}
);
return;
}
var file = new FileInfo(path);
addFile(files, file);
getFilePath(looper);
}
// First read files from arguments
var last = null;
process.argv.forEach(function (val, index) {
last = val;
if(index>1 && val!="+start") {
var file = new FileInfo(val);
addFile(files, file);
}
});
// Now, if needed, read files from stdin
if(last!="+start")
getFilePath(looper);
else
callback(files);
//callback(files);
}
function getFilePath(cb) {
rl.question('Filename: ', (path) => {
cb(path);
});
}
function addFile(files, file) {
if(!file.exists) {
console.log("File "+file.path+" does not exist.");
}
//else if(files.indexOf(file)!=-1)
// console.log("File "+file.path+" already added.");
else {
console.log("+ "+file.path+"");
files.push(file);
}
}
function updateLoop(files) {
if(files.length<1)
throw new Error("Not enough files specified.");
console.log("Finding occurences of those files.");
var userscripts = findUserscripts(process.env.appdata+"\\Mozilla\\Firefox\\Profiles\\ap7ptx7o.default\\gm_scripts");
//console.log("List of userscripts: ", userscripts);
userscripts.forEach( (userscript) => {
console.log("Checking "+userscript);
findOccurences(userscript, files);
});
console.log("Started waiting for changes.");
setInterval(checkFiles, 500, files);
}
function checkFiles(files) {
files.forEach((file) => {
if(file.changed()) {
console.log(file.path+" changed.");
file.update();
}
});
}
function findUserscripts(p, scripts) {
// if provided, use existing array
var userscripts = scripts || [];
if (!fs.existsSync(p)){
console.log("Directory does not exist:", p);
return;
}
var files = fs.readdirSync(p);
for(var i=0;i<files.length;i++) {
var filename=path.join(p, files[i]);
var stat = fs.lstatSync(filename);
if (stat.isDirectory()){
findUserscripts(filename, userscripts); //recurse
}
else if (/\.user\.js$/i.test(filename))
userscripts.push(filename);
};
return userscripts;
}
/** Finds occureces or resources in userscript **/
function findOccurences(userscript, watchedFiles) {
//var filenamesescaped = watchedFiles.map((file)=>{return file.jsPathEscaped;});
//var matcher = new RegExp("("+filenamesescaped.join("|")+")", "i");
var data = fs.readFileSync(userscript, 'utf8');
watchedFiles.forEach( (file) => {
if(data.indexOf(file.jsPath)!=-1) {
console.log("Occurence: "+file.path+" in "+userscript);
file.refferencingFiles.push(userscript);
}
});
}
/** Represent the file of a resource
* if this file changes, all refferencing GM scripts will be updated. **/
function FileInfo(p) {
this.path = p;
this.jsPath = "file:///"+path.normalize(p).replace(/\\/g, "/");
this.jsPathEscaped = escapeRegexChars(this.jsPath);
if(!fs.existsSync(p)) {
this.exists = false;
return;
}
this.exists = true;
this.stat = fs.statSync(p);
this.updateTime();
// List of files that refference this file and will need update
this.refferencingFiles = [];
}
FileInfo.prototype.changed = function() {
//console.log("Time diff: "+this.currentTime() +" - "+this.time + " = " + (this.currentTime()-this.time));
return this.time!=this.currentTime();
}
FileInfo.prototype.currentTime = function() {
return new Date(fs.statSync(this.path).mtime).getTime();
}
FileInfo.prototype.updateTime = function() {
this.time = this.currentTime();
}
FileInfo.prototype.update = function() {
this.refferencingFiles.forEach(function(file) {
updatePaths(file, this.jsPathEscaped);
}, this);
this.updateTime();
}
// Adds random #XXXX to path to force update
// first argument is the file to be edited (userscript)
// Second argument is the path to be replaced
function updatePaths(filename, resourcepath, callback) {
fs.readFile(filename, 'utf8', function (err,data) {
if (err) {
return console.log(err);
}
//var correctPath = "file:///"+path.normalize(resourcepath).replace(/\\/g, "/");
var regexstr = "^\\/\\/(\\s*)@(resource\\s+[a-z0-9_]+|require)(\\s+)("+resourcepath+")(#[^\n]*?)*?$";
var result = data.replace(new RegExp(regexstr, "gmi"), '//$1@$2$3$4#'+Math.random());
console.log("Replacing: ", regexstr);
fs.writeFile(filename, result, 'utf8', function (err) {
if(callback) {
if (err)
callback(err);
else
callback();
}
});
});
}
function escapeRegexChars(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};
// Start
getFiles(updateLoop);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment