diff --git a/bin/jsctags b/bin/jsctags index 47f0d59..6d91198 100755 --- a/bin/jsctags +++ b/bin/jsctags @@ -1,18 +1,18 @@ #!/usr/bin/env node -var format = require('util').format; -var forceArray = require('force-array'); -var argv = require('optimist').argv; -var path = require('path'); -var glob = require('glob'); -var collect = require('collect-stream'); -var flatten = require('lodash.flatten'); -var async = require('async'); -var fs = require('fs'); - -var jsctags = require('../'); - -var dir = (function() { +const format = require('util').format; +const forceArray = require('force-array'); +const argv = require('optimist').argv; +const path = require('path'); +const glob = require('glob'); +const collect = require('collect-stream'); +const flatten = require('lodash.flatten'); +const async = require('async'); +const fs = require('fs'); + +const jsctags = require('../'); + +const dir = (function () { if (argv.dir) { return path.resolve(argv.dir); } @@ -28,7 +28,7 @@ var dir = (function() { return '///null'; })(); -var file = (function() { +const file = (function () { if (argv.file) { return path.resolve(process.cwd(), argv.file); } @@ -37,40 +37,40 @@ var file = (function() { return format('///null/%s', Math.floor(Math.random() * 100)); } - var find = forceArray(argv.find); - var files = !find.length - ? argv._ - : find.reduce(function(files, pattern) { - return files.concat( - glob.sync(pattern, { - nosort: true, - silent: true - }) - ); - }, []); - - return files.map(function(file) { + const find = forceArray(argv.find); + const files = find.length ? + find.reduce((files, pattern) => { + return files.concat( + glob.sync(pattern, { + nosort: true, + silent: true + }) + ); + }, []) : + argv._; + + return files.map(file => { return path.resolve(process.cwd(), file); }); })(); -var outputTags = function(tags) { - var ctags = flatten(jsctags.ctags(tags)); +const outputTags = function (tags) { + const ctags = flatten(jsctags.ctags(tags)); console.log(ctags.join('')); }; -var outputJSON = function(tags) { +const outputJSON = function (tags) { console.log(JSON.stringify(flatten(forceArray(tags)), null, 2)); }; -var onResults = function(err, results) { +const onResults = function (err, results) { if (err) { throw err; } - var fn = argv.f ? outputTags : outputJSON; + const fn = argv.f ? outputTags : outputJSON; - var tags = results.filter(function(res) { + const tags = results.filter(res => { if (res instanceof Error) { console.error(err); return false; @@ -84,14 +84,14 @@ var onResults = function(err, results) { fn(tags); }; -var parse = function(ctx, fn) { - jsctags(ctx, function(err, tags) { +const parse = function (ctx, fn) { + jsctags(ctx, (err, tags) => { if (err) { return fn(err); } tags.tagfile = ctx.file; - tags.forEach(function(tag) { + tags.forEach(tag => { tag.tagfile = ctx.file; }); @@ -99,8 +99,8 @@ var parse = function(ctx, fn) { }); }; -var fromStdin = function() { - collect(process.stdin, function(err, content) { +const fromStdin = function () { + collect(process.stdin, (err, content) => { if (err) { throw err; } @@ -111,30 +111,30 @@ var fromStdin = function() { parse( { - file: file, - dir: dir, - content: content + file, + dir, + content }, onResults ); }); }; -var fromFiles = function() { - var files = Array.isArray(file) ? file : [file]; +const fromFiles = function () { + const files = Array.isArray(file) ? file : [file]; async.map( files, - function(file, fn) { - fs.readFile(file, 'utf8', function(err, content) { + (file, fn) => { + fs.readFile(file, 'utf8', (err, content) => { if (err) { return fn(null, err); } parse( { - file: file, - dir: dir, - content: content + file, + dir, + content }, fn ); diff --git a/bin/xjsctags b/bin/xjsctags index 9df4369..6dc22aa 100755 --- a/bin/xjsctags +++ b/bin/xjsctags @@ -1,18 +1,18 @@ #!/usr/bin/env node -var path = require('path'); -var cp = require('child_process'); +const path = require('path'); +const cp = require('child_process'); -var jsctags = path.join(__dirname, 'jsctags'); +const jsctags = path.join(__dirname, 'jsctags'); -var bin = cp.execFile( +const bin = cp.execFile( jsctags, process.argv.shift(), { cwd: process.cwd(), env: process.env }, - function(err, stderr, stdout) { + (err, stderr, stdout) => { if (err) { throw err; } diff --git a/package.json b/package.json index 15adace..1898d03 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ }, "scripts": { "test": "NODE_ENV=test nyc node test", - "lint": "eslint . --fix", + "lint": "eslint . bin/* --fix", "coverage": "nyc report --reporter=text-lcov | codeclimate-test-reporter" }, "dependencies": { diff --git a/src/condenser.js b/src/condenser.js index 52c0197..fbe0f3a 100644 --- a/src/condenser.js +++ b/src/condenser.js @@ -10,7 +10,7 @@ const once = require('once'); require('tern-jsx'); require('./local-scope'); -const config = function(dir, file) { +const config = function (dir, file) { const config = tryor(() => { return fs.readFileSync(path.join(dir, '.tern-project'), 'utf8'); }, '{}'); @@ -24,7 +24,7 @@ const config = function(dir, file) { }; const plugins = { - doc_comment: true, + doc_comment: true, // eslint-disable-line camelcase 'local-scope': true }; @@ -35,7 +35,7 @@ const config = function(dir, file) { }); }; -const defs = function(libs) { +const defs = function (libs) { const base = path.resolve(__dirname, '../node_modules/tern/defs'); return libs @@ -47,13 +47,15 @@ const defs = function(libs) { if (fs.existsSync(file)) { return require(file); } + + return null; }) .filter(lib => { return Boolean(lib); }); }; -const server = function(config, dir) { +const server = function (config, dir) { const base = path.resolve(__dirname, '../node_modules/tern/plugin'); Object.keys(config.plugins).forEach(plugin => { @@ -61,9 +63,8 @@ const server = function(config, dir) { if (fs.existsSync(file)) { return require(file); - } else { - return tryor(require.bind(require, 'tern-' + plugin)); } + return tryor(require.bind(require, 'tern-' + plugin)); }); return new tern.Server({ @@ -74,10 +75,10 @@ const server = function(config, dir) { }); }; -module.exports = function(options, fn) { +module.exports = function (options, fn) { const __fn = once(fn); - const _fn = function(err, tags) { + const _fn = function (err, tags) { if (err) { return __fn(err); } diff --git a/src/ctags.js b/src/ctags.js index a39a2f0..7f376c8 100644 --- a/src/ctags.js +++ b/src/ctags.js @@ -19,14 +19,14 @@ const SPECIAL_FIELDS = { parent: true }; -const convert = (module.exports = function(tags) { +const convert = function (tags) { return tags.map(tag => { if (isArray(tag)) { return convert(tag); } const buf = [tag.name, '\t', tags.tagfile, '\t']; - buf.push(tag.addr !== undefined ? tag.addr : '//'); + buf.push(tag.addr === undefined ? '//' : tag.addr); const tagfields = []; Object.keys(tag).forEach(key => { @@ -68,4 +68,6 @@ const convert = (module.exports = function(tags) { buf.push('\n'); return buf.join(''); }); -}); +}; + +module.exports = convert; diff --git a/src/local-scope/defnode.js b/src/local-scope/defnode.js index 00f9a00..26740b8 100644 --- a/src/local-scope/defnode.js +++ b/src/local-scope/defnode.js @@ -16,7 +16,7 @@ const walkall = require('./walkall'); // of a given definition. I.e., if you use tern to ask for the definition of // some symbol, and it gives you an Identifier, you can use findDefinitionNode // to find the definition value of the Identifier. -exports.findDefinitionNode = function(ast, start, end) { +exports.findDefinitionNode = function (ast, start, end) { const origin = exports.findOriginPseudonode(ast, start, end); if (!origin) { @@ -45,7 +45,7 @@ exports.findDefinitionNode = function(ast, start, end) { // // This mapping is intended to correspond to the mapping between a tern defs // JSON file !span and the names/paths of keys pointing to that !span. -exports.findNameNodes = function(ast, start, end) { +exports.findNameNodes = function (ast, start, end) { let def = walk.findNodeAt(ast, start, end, null, walkall.traversers); if (!def) { @@ -58,7 +58,7 @@ exports.findNameNodes = function(ast, start, end) { // When we search for the enclosing node, we don't want to just end up with // the def node itself, so exclude it (node != def). - const test = function(type, node) { + const test = function (type, node) { return ( (def.type === 'FunctionDeclaration' && type === 'FunctionDeclaration') || (node !== def && @@ -80,6 +80,8 @@ exports.findNameNodes = function(ast, start, end) { ); } + let prop = null; + enc = enc.node; switch (enc.type) { case 'AssignmentExpression': @@ -100,7 +102,7 @@ exports.findNameNodes = function(ast, start, end) { break; case 'ObjectExpression': - const prop = findPropInObjectExpressionByValuePos(enc, start, end); + prop = findPropInObjectExpressionByValuePos(enc, start, end); if (!prop) { throw new Error( @@ -114,9 +116,28 @@ exports.findNameNodes = function(ast, start, end) { return [prop.key]; case 'VariableDeclarator': return [enc.id]; + + default: + } +}; + +// IdentOrLiteralString takes an AST node whose type is either Identifier or +// Literal and returns the Identifier name or Literal string value. It is +// useful when you have a name node in an ObjectExpression property or +// MemberExpression property, which could be either an Identifier or Literal, +// and you just want to extract the string name. +const identOrLiteralString = function (n) { + if (n.type === 'Identifier') { + return n.name; + } + + if (n.type === 'Literal' && typeof n.value === 'string') { + return n.value; } }; +exports.identOrLiteralString = identOrLiteralString; + // FindOriginPseudonode finds the AST node or node-like object of the // declaration/definition that encloses the Identifier AST node with the // specified start/end. @@ -124,7 +145,7 @@ exports.findNameNodes = function(ast, start, end) { // This function returns ObjectExpression property objects if the Identifier is // an ObjectExpression property key. These objects are not true AST nodes (thus // the "pseudonode" description). -exports.findOriginPseudonode = function(ast, start, end) { +exports.findOriginPseudonode = function (ast, start, end) { let nameNode = walk.findNodeAt( ast, start, @@ -196,11 +217,13 @@ exports.findOriginPseudonode = function(ast, start, end) { return findPropInObjectExpressionByKeyPos(enc, start, end); case 'VariableDeclarator': return enc; + + default: } }; function okNodeTypes(types) { - return function(_t) { + return function (_t) { return types.indexOf(_t) !== -1; }; } @@ -254,7 +277,7 @@ function collectChainedAssignmentNames(ast, expr, seen) { seen.push(expr); // Traverse to parent AssignmentExpressions to return all names in chained assignments. - const test = function(type, node) { + const test = function (type, node) { return ( seen.indexOf(node) === -1 && ((type === 'AssignmentExpression' && node.right === expr) || @@ -272,18 +295,3 @@ function collectChainedAssignmentNames(ast, expr, seen) { return names; } - -// IdentOrLiteralString takes an AST node whose type is either Identifier or -// Literal and returns the Identifier name or Literal string value. It is -// useful when you have a name node in an ObjectExpression property or -// MemberExpression property, which could be either an Identifier or Literal, -// and you just want to extract the string name. -const identOrLiteralString = (exports.identOrLiteralString = function(n) { - if (n.type === 'Identifier') { - return n.name; - } - - if (n.type === 'Literal' && typeof n.value === 'string') { - return n.value; - } -}); diff --git a/src/local-scope/index.js b/src/local-scope/index.js index 4102d27..9d7eb9b 100644 --- a/src/local-scope/index.js +++ b/src/local-scope/index.js @@ -13,7 +13,7 @@ const infer = require('tern/lib/infer'); const defnode = require('./defnode'); const walkall = require('./walkall'); -const joinPaths = function(a, b) { +const joinPaths = function (a, b) { if (a) { return a + '.' + b; } @@ -21,14 +21,14 @@ const joinPaths = function(a, b) { return b; }; -const getId = function(n) { +const getId = function (n) { return format('%d-%d', n.start, n.end); }; -const postCondenseReach = function(server, options, state) { +const postCondenseReach = function (server, options, state) { const seenSpans = {}; - const visitScope = function(state, scope, path) { + const visitScope = function (state, scope, path) { // Detect cycles if (scope._localScopeCondenseSeen) { return; @@ -39,11 +39,11 @@ const postCondenseReach = function(server, options, state) { Object.keys(get(scope, 'props', {})) .sort() .forEach(prop => { - visitAVal(state, scope.props[prop], joinPaths(path, prop)); + visitAVal(state, scope.props[prop], joinPaths(path, prop)); // eslint-disable-line no-use-before-define }); }; - const visitNode = function(state, node, path) { + const visitNode = function (state, node, path) { if (!node) { return; } @@ -75,19 +75,19 @@ const postCondenseReach = function(server, options, state) { ); }; - const isArg = function(state, av) { + const isArg = function (state, av) { return get(av, 'propertyOf.fnType.args', []).some(arg => { return arg.propertyName === av.propertyName; }); }; - const isScoped = function(state, av) { + const isScoped = function (state, av) { const g = av.path === '' || isUndefined(get(av, 'propertyOf.isBlock')); return g ? false : isArg(state, av); }; - const getType = function(state, av, proto) { + const getType = function (state, av) { const types = get(av, 'types', []).map(type => { return get(type, 'proto.name'); }); @@ -103,11 +103,11 @@ const postCondenseReach = function(server, options, state) { return single; }; - const isConstructor = function(state, type) { + const isConstructor = function (state, type) { return !isUndefined(get(type, 'props.prototype')); }; - const visitAVal = function(state, av, path) { + const visitAVal = function (state, av, path) { if (av._localScopeCondenseSeen) { return; } @@ -152,7 +152,8 @@ const postCondenseReach = function(server, options, state) { const node = av.originNode; const ast = node.sourceFile.ast; - let defNode, type; + let defNode; + let type; try { type = infer.expressionType({ diff --git a/src/local-scope/walkall.js b/src/local-scope/walkall.js index f3e7560..5cd4f58 100644 --- a/src/local-scope/walkall.js +++ b/src/local-scope/walkall.js @@ -1,7 +1,7 @@ // Based on acorn-walkall (https://github.com/sourcegraph/acorn-walkall) // types is an array of all SpiderMonkey AST node types recognized by acorn. -const types = (exports.types = [ +const types = [ 'ArrayExpression', 'AssignmentExpression', 'BinaryExpression', @@ -58,10 +58,12 @@ const types = (exports.types = [ 'WhileStatement', 'WithStatement', 'Property' -]); +]; + +exports.types = types; // MakeVisitors returns an object with a property keyed on each AST node type whose value is c. -exports.makeVisitors = function(c) { +exports.makeVisitors = function (c) { const visitors = {}; for (let i = 0; i < types.length; ++i) { const type = types[i]; @@ -72,7 +74,7 @@ exports.makeVisitors = function(c) { // Traverser is an AST visitor that programmatically traverses the AST node by inspecting its object // structure (as opposed to following hard-coded paths). -exports.traverser = function(node, st, c) { +exports.traverser = function (node, st, c) { const keys = Object.keys(node).sort(); for (let i = 0; i < keys.length; ++i) { const key = keys[i]; diff --git a/src/parser.js b/src/parser.js index 45ffba2..32b91f9 100644 --- a/src/parser.js +++ b/src/parser.js @@ -16,21 +16,21 @@ const path = require('path'); const condense = require('./condenser'); const MATCHES = { - pos: /^(\d*?)\[\d*?\:\d*?\]-(\d*?)\[\d*?\:\d*?\]$/, - addr: /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, + pos: /^(\d*?)\[\d*?:\d*?\]-(\d*?)\[\d*?:\d*?\]$/, + addr: /[-[\]/{}()*+?.\\^$|]/g, fn: /^fn\*?\(/, args: /^fn\*?\((.*?)\)/, - arg: /^.*?\: (.*?)$/, + arg: /^.*?: (.*?)$/, arrArg: /^\[/, ret: /-> (.*?)$/, - lineno: /^\d*?\[(\d*?)\:\d*?\]-\d*?\[\d*?\:\d*?\]$/, + lineno: /^\d*?\[(\d*?):\d*?\]-\d*?\[\d*?:\d*?\]$/, namespace: /\// }; const DEFAULT_TYPES = [ /^\?/, MATCHES.fn, - /^\ { return !/^!/.test(key); }) - .map(function(name) { + .map(function (name) { return this.onNode(name, tree[name], parent); }, this); }; -Parser.prototype.namespace = function(node, parent) { +Parser.prototype.namespace = function (node, parent) { if (!parent) { return; } @@ -181,7 +181,7 @@ Parser.prototype.namespace = function(node, parent) { return parent.name; }; -Parser.prototype.lineno = function(node) { +Parser.prototype.lineno = function (node) { if (!isString(node['!span'])) { return; } @@ -189,7 +189,7 @@ Parser.prototype.lineno = function(node) { return Number(node['!span'].match(MATCHES.lineno).pop()) + 1; }; -Parser.prototype.returns = function(node) { +Parser.prototype.returns = function (node) { if (!isString(node['!type'])) { return; } @@ -205,7 +205,7 @@ Parser.prototype.returns = function(node) { }); }; -Parser.prototype.isFn = function(node) { +Parser.prototype.isFn = function (node) { if (!isString(node['!type'])) { return false; } @@ -213,7 +213,7 @@ Parser.prototype.isFn = function(node) { return MATCHES.fn.test(node['!type']); }; -Parser.prototype.fnArgs = function(node) { +Parser.prototype.fnArgs = function (node) { if (!isString(node['!type'])) { return []; } @@ -227,11 +227,11 @@ Parser.prototype.fnArgs = function(node) { return args .pop() .split(',') - .map(function(arg) { + .map(function (arg) { const t = arg.match(MATCHES.arg); if (!Array.isArray(t) || !t.length) { - return; + return ''; } const type = t.pop(); @@ -251,14 +251,14 @@ Parser.prototype.fnArgs = function(node) { }); }; -Parser.prototype.typeFn = function(node) { +Parser.prototype.typeFn = function (node) { const args = this.fnArgs(node); const ret = this.returns(node); return format('%s function(%s)', ret, args ? args.join(', ') : ''); }; -Parser.prototype.type = function(node) { +Parser.prototype.type = function (node) { if (!isString(node['!type'])) { return; } @@ -277,11 +277,11 @@ Parser.prototype.type = function(node) { return this.ctx.preserveType ? node['!type'] : clean; }; -Parser.prototype.kind = function(node) { +Parser.prototype.kind = function (node) { return this.isFn(node) ? 'f' : 'v'; }; -Parser.prototype.addr = function(node) { +Parser.prototype.addr = function (node) { if (!isString(node['!span'])) { return; } @@ -301,7 +301,7 @@ Parser.prototype.addr = function(node) { return str; }; -Parser.prototype.walk = function(node, parent) { +Parser.prototype.walk = function (node, parent) { const hash = parent ? hashNode(parent) : undefined; const id = format('%s-%s', node, hash); @@ -316,7 +316,7 @@ Parser.prototype.walk = function(node, parent) { return get(this.condense, node, node); }; -Parser.prototype.push = function(tag) { +Parser.prototype.push = function (tag) { this.tags.push(tag); const hasSpan = tag.origin && tag.origin['!span']; @@ -334,7 +334,7 @@ Parser.prototype.push = function(tag) { this.bySpan[span].push(tag); }; -Parser.prototype.onNode = function(name, node, parent) { +Parser.prototype.onNode = function (name, node, parent) { if (!node) { return false; } @@ -374,7 +374,7 @@ Parser.prototype.onNode = function(name, node, parent) { this.fromTree(node, tag); }; -module.exports = function(ctx, fn) { +module.exports = function (ctx, fn) { return new Parser(ctx).parse(fn); }; diff --git a/test/clean.js b/test/clean.js index 9536121..ddaad97 100644 --- a/test/clean.js +++ b/test/clean.js @@ -10,13 +10,13 @@ const tags = path.join(cases, '*.tags'); glob.sync(jsons).forEach(file => { let json = fs.readFileSync(file, 'utf-8'); - json = json.replace(/\n\s+"id"\:\s+".+",\n/g, '\n'); - json = json.replace(/\n\s+"parent"\:\s+".+",\n/g, '\n'); + json = json.replace(/\n\s+"id":\s+".+",\n/g, '\n'); + json = json.replace(/\n\s+"parent":\s+".+",\n/g, '\n'); fs.writeFileSync(file, json, 'utf-8'); }); -const cleanPath = function(file) { +const cleanPath = function (file) { let str = fs .readFileSync(file, 'utf-8') .split(/\n/) diff --git a/test/run.js b/test/run.js index 66322de..e670b6b 100644 --- a/test/run.js +++ b/test/run.js @@ -13,7 +13,7 @@ const async = require('async'); const fs = require('fs'); const md5 = require('md5'); -const read = function(filenames) { +const read = function (filenames) { return forceArray(filenames) .map(filename => { return fs.readFileSync(filename, 'utf-8'); @@ -21,19 +21,19 @@ const read = function(filenames) { .join('\n'); }; -const test = function(t, fn) { - const clean = function(v) { +const test = function (t, fn) { + const clean = function (v) { return [ { m: /__DIR__/g, r: t.dir }, { - m: /\n\s+"id"\:\s+".+",\n/g, + m: /\n\s+"id":\s+".+",\n/g, r: '\n' }, { - m: /\n\s+"parent"\:\s+".+",\n/g, + m: /\n\s+"parent":\s+".+",\n/g, r: '\n' } ].reduce((v, r) => { @@ -41,13 +41,13 @@ const test = function(t, fn) { }, v); }; - return function(err, stdio) { + return function (err, stdio) { if (err || stdio.stderr.length) { return fn(err || new Error(stdio.stderr.toString())); } const expected = read( - (function() { + (function () { if (!t.filename) { return path.join(__dirname, format('cases/_%s', t.ext)); } @@ -77,8 +77,8 @@ const test = function(t, fn) { }; }; -const logErr = function(fn) { - return function(err) { +const logErr = function (fn) { + return function (err) { if (!err) { return fn(); } @@ -87,16 +87,16 @@ const logErr = function(fn) { throw err; } - const firstchar = function(str) { - const match = /^[\x20\x09\x0a\x0d]*(.)/.exec(str); + const firstchar = function (str) { + const match = /^[\x20\x09\x0a\x0d]*(.)/.exec(str); // eslint-disable-line no-control-regex return match ? match[1] : ''; }; - const parse = function(v) { + const parse = function (v) { return includes(['[', '{'], firstchar(v)) ? JSON.parse(v) : v; }; - const write = function(type) { + const write = function (type) { const filename = format('%s.%s%s', md5(err.expected), type, err.ext); fs.writeFileSync(path.join(process.cwd(), filename), err[type], 'utf-8'); console.log('wrote %s', filename); @@ -109,8 +109,8 @@ const logErr = function(fn) { }; }; -module.exports = function(ctx) { - return function(t, fn) { +module.exports = function (ctx) { + return function (t, fn) { const child = cp.spawn(ctx.bin, t.cmd.split(/\s/), { cwd: process.cwd() });