Socket
Socket
Sign inDemoInstall

doctor

Package Overview
Dependencies
14
Maintainers
1
Versions
41
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.2.3 to 0.3.0

11

lib/cli.js

@@ -30,7 +30,7 @@ /*

.describe('transform', 'use transform rules')
.alias('report', 'r')
.default('report', true)
.describe('report', 'use report rules')
.default('render', true)

@@ -45,3 +45,3 @@ .describe('render', 'use render module')

.describe('ast', 'return ast')
.alias('output', 'o')

@@ -61,3 +61,3 @@ .default('output', false)

// })
.argv;

@@ -78,4 +78,3 @@

transform: argv.transform,
unknown: argv.unknown,
sync: argv.sync
unknown: argv.unknown
};

@@ -82,0 +81,0 @@

@@ -9,5 +9,3 @@ /*

var argv = require('optimist').argv;
//var parser = require('uglify-js').parser;
var fs = require('fs');
//var traverse = require('traverse');
var async = require('async');

@@ -24,34 +22,29 @@ var Path = require('path');

var u = require('./util');
function getAst(options, astList, astPathList, cb) {
(function (async, u) {
if (options.sync) {
async = u.sync.async;
}
async.forEachSeries(options.files, function (file, cb) {
options.progressCb({
type: 'get-ast',
file: file,
message: 'get ast: ' + file
});
nast.astFromFile(options, file, function (e, ast) {
if (e) {
return cb(e);
}
astList.push(ast);
astPathList.push(ast.path);
cb(null);
});
}, function (err) {
if (err) {
return cb(err);
async.forEachSeries(options.files, function (file, cb) {
options.progressCb({
type: 'get-ast',
file: file,
message: 'get ast: ' + file
});
nast.astFromFile(options, file, function (e, ast) {
if (e) {
return cb(e);
}
astPathList = u.localizePaths(astPathList);
astList.forEach(function (ast, i) {
ast.fullPath = ast.path;
ast.path = astPathList[i];
});
astList.push(ast);
astPathList.push(ast.path);
cb(null);
});
}(async, u));
}, function (err) {
if (err) {
return cb(err);
}
astPathList = u.localizePaths(astPathList);
astList.forEach(function (ast, i) {
ast.fullPath = ast.path;
ast.path = astPathList[i];
});
cb(null);
});
}

@@ -86,3 +79,2 @@

}
// no sync here yet

@@ -317,591 +309,296 @@ var requires = [];

function examine(options, progressCb, cb) {
(function (Path, fs, async, u) {
var syncFinished = false;
options = _.clone(options);
if (typeof options.follow === 'undefined') {
options.follow = true;
}
if (typeof cb === 'undefined') {
cb = progressCb;
progressCb = function () {};
}
options.progressCb = progressCb;
options = _.clone(options);
options.sync = options.sync || false;
if (typeof options.follow === 'undefined') {
options.follow = true;
if (!Array.isArray(options.files)) {
cb(new Error('must pass in array of files'));
}
if (options.output === true) {
options.output = 'output';
}
if (options.transform === true || typeof options.transform === 'undefined') {
options.transform = 'default';
}
if (options.report === true || typeof options.report === 'undefined') {
options.report = 'default';
}
if (options.view === true) {
options.view = 'default';
}
// if (options.report && !options.render) {
// options.render = true;
// }
if (options.render === true || typeof options.render === 'undefined') {
options.render = 'default';
}
if (typeof options.unknown === 'undefined') {
options.unknown = true;
}
var outputDir = null;
var outputReportName = null;
if (options.output) {
outputDir = Path.dirname(options.output);
if (Path.extname(options.output) === '.json') {
outputReportName = Path.basename(options.output);
} else {
outputDir = options.output;
}
if (typeof cb === 'undefined') {
cb = progressCb;
progressCb = function () {};
}
options.progressCb = progressCb;
var origCb = cb;
var syncError = null;
var syncResult = null;
}
if (outputReportName) {
options.outputReportName = outputReportName;
}
var astList = [];
var astPathList = [];
var done = false;
if (options.sync) {
Path = u.sync.path;
fs = u.sync.fs;
async = u.sync.async;
u = u.sync;
cb = function (err, result) {
syncFinished = true;
syncError = err;
syncResult = result;
};
}
if (!Array.isArray(options.files)) {
cb(new Error('must pass in array of files'));
}
if (options.output === true) {
options.output = 'output';
}
if (options.transform === true || typeof options.transform === 'undefined') {
options.transform = 'default';
}
if (options.report === true || typeof options.report === 'undefined') {
options.report = 'default';
}
if (options.view === true) {
options.view = 'default';
}
// if (options.report && !options.render) {
// options.render = true;
// }
if (options.render === true || typeof options.render === 'undefined') {
options.render = 'default';
}
if (typeof options.unknown === 'undefined') {
options.unknown = true;
}
var outputDir = null;
var outputReportName = null;
if (options.output) {
outputDir = Path.dirname(options.output);
if (Path.extname(options.output) === '.json') {
outputReportName = Path.basename(options.output);
} else {
outputDir = options.output;
function finish(result) {
progressCb({
type: 'create-result',
message: 'create result'
});
if (!options.ast && !options.render) {
result = result.report;
} else if (!options.ast) {
result = result.files;
} else {
var cleanAst = nast.cleanAst(result.ast);
result.ast = cleanAst;
if (!options.report) {
result = cleanAst;
} else if (!options.render) {
result = {ast: cleanAst, report: result.report};
}
}
if (outputReportName) {
options.outputReportName = outputReportName;
}
var astList = [];
var astPathList = [];
var done = false;
return cb(null, result);
}
function finish(result) {
progressCb({
type: 'create-result',
message: 'create result'
});
if (!options.ast && !options.render) {
result = result.report;
} else if (!options.ast) {
result = result.files;
} else {
var cleanAst = nast.cleanAst(result.ast);
result.ast = cleanAst;
if (!options.report) {
result = cleanAst;
} else if (!options.render) {
result = {ast: cleanAst, report: result.report};
}
}
syncFinished = true;
return cb(null, result);
function writeOutput(result) {
if (!options.output) {
return finish(result);
}
function writeOutput(result) {
if (!options.output) {
return finish(result);
}
progressCb({
type: 'write-output',
message: 'write output'
});
function writeFile(file, cb) {
var filePath = Path.join(outputDir, file);
// this will not work for sync, but we shouldn't be writing files
// for sync anyway
mkdirp(Path.dirname(filePath), parseInt(755, 8), function (err) {
if (err) {
return cb(err);
}
var fileString = result.files[file];
if (typeof fileString !== 'string') {
try {
// if circular references were introduced, this will fail
fileString = JSON.stringify(fileString, null, 2);
} catch (e) {
return cb(e);
}
}
fs.writeFile(filePath, fileString, cb);
});
}
async.forEach(Object.keys(result.files), writeFile, function (err) {
progressCb({
type: 'write-output',
message: 'write output'
});
function writeFile(file, cb) {
var filePath = Path.join(outputDir, file);
mkdirp(Path.dirname(filePath), parseInt(755, 8), function (err) {
if (err) {
return cb(err);
}
finish(result);
var fileString = result.files[file];
if (typeof fileString !== 'string') {
try {
// if circular references were introduced, this will fail
fileString = JSON.stringify(fileString, null, 2);
} catch (e) {
return cb(e);
}
}
fs.writeFile(filePath, fileString, cb);
});
}
async.forEach(Object.keys(result.files), writeFile, function (err) {
if (err) {
return cb(err);
}
finish(result);
});
}
function renderReport(result) {
progressCb({
type: 'render-report',
message: 'render report'
});
result.files = {};
if (!result.report) {
return writeOutput(result);
function renderReport(result) {
progressCb({
type: 'render-report',
message: 'render report'
});
result.files = {};
if (!result.report) {
return writeOutput(result);
}
render(options, result.report, function (err, files) {
if (err) {
return cb(err);
}
render(options, result.report, function (err, files) {
if (err) {
return cb(err);
}
Object.keys(files).forEach(function (file, i) {
result.files[file] = files[file];
});
return writeOutput(result);
Object.keys(files).forEach(function (file, i) {
result.files[file] = files[file];
});
}
return writeOutput(result);
});
}
// function postProcess(result, cb) {
// if (!result.report) {
// return renderReport(result);
// }
// result.report = postProcessReport(result.report);
// return renderReport(result);
// }
// function postProcess(result, cb) {
// if (!result.report) {
// return renderReport(result);
// }
// result.report = postProcessReport(result.report);
// return renderReport(result);
// }
// function followRequired(result) {
// pickupRequiredFiles(options, astList, astPathList, result, function (err, newResult) {
// if (err) {
// return cb(err);
// }
// return postProcess(newResult);
// });
// }
// function followRequired(result) {
// pickupRequiredFiles(options, astList, astPathList, result, function (err, newResult) {
// if (err) {
// return cb(err);
// }
// return postProcess(newResult);
// });
// }
function doMakeReport(ast) {
progressCb({
type: 'make-report',
message: 'make report'
});
makeReport(options, ast, function (err, report) {
if (err) {
return cb(err);
}
//return followRequired(report);
return renderReport(report);
//return postProcess(report);
});
}
function doMakeReport(ast) {
progressCb({
type: 'make-report',
message: 'make report'
});
makeReport(options, ast, function (err, report) {
if (err) {
return cb(err);
}
//return followRequired(report);
return renderReport(report);
//return postProcess(report);
});
}
function doTransformAst() {
progressCb({
type: 'transform-ast',
message: 'transform ast'
});
transformAst(options, astList, function (err, transformedAst) {
if (err) {
return cb(err);
}
return doMakeReport(transformedAst);
});
}
function doTransformAst() {
progressCb({
type: 'transform-ast',
message: 'transform ast'
});
transformAst(options, astList, function (err, transformedAst) {
if (err) {
return cb(err);
}
return doMakeReport(transformedAst);
});
}
function hoistToc(astList) {
var tocIndex = -1;
for (var i = 0; i < astList.length; i++) {
var ast = astList[i];
if (ast.type === 'markdown') {
if (ast.path === 'TOC.md' || ast.path === 'toc.md') {
tocIndex = i;
break;
}
function hoistToc(astList) {
var tocIndex = -1;
for (var i = 0; i < astList.length; i++) {
var ast = astList[i];
if (ast.type === 'markdown') {
if (ast.path === 'TOC.md' || ast.path === 'toc.md') {
tocIndex = i;
break;
}
}
if (tocIndex >= 0) {
var tocAst = astList[tocIndex];
astList.splice(tocIndex, 1);
astList.splice(0, 0, tocAst);
}
}
function doGetAst(moduleInfo) {
moduleInfo = moduleInfo || {};
(function (originalOptions) {
var options = _.clone(originalOptions);
options.files = moduleInfo.sorted || options.files;
getAst(options, astList, astPathList, function (err) {
if (err) {
return cb(err);
}
if (moduleInfo.sorted) {
astList.forEach(function (ast, i) {
var m = {};
if (moduleInfo.pathToModule &&
ast.fullPath in moduleInfo.pathToModule) {
m = moduleInfo.pathToModule[ast.fullPath];
if (!ast.package && m.package) {
ast.package = m.package;
}
}
if (moduleInfo.modulePaths.indexOf(ast.fullPath) < 0) {
ast.required = true;
}
});
}
hoistToc(astList);
return doTransformAst();
});
}(options));
if (tocIndex >= 0) {
var tocAst = astList[tocIndex];
astList.splice(tocIndex, 1);
astList.splice(0, 0, tocAst);
}
}
function followRequired() {
if (!options.follow) {
return doGetAst();
}
progressCb({
type: 'follow-required',
message: 'find required modules'
});
findRequired(options, options.files, function (err, moduleInfo) {
function doGetAst(moduleInfo) {
moduleInfo = moduleInfo || {};
(function (originalOptions) {
var options = _.clone(originalOptions);
options.files = moduleInfo.sorted || options.files;
getAst(options, astList, astPathList, function (err) {
if (err) {
return cb(err);
}
return doGetAst(moduleInfo);
if (moduleInfo.sorted) {
astList.forEach(function (ast, i) {
var m = {};
if (moduleInfo.pathToModule &&
ast.fullPath in moduleInfo.pathToModule) {
m = moduleInfo.pathToModule[ast.fullPath];
if (!ast.package && m.package) {
ast.package = m.package;
}
}
if (moduleInfo.modulePaths.indexOf(ast.fullPath) < 0) {
ast.required = true;
}
});
}
hoistToc(astList);
return doTransformAst();
});
}
}(options));
}
function getCommentParser() {
progressCb({
type: 'get-comment-parser',
message: 'get comment parser'
});
nast.pegParser({grammar: 'comment-tags', sync: options.sync}, function (err, parser) {
if (err) {
return cb(err);
}
options.commentParser = parser;
return followRequired();
});
function followRequired() {
if (!options.follow) {
return doGetAst();
}
// this will not run synchronously
function copyView() {
if (!options.view) {
return getCommentParser();
progressCb({
type: 'follow-required',
message: 'find required modules'
});
findRequired(options, options.files, function (err, moduleInfo) {
if (err) {
return cb(err);
}
options.view = Array.isArray(options.view) ? options.view : [options.view];
function eachView(view, cb) {
progressCb({
type: 'copy-view',
message: 'copy view: ' + view
});
var viewChoices = [view, Path.join(__dirname, '../view', view)];
u.findDir(viewChoices,
function viewExists(dir) {
ncp(dir, outputDir, {stopOnError: true}, function (err) {
cb(err);
});
},
function viewNotFound() {
cb(new Error('View directory ' + view + ' does not exist.'));
}
);
}
async.forEachSeries(options.view, eachView, function (err) {
if (err) {
cb(err);
}
return getCommentParser();
});
}
return doGetAst(moduleInfo);
});
}
function checkOutputDir() {
if (!outputDir) {
return copyView();
function getCommentParser() {
progressCb({
type: 'get-comment-parser',
message: 'get comment parser'
});
nast.pegParser({grammar: 'comment-tags'}, function (err, parser) {
if (err) {
return cb(err);
}
var fsExists = fs.exists || Path.exists;
fsExists(outputDir, function (exists) {
if (!exists) {
return cb(new Error('output directory ' + outputDir + ' does not exist'));
}
return copyView();
});
}
checkOutputDir();
options.commentParser = parser;
return followRequired();
});
}
if (options.sync) {
if (!syncFinished) {
throw new Error('Problem attempting synchronous examine.');
} else {
origCb(syncError, syncResult);
}
function copyView() {
if (!options.view) {
return getCommentParser();
}
}(Path, fs, async, u));
}
/*
__EXPERIMENTAL!__ __(and not even close to finished)__ Registers a compiler
for the .js extension that will compile harmony files to ECMAScript 5.
@private
*/
function harmony() {
function compiler(module, filename) {
// don't attempt to run doctor code through harmony compiler before
// doctor's dynamic requires are finished; otherwise, we'll deadlock
if (!harmony.isStarted || harmony.isReady) {
harmony.isStarted = true;
var options = {
files: [filename],
grammar: 'harmony',
report: ['ast'],
render: ['source'],
transform: ['harmony-es5'],
sync: true,
follow: false
};
var source;
var error;
examine(options, function (err, report) {
if (err) {
error = err;
} else {
var key = Object.keys(report)[0];
source = report[key];
}
harmony.isReady = true;
options.view = Array.isArray(options.view) ? options.view : [options.view];
function eachView(view, cb) {
progressCb({
type: 'copy-view',
message: 'copy view: ' + view
});
//return module._compile(content, filename);
if (error) {
//throw error;
throw SyntaxError("In " + filename + ", " + error.message + " on line " + error.line);
}
if (!error) {
try {
return module._compile(source, filename);
} catch (e) {
return module._compile(fs.readFileSync(filename, 'utf-8'), filename);
var viewChoices = [view, Path.join(__dirname, '../view', view)];
u.findDir(viewChoices,
function viewExists(dir) {
ncp(dir, outputDir, {stopOnError: true}, function (err) {
cb(err);
});
},
function viewNotFound() {
cb(new Error('View directory ' + view + ' does not exist.'));
}
} else {
return module._compile(fs.readFileSync(filename, 'utf-8'), filename);
);
}
async.forEachSeries(options.view, eachView, function (err) {
if (err) {
cb(err);
}
} else {
return module._compile(fs.readFileSync(filename, 'utf-8'), filename);
}
return getCommentParser();
});
}
require.extensions['.js'] = compiler;
}
/*
__EXPERIMENTAL!__ Registers a compiler for the .js extension that will catch
function entry/exit.
@param options {Object|RegExp} Pass in a regular expression apply the compiler
only to paths matching that regular expression.
@param enterCb {function} Calls this function (passing it info) when a
function is entered.
@param exitCb {Fucntion} Calls this function (passing it info) when a function
is exited.
@example
var doctor = require('doctor');
function hookMessage(title, info) {
return title + " " + info.name + ":\n" +
" type: " + info.type + "\n" +
" line: " + info.line + "\n" +
" column: " + info.column + "\n" +
" path: " + info.filename;
}
doctor.hookEnterExit(
/foobar/,
function (info) {
console.log(hookMessage("Enter", info));
},
function (info) {
console.log(hookMessage("Exit", info));
function checkOutputDir() {
if (!outputDir) {
return copyView();
}
);
require('./foobar');
@private
*/
function hookEnterExit(options, enterCb, exitCb) {
if (typeof options === 'function') {
exitCb = enterCb;
enterCb = options;
options = {};
}
if (options instanceof RegExp) {
var re = options;
options = {include: re};
}
enterCb = enterCb || function () {};
if (typeof exitCb === 'undefined') {
exitCb = enterCb;
} else {
exitCb = exitCb || function () {};
}
hookEnterExit.enterCb = enterCb;
hookEnterExit.exitCb = exitCb;
function includeFile(filename) {
//console.log(filename);
return filename.match(options.include);
}
function compiler(module, filename) {
if (!includeFile(filename)) {
return module._compile(fs.readFileSync(filename, 'utf-8'), filename);
}
//console.log("FILE:" + filename)
// don't attempt to run doctor code through enter-exit compiler before
// doctor's dynamic requires are finished; otherwise, we'll deadlock
if (!hookEnterExit.isStarted || hookEnterExit.isReady) {
hookEnterExit.isStarted = true;
var options = {
files: [filename],
grammar: 'javascript',
report: ['ast'],
render: ['source'],
transform: ['enter-exit'],
sync: true,
follow: false,
hookDoctorPath: __filename
};
var source;
var error;
examine(options, function (err, report) {
if (err) {
error = err;
} else {
var key = Object.keys(report)[0];
source = report[key];
}
//console.log(err);
//console.log(source);
hookEnterExit.isReady = true;
});
//return module._compile(content, filename);
if (error) {
//throw error;
throw new SyntaxError("In " + filename + ", " + error.message + " on line " + error.line);
var fsExists = fs.exists || Path.exists;
fsExists(outputDir, function (exists) {
if (!exists) {
return cb(new Error('output directory ' + outputDir + ' does not exist'));
}
if (!error) {
try {
var m = module._compile(source, filename);
return m;
} catch (e) {
return module._compile(fs.readFileSync(filename, 'utf-8'), filename);
}
} else {
return module._compile(fs.readFileSync(filename, 'utf-8'), filename);
}
} else {
return module._compile(fs.readFileSync(filename, 'utf-8'), filename);
}
return copyView();
});
}
require.extensions['.js'] = compiler;
checkOutputDir();
}
function _callEnterHook() {
hookEnterExit.enterCb.apply(null, arguments);
}
function _callExitHook() {
hookEnterExit.exitCb.apply(null, arguments);
}
var enterExitHooks = {};
function _callHook(type, filename, info) {
info.hook = type;
if (filename) {
enterExitHooks[filename][type + 'Cb'].call(null, info);
} else {
hookEnterExit[type + 'Cb'].call(null, info);
}
}
function _wrapCall(fn, ctx, args, filename, info) {
_callHook('enter', filename, info);
var result = fn.apply(ctx, args);
_callHook('exit', filename, info);
return result;
}
/*
__EXPERIMENTAL__ Inserts enter/exit hooks into a JavaScript file. Registers
callback hooks for that file.
@param filename {String} Filename of file to trasform. Callback hooks are
registered to this filename.
@param enterCb {function} Function to call when entering function. If exitCb
is not defined, enterCb will be called for exit as well.
@param [exitCb] {function} Function to call when exiting function.
@param [sourceCb] {function} Function to call when building the source for a function.
@example
var doctor = require('doctor');
var source = doctor.insertEnterExitHooksSync('foobar.js', function (info) {
console.log(JSON.stringify(info, null, 2));
});
eval(source);
@private
*/
function insertEnterExitHooksSync(filename, enterCb, exitCb, sourceCb) {
var options = {
files: [filename],
grammar: 'javascript',
transform: ['enter-exit'],
report: ['ast'],
render: ['source'],
sync: true,
follow: false,
hookFilename: filename,
hookSourceCb: sourceCb,
hookDoctorPath: __filename
};
enterCb = enterCb || function () {};
if (typeof exitCb === 'undefined') {
exitCb = enterCb;
} else {
exitCb = exitCb || function () {};
}
enterExitHooks[filename] = {enterCb: enterCb, exitCb: exitCb};
var source;
examine(options, function (error, report) {
if (error) {
throw new SyntaxError("In " + filename + ", " + error.message + " on line " + error.line);
} else {
var key = Object.keys(report)[0];
source = report[key];
}
});
return source;
}
module.exports.examine = examine;
module.exports.harmony = harmony;
module.exports.hookEnterExit = hookEnterExit;
module.exports._callEnterHook = _callEnterHook;
module.exports._callExitHook = _callExitHook;
module.exports.insertEnterExitHooksSync = insertEnterExitHooksSync;
module.exports._callHook = _callHook;
module.exports._wrapCall = _wrapCall;
module.exports.examine = examine;

@@ -1,7 +0,7 @@

var fs = require('fs');
var peg = require('pegjs');
var path = require('path');
var fs = require('fs');
var async = require('async');
var fsCompare = require('fs-compare');
var quickpeg = require('quickpeg').config({pegjs: {cache: true}});

@@ -11,4 +11,2 @@ var AstNode = require('./ast-node');

var pegParsers = {};
/*

@@ -142,107 +140,68 @@ Walks a tree of AST nodes.

function pegParser(options, extension, cb) {
(function (path, fs, async, u) {
if (options.sync) {
path = u.sync.path;
fs = u.sync.fs;
async = u.sync.async;
u = u.sync;
}
if (typeof cb === 'undefined') {
cb = extension;
extension = null;
}
if (typeof cb === 'undefined') {
cb = extension;
extension = null;
var grammarFiles = [];
if (typeof options === 'string') {
options = {grammar: options};
}
var grammar = options.grammar;
if (typeof grammar === 'object') {
if (grammar[extension]) {
grammar = grammar[extension];
}
var grammarFiles = [];
if (typeof options === 'string') {
options = {grammar: options};
}
if (typeof grammar !== 'string') {
if (extension === 'js') {
grammar = 'javascript';
} else if (extension === 'md') {
grammar = 'markdown';
}
var grammar = options.grammar;
if (typeof grammar === 'object') {
if (grammar[extension]) {
grammar = grammar[extension];
}
}
if (typeof grammar !== 'string') {
if (extension === 'js') {
grammar = 'javascript';
} else if (extension === 'md') {
grammar = 'markdown';
}
}
if (grammar) {
grammarFiles.push(grammar);
grammarFiles.push(path.join(__dirname, '../grammar', grammar + '.pegjs'));
} else {
grammarFiles.push(path.join(__dirname, '../grammar/javascript.pegjs'));
}
}
if (grammar) {
grammarFiles.push(grammar);
grammarFiles.push(path.join(__dirname, '../grammar', grammar + '.pegjs'));
} else {
grammarFiles.push(path.join(__dirname, '../grammar/javascript.pegjs'));
}
var parser = null;
var parserGrammarFile = null;
var grammarErr = null;
function existsNewerCompiledGrammarFile(grammarFile, cb) {
u.compareFileDates(grammarFile, grammarFile + '.js', function (c) {
cb(c > 0);
});
function findGrammarFile(wrapper, cb) {
var grammarFile = wrapper.file;
var cachedParser = quickpeg.parserFromMemory(grammarFile);
if (cachedParser) {
wrapper.parser = cachedParser;
return cb(true);
}
function findGrammarFile(grammarFile, cb) {
if (pegParsers[grammarFile]) {
parser = pegParsers[grammarFile];
parserGrammarFile = grammarFile;
return cb(true);
var fsExists = fs.exists || path.exists;
fsExists(grammarFile, function (exists) {
if (!exists) {
return cb(false);
}
var fsExists = fs.exists || path.exists;
fsExists(grammarFile, function (exists) {
if (!exists) {
return cb(false);
quickpeg(grammarFile, function (err, cachedParser) {
if (err) {
wrapper.err = err;
return;
}
existsNewerCompiledGrammarFile(grammarFile, function (existsCompiled) {
if (existsCompiled) {
var cachedParser;
try {
cachedParser = require(grammarFile + '.js');
} catch (e) {
// something wrong with compiled grammar, just write new one
}
if (cachedParser && cachedParser.parse) {
parser = cachedParser;
pegParsers[grammarFile] = parser;
parserGrammarFile = grammarFile;
return cb(true);
}
}
fs.readFile(grammarFile, 'utf-8', function (err, data) {
if (err) {
grammarErr = err;
return cb(true);
}
try {
parser = peg.buildParser(data);
pegParsers[grammarFile] = parser;
parserGrammarFile = grammarFile;
// cache the grammar for next time
} catch (e) {
grammarErr = e;
return cb(true);
}
fs.writeFile(grammarFile + '.js', 'module.exports = ' + parser.toSource(), function (err) {
if (err) {
grammarErr = err;
}
return cb(true);
});
});
});
wrapper.parser = cachedParser;
return cb(true);
});
});
}
var grammarFileWrappers = grammarFiles.map(function (file) {
return {
parser: null,
file: file,
err: null
};
});
async.detectSeries(grammarFileWrappers, findGrammarFile, function (wrapper) {
if (wrapper.err || wrapper.parser) {
return cb(wrapper.err, wrapper.parser, wrapper.file);
}
async.some(grammarFiles, findGrammarFile, function () {
if (grammarErr || parser) {
return cb(grammarErr, parser, parserGrammarFile);
}
cb(new Error('grammar file not found'));
});
}(path, fs, async, u));
cb(new Error('grammar file not found'));
});
}

@@ -339,65 +298,58 @@

function astFromFile(options, file, cb) {
(function (fs) {
if (options.sync) {
fs = u.sync.fs;
}
function convertSourceFile(file, packageName, packagePath) {
fs.readFile(file, 'utf-8', function (err, source) {
function convertSourceFile(file, packageName, packagePath) {
fs.readFile(file, 'utf-8', function (err, source) {
if (err) {
return cb(err);
}
var extension = path.extname(file);
if (extension[0] === '.') {
extension = extension.substr(1);
}
astFromSource(options, source, extension, function (err, ast) {
if (err) {
err.file = file;
return cb(err);
}
var extension = path.extname(file);
if (extension[0] === '.') {
extension = extension.substr(1);
if (packageName) {
ast.package = {name: packageName, path: packagePath};
if (packagePath[0] !== '/') {
ast.package.path = path.join(process.cwd(), packagePath);
}
ast.package.originalPath = packagePath;
}
astFromSource(options, source, extension, function (err, ast) {
if (err) {
err.file = file;
return cb(err);
ast.originalPath = file;
if (file[0] === '/') {
ast.path = file;
} else {
ast.path = path.join(process.cwd(), file);
}
return cb(null, ast);
});
});
}
function convertPackage() {
if (path.basename(file) === 'package.json') {
fs.readFile(file, 'utf-8', function (err, pkgString) {
try {
var pkg = JSON.parse(pkgString);
if (!pkg.main) {
return cb(new Error(file + " has no main modules."));
}
if (packageName) {
ast.package = {name: packageName, path: packagePath};
if (packagePath[0] !== '/') {
ast.package.path = path.join(process.cwd(), packagePath);
}
ast.package.originalPath = packagePath;
var main = path.join(path.dirname(file), pkg.main);
if (!main.match(/\.js$/)) {
main = main + '.js';
}
ast.originalPath = file;
if (file[0] === '/') {
ast.path = file;
} else {
ast.path = path.join(process.cwd(), file);
}
return cb(null, ast);
});
convertSourceFile(main, pkg.name, file);
} catch (packageParseError) {
packageParseError.message = 'Error parsing ' + file + ' - ' + packageParseError.message;
return cb(packageParseError);
}
});
} else {
convertSourceFile(file, null);
}
}
function convertPackage() {
if (path.basename(file) === 'package.json') {
fs.readFile(file, 'utf-8', function (err, pkgString) {
try {
var pkg = JSON.parse(pkgString);
if (!pkg.main) {
return cb(new Error(file + " has no main modules."));
}
var main = path.join(path.dirname(file), pkg.main);
if (!main.match(/\.js$/)) {
main = main + '.js';
}
convertSourceFile(main, pkg.name, file);
} catch (packageParseError) {
packageParseError.message = 'Error parsing ' + file + ' - ' + packageParseError.message;
return cb(packageParseError);
}
});
} else {
convertSourceFile(file, null);
}
}
convertPackage();
}(fs));
convertPackage();
}

@@ -645,6 +597,3 @@

function parserForGrammar(grammarFile) {
if (grammarFile in pegParsers) {
return pegParsers[grammarFile];
}
return null;
return quickpeg.parserFromMemory(grammarFile);
}

@@ -651,0 +600,0 @@

@@ -13,47 +13,40 @@ var async = require('async');

function render(options, report, cb) {
(function (async, u) {
if (options.sync) {
async = u.sync.async;
u = u.sync;
}
var reportName = options.outputReportName || 'report.json';
var files = {};
files[reportName] = report;
var renderFunctions = u.toArray(options.render || 'default');
try {
renderFunctions = u.findFunctions(renderFunctions, 'render');
} catch (findError) {
return cb(findError);
}
function eachRender(fn, cb) {
fn(options, files, function (err, renderedFiles) {
if (err) {
return cb(err);
}
files = renderedFiles;
cb(null, files);
});
}
async.forEachSeries(renderFunctions, eachRender, function (err) {
// if (err) {
// return cb(err);
// }
// Object.keys(files).forEach(function (filename, i) {
// var file = files[filename];
// if (typeof file !== 'string') {
// try {
// // if circular references were introduced, this will fail
// var fileString = JSON.stringify(file, null, 2);
// files[filename] = fileString;
// } catch (jsonError) {
// return cb(jsonError);
// }
// }
// });
cb(err, files);
var reportName = options.outputReportName || 'report.json';
var files = {};
files[reportName] = report;
var renderFunctions = u.toArray(options.render || 'default');
try {
renderFunctions = u.findFunctions(renderFunctions, 'render');
} catch (findError) {
return cb(findError);
}
function eachRender(fn, cb) {
fn(options, files, function (err, renderedFiles) {
if (err) {
return cb(err);
}
files = renderedFiles;
cb(null, files);
});
}(async, u));
}
async.forEachSeries(renderFunctions, eachRender, function (err) {
// if (err) {
// return cb(err);
// }
// Object.keys(files).forEach(function (filename, i) {
// var file = files[filename];
// if (typeof file !== 'string') {
// try {
// // if circular references were introduced, this will fail
// var fileString = JSON.stringify(file, null, 2);
// files[filename] = fileString;
// } catch (jsonError) {
// return cb(jsonError);
// }
// }
// });
cb(err, files);
});
}
module.exports = render;
/*
This module contains various utility functions used by doctor.
It notably contains some nasty synchronous versions of asynchronous modules.
Say what?
Yeah, that's right. This is so doctor can run synchronously or ansynhronously.
Doctor wants to run asynchronously, so when we have to run synchronously (such
as during that ugly startup phase where you have to deal with synchronous
require), we have to fake it out with synchronous versions of asynchronous
modules. This is ugly, but it's better than having to write two different
versions of doctor.
*/

@@ -21,110 +10,2 @@

// turn synchronous things into callbacks to they can stand in
// for asynchronous things
function callbackify(thing, canError) {
if (typeof thing === 'function') {
return function () {
try {
var result = thing.apply(this, Array.prototype.slice.call(arguments, 0, arguments.length - 1));
if (canError) {
arguments[arguments.length - 1](null, result);
} else {
arguments[arguments.length - 1](result);
}
} catch (err) {
if (canError) {
arguments[arguments.length - 1](err);
} else {
arguments[arguments.length - 1](false);
}
}
};
} else if (typeof thing === 'object' && !_.isArray(thing)) {
var copy = _.extend({}, thing);
_(copy).each(function (f, key) {
copy[key].__name = key;
if (key.substring(key.length - 4) === 'Sync') {
copy[key.substring(0, key.length - 4)] = callbackify(f,
key === 'existsSync' ? false : true);
copy[key.substring(0, key.length - 4)].__name = key;
}
});
return copy;
} else {
return thing;
}
}
var fsSync = callbackify(fs);
var pathSync = callbackify(path);
// hack in a synchronous version of async
var asyncSync = {
/*
Synchronous forEachSeries, with an asynchronous signature. Because it's
synchronous, it can also be used as the forEach function.
See the async module for details.
*/
forEachSeries: function (array, eachCb, finalCb) {
var error = null;
function flag(err) {
if (err) {
error = err;
}
}
for (var i = 0; i < array.length; i++) {
var item = array[i];
eachCb(item, flag);
if (error) {
break;
}
}
finalCb(error);
},
/*
Synchronous detect, with an asynchronous signature.
See the async module for details.
*/
detect: function (array, passCb, resultCb) {
var detected;
var detectedValue;
function flag(yes) {
detected = yes;
if (detected) {
detectedValue = this;
}
}
for (var i = 0; i < array.length; i++) {
var item = array[i];
passCb(item, flag.bind(item));
if (detected) {
break;
}
}
resultCb(detectedValue);
},
/*
Synchronous some, with an asynchronous signature.
See the async module for details.
*/
some: function (array, passCb, doneCb) {
var detected;
function flag(yes) {
detected = yes;
}
for (var i = 0; i < array.length; i++) {
var item = array[i];
passCb(item, flag);
if (detected) {
break;
}
}
doneCb(detected);
}
};
asyncSync.forEach = asyncSync.forEachSeries;
/*

@@ -159,45 +40,2 @@ Take an array or a value, and if it's a value, wrap it in an array.

/*
Compare the modified time stamps of two files.
@param {String} a Path to first file.
@param {String} b Path to second file.
@param {function} cb Function to call with comparison value.
@param {boolean} sync Flag to force function to run synchronously.
*/
function compareFileDates(a, b, cb, sync) {
(function (fs) {
if (sync) {
fs = fsSync;
}
fs.stat(a, function (errA, statA) {
fs.stat(b, function (errB, statB) {
if (errA && errB) {
// assuming neither file exists
return cb(0);
}
if (errA) {
return cb(1);
}
if (errB) {
return cb(-1);
}
if (statB.mtime > statA.mtime) {
return cb(1);
}
if (statB.mtime < statA.mtime) {
return cb(-1);
}
return cb(0);
});
});
}(fs));
}
/*
@copy compareFileDates
*/
function compareFileDatesSync(a, b, cb) {
compareFileDates(a, b, cb, true);
}
/*
Require the specified rule modules, either from the default location or from

@@ -293,27 +131,17 @@ the user-specified location.

/* Check if a directory exists. */
function dirExists(dir, cb, sync) {
(function (path, fs) {
if (sync) {
path = pathSync;
fs = fsSync;
function dirExists(dir, cb) {
var fsExists = fs.exists || path.exists;
fsExists(dir, function (exists) {
if (!exists) {
return cb(false);
}
var fsExists = fs.exists || path.exists;
fsExists(dir, function (exists) {
if (!exists) {
fs.stat(dir, function (err, stat) {
if (err) {
return cb(false);
}
fs.stat(dir, function (err, stat) {
if (err) {
return cb(false);
}
return cb(stat.isDirectory());
});
return cb(stat.isDirectory());
});
}(path, fs));
});
}
function dirExistsSync(dir, cb) {
dirExists(dir, cb, true);
}
/*

@@ -324,29 +152,15 @@ Find one of a list of files.

@param {function} elseCb If one is not found, do this.
@param {boolean} sync Flag to force function to run synchronously.
*/
function findFile(fileList, ifCb, elseCb, sync) {
(function (async, fs, path) {
if (sync) {
async = asyncSync;
fs = fsSync;
function findFile(fileList, ifCb, elseCb) {
var fsExists = fs.exists || path.exists;
async.detect(fileList, fsExists, function (file) {
if (file) {
ifCb(file);
} else {
elseCb();
}
var fsExists = fs.exists || path.exists;
async.detect(fileList, fsExists, function (file) {
if (file) {
ifCb(file);
} else {
elseCb();
}
});
}(async, fs, path));
});
}
/*
@copy findFile
*/
function findFileSync(fileList, ifCb, elseCb) {
findFile(fileList, ifCb, elseCb, true);
}
/*
Find one of a list of directories.

@@ -356,29 +170,14 @@ @param {Array} dirList Possible directories to find.

@param {function} elseCb If one is not found, do this.
@param {boolean} sync Flag to force function to run synchronously.
*/
function findDir(dirList, ifCb, elseCb, sync) {
(function (async, path, dirExists) {
if (sync) {
async = asyncSync;
path = pathSync;
dirExists = dirExistsSync;
function findDir(dirList, ifCb, elseCb) {
async.detect(dirList, dirExists, function (dir) {
if (dir) {
ifCb(dir);
} else {
elseCb();
}
async.detect(dirList, dirExists, function (dir) {
if (dir) {
ifCb(dir);
} else {
elseCb();
}
});
}(async, path, dirExists));
});
}
/*
@copy findDir
*/
function findDirSync(dirList, ifCb, elseCb) {
findDir(dirList, ifCb, elseCb, true);
}
/*
Compress absolute paths down to minimal non-colliding lengths.

@@ -468,20 +267,5 @@ @param {Array} pathList List of absolute paths.

module.exports.findDir = findDir;
module.exports.compareFileDates = compareFileDates;
module.exports.toArray = toArray;
module.exports.localizePaths = localizePaths;
module.exports.cleanUndefinedProperties = cleanUndefinedProperties;
module.exports.isCapitalized = isCapitalized;
module.exports.fs = fs;
module.exports.path = path;
module.exports.async = async;
var sync = {};
_.extend(sync, module.exports);
sync.compareFileDates = compareFileDatesSync;
sync.findFile = findFileSync;
sync.findDir = findDirSync;
sync.fs = fsSync;
sync.path = pathSync;
sync.async = asyncSync;
module.exports.sync = sync;
module.exports.isCapitalized = isCapitalized;

@@ -5,3 +5,3 @@ {

"description": "Create documentation from or otherwise analyze/transform JavaScript source.",
"version": "0.2.3",
"version": "0.3.0",
"homepage": "https://github.com/jdeal/doctor",

@@ -13,19 +13,17 @@ "repository": {

"main": "lib/doctor",
"engines": {
"node": ">= 0.4.12"
},
"dependencies": {
"optimist": "~0.3.1",
"async": "~0.1.9",
"ncp": "=0.2.6",
"pegjs": "=0.6.2",
"async": "~0.2.9",
"ncp": "=0.4.2",
"handlebars": "~1.0.0",
"underscore": "~1.2.2",
"mkdirp": "~0.3.4",
"marked": "~0.2.5"
"mkdirp": "=0.3.4",
"marked": "=0.2.5",
"fs-compare": "=0.0.4",
"quickpeg": "=0.0.3"
},
"devDependencies": {
"mocha": ">=0.12.1",
"chai": ">=0.3.3",
"js-yaml": "~0.3.5"
"mocha": "=0.12.1",
"chai": "=0.3.3",
"js-yaml": "=0.3.5"
},

@@ -32,0 +30,0 @@ "bin": {

@@ -5,6 +5,2 @@ # doctor

__Doctor is still under development, so be careful.__ It is probably stable enough to be useful, but no promises.
![pinch](https://github.com/jdeal/doctor/raw/master/images/pinch-points-warning-143.png)
Doctor converts JavaScript source to documentation, using rules to rely on

@@ -11,0 +7,0 @@ conventions so that comment tags are (mostly) not needed.

@@ -948,4 +948,2 @@ var Path = require('path');

if (modulesToRemove.indexOf(moduleName) >= 0) {
//console.log(key)
//console.log(item.visibility);
if (item.visibility === 'public') {

@@ -1128,45 +1126,2 @@ // don't remove public item

// rules.push({
// type: 'markdown-link',
// report: function (node, report) {
// var isToc = node.item('isToc');
// if (isToc) {
// if (node.nodes && node.nodes[0] && node.nodes[0].value) {
// return {
// key: ''
// }
// }
// console.log(node.nodes[0].value)
// }
// }
// });
// rules.push({
// type: 'markdown-content',
// report: function (node, report) {
// var content = node.item('document-content');
// content.push(node.value);
// }
// });
// rules.push({
// type: 'end-markdown',
// report: function (node, report) {
// var content = node.item('document-content');
// content = content.join('');
// var item = node.item('document-item');
// item.content = content;
// }
// });
// rules.push({
// type: '',
// match: function (node) {
// return node.type.substring(0, 'markdown-'.length) === 'markdown-';
// },
// report: function (node, report) {
// console.log("begin:" + node.type);
// }
// });
module.exports = rules;

@@ -1173,0 +1128,0 @@

@@ -1,9 +0,8 @@

/*global suite:false, test:false*/
/*global suite:false, test:false, before:false*/
var peg = require('pegjs');
var fs = require('fs');
var Path = require('path');
var quickpeg = require('quickpeg');
var grammar = fs.readFileSync(Path.join(__dirname, '../grammar/comment-tags.pegjs'), 'utf8');
var parser = peg.buildParser(grammar);
var parser;

@@ -14,2 +13,9 @@ var assert = require('chai').assert;

before(function (done) {
quickpeg(Path.join(__dirname, '../grammar/comment-tags.pegjs'), function (err, p) {
parser = p;
done();
});
});
test('simple returns tag', function () {

@@ -16,0 +22,0 @@ var comment = "@returns description";

@@ -67,52 +67,2 @@ /*global suite:false, test:false*/

test('compareFileDates old file, new file', function (done) {
var newFile = 'new1.txt';
fs.writeFileSync(newFile, 'data');
util.compareFileDates(__filename, newFile, function (cmp) {
assert.equal(cmp, 1);
fs.unlinkSync(newFile);
done();
});
});
test('compareFileDates new file, old file', function (done) {
var newFile = 'new2.txt';
fs.writeFileSync(newFile, 'data');
util.compareFileDates(newFile, __filename, function (cmp) {
assert.equal(cmp, -1);
fs.unlinkSync(newFile);
done();
});
});
test('compareFileDates same file', function (done) {
util.compareFileDates(__filename, __filename, function (cmp) {
assert.equal(cmp, 0);
done();
});
});
test('compareFileDates file a does not exist', function (done) {
util.compareFileDates('doesNotExist', __filename, function (cmp) {
assert.equal(cmp, 1);
done();
});
});
test('compareFileDates file b does not exist', function (done) {
util.compareFileDates(__filename, 'doesNotExist', function (cmp) {
assert.equal(cmp, -1);
done();
});
});
test('compareFileDates neither file exists', function (done) {
util.compareFileDates('doesNotExist', 'doesNotExistEither', function (cmp) {
assert.equal(cmp, 0);
done();
});
});
test('findFunctions', function () {

@@ -119,0 +69,0 @@ var dir = 'render';

@@ -7,2 +7,6 @@ # [Overview](README.md)

## [count-console-log](examples/count-console-log/README.md)
## [count-console-log](examples/count-console-log/README.md)
## [public-api](examples/public-api/README.md)
## [remove-debug](examples/remove-debug/README.md)

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc