devil-windows
Version:
Debugger, profiler and runtime with embedded WebKit DevTools client (for Windows).
261 lines (217 loc) • 8.31 kB
JavaScript
var fs = require('fs'),
path = require('path'),
async = require('async'),
glob = require('glob');
function escapeRegex(str) {
return str.replace(/([/\\.?*()^${}|[\]])/g, '\\$1');
}
var MODULE_HEADER = '(function (exports, require, module, __filename, __dirname) { ';
var MODULE_TRAILER = '\n});';
var MODULE_WRAP_REGEX = new RegExp(
'^' + escapeRegex(MODULE_HEADER) +
'([\\s\\S]*)' +
escapeRegex(MODULE_TRAILER) + '$'
);
var CONVENTIONAL_DIRS_PATTERN = '{*.js,lib/**/*.js,node_modules/**/*.js,test/**/*.js}';
var ALL_JS_PATTERN = '**/*.js';
/**
* ScriptStorage class. Controls all scripts and live edit.
* Refactored from node-inspector
* https://github.com/node-inspector/node-inspector
*
* @param {boolean} preload
* @param {v8.DebuggerClient} debuggerClient
* @constructor
*/
var ScriptStorage = function (debuggerClient, preload) {
var $this = this;
//
// Public stuff
/**
* @type {object}
* @private
*/
var _lastList = null;
/**
* @type {boolean}
* @private
*/
var _noPreload = (preload === false);
/**
* @type {ScriptManager}
* @private
*/
var _scriptManager = debuggerClient.scriptManager;
/**
* For a given script file, find the root directory containing all application source files.
*
* Example:
* file = ~/work/app/bin/cli.js
* root = ~/work/app
*
* The algorithm:
*
* By default, we assume that the source file is in the root directory
* (~/work/app/bin in the example above).
*
* If this directory does not contain 'package.json' and the parent directory
* contains 'package.json', then we assume the parent directory is
* the application root (~/work/app in the example above).
*
* @param {string} file
* @param {Function} callback
*/
var _findApplicationRootForRealFile = function (file, callback) {
var mainDir = path.dirname(file);
var parentDir = path.dirname(mainDir);
console.log("_findApplicationRootForRealFile", mainDir, parentDir, file);
async.detect([mainDir, parentDir], _isApplicationRoot, function (result) {
callback(null, result || mainDir, !!result);
});
};
var _isApplicationRoot = function (folder, callback) {
fs.exists(path.join(folder, 'package.json'), callback);
};
var _findScriptsOfRunningApp = function (mainScriptFile, callback) {
if (!mainScriptFile) {
// mainScriptFile is null when running in the REPL mode
return process.nextTick(callback.bind(null, null, []));
}
$this.findApplicationRoot(mainScriptFile, function (err, dir, isRoot) {
if (err) return callback(err);
$this.rootDir = dir;
var pattern = isRoot ? ALL_JS_PATTERN : CONVENTIONAL_DIRS_PATTERN;
$this.listScripts(dir, pattern, callback);
});
};
var _findScriptsOfStartDirectoryApp = function (startDirectory, callback) {
// TODO: check this out some day.
return callback(null, []);
_isApplicationRoot(startDirectory, function handleIsStartDirectoryApplicationRoot(result) {
if (!result) callback(null, []);
else $this.listScripts(startDirectory, ALL_JS_PATTERN, callback);
});
};
//
// Public stuff
this.rootDir = null;
/**
* @param {string} path
* @param {function(Object, string)} callback
*/
this.load = function (path, callback) {
fs.readFile(path, 'utf-8', function (err, content) {
if (err) return callback(err);
// remove shebang
content = content.replace(/^\#\!.*/, '');
var source = content;
_scriptManager.setContent(path, source);
return callback(null, source);
});
};
/**
* @param {string} path
* @param {string} content
* @param {Function} callback
*/
this.save = function (path, content, callback) {
/*var match = MODULE_WRAP_REGEX.exec(content);
if (!match) {
callback(new Error('The new content is not a valid node.js script.'));
return;
}
var newSource = match[1];*/
var newSource = content;
fs.readFile(path, 'utf-8', function (err, oldContent) {
if (err) return callback(err);
var match = /^(\#\!.*)/.exec(oldContent);
if (match) newSource = match[1] + newSource;
console.log("-- GOD DAMN IT", err);
fs.writeFile(path, newSource, callback);
});
};
/**
* @param {string} mainScriptFile
* @param {function(Object, string)} callback
*/
this.findApplicationRoot = function (mainScriptFile, callback) {
fs.realpath(mainScriptFile, function (err, realPath) {
if (err) {
console.demonicLog('[WARNING] Cannot resolve real path of %s: %s', mainScriptFile, err);
realPath = mainScriptFile;
}
_findApplicationRootForRealFile(realPath, callback);
});
};
/**
* @param {string} rootFolder
* @param {string} pattern
* @param {function(Object, Array.<string>?)} callback
*/
this.listScripts = function (rootFolder, pattern, callback) {
var last = _lastList;
if (last && last.rootFolder == rootFolder && last.pattern == pattern) {
process.nextTick(function () {
callback(last.error, last.result);
});
return;
}
// This simpler solution unfortunately does not work on windows
// see https://github.com/isaacs/node-glob/pull/68
// glob(
// '**/*.js',
// { root: rootFolder },
// callback
// );
glob(pattern, {cwd: rootFolder, strict: false}, function (err, result) {
if (result) {
result = result.map(function (relativeUnixPath) {
var relativePath = relativeUnixPath.split('/').join(path.sep);
return path.join(rootFolder, relativePath);
});
}
_lastList = {
rootFolder: rootFolder,
pattern: pattern,
error: err,
result: result
};
callback(err, result);
});
};
/**
* @param {string} startDirectory
* @param {string} mainScriptFile
* @param {function(Object, Array.<string>)} callback
*/
this.findAllApplicationScripts = function (startDirectory, mainScriptFile, callback) {
if (_noPreload) return process.nextTick(function () {
callback(null, []);
});
console.log("\n\n\n\n\n\n\n\n\n\n====\n\n\n\n\n\n\n\n\n", startDirectory, mainScriptFile, "\n\n\n\n\n\n\n\n\n\n====\n\n\n\n\n\n\n\n\n");
async.series([
_findScriptsOfRunningApp.bind(this, mainScriptFile),
_findScriptsOfStartDirectoryApp.bind(this, startDirectory)//,
//_scriptManager.getAllScripts.bind(_scriptManager)
], function (err, results) {
if (err) return callback(err);
var files = results[0].concat(results[1]); //, results[2]
files = files.filter(function onlyUnique(value, index, self) {
return self.indexOf(value) === index && value.length > 0;
});
console.log("\n\n\n\n\n\n\n\n\n\n====\n\n\n\n\n\n\n\n\n", files, "\n\n\n\n\n\n\n\n\n\n====\n\n\n\n\n\n\n\n\n");
// Filter out duplicates and files to hide
files = files.filter(function (elem, ix, arr) {
return arr.indexOf(elem) >= ix && !_scriptManager.isScriptHidden(elem);
});
return callback(null, files);
});
};
/**
* Destructor
*/
this.destroy = function () {
_scriptManager = null;
};
};
module.exports = ScriptStorage;