Skip to content

Commit

Permalink
pulled out moduleVisitor from no-unresolved, to be used with eslint-p…
Browse files Browse the repository at this point in the history
…lugin-git
  • Loading branch information
benmosher committed Jul 2, 2016
1 parent 2c14c4c commit 96b5ef9
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 81 deletions.
84 changes: 3 additions & 81 deletions src/rules/no-unresolved.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,97 +4,19 @@
*/

import resolve from 'eslint-module-utils/resolve'
import moduleVisitor, { optionsSchema } from 'eslint-module-utils/moduleVisitor'

module.exports = function (context) {

let ignoreRegExps = []
if (context.options[0] != null && context.options[0].ignore != null) {
ignoreRegExps = context.options[0].ignore.map(p => new RegExp(p))
}

function checkSourceValue(source) {
if (source == null) return

if (ignoreRegExps.some(re => re.test(source.value))) return

if (resolve(source.value, context) === undefined) {
context.report(source,
'Unable to resolve path to module \'' + source.value + '\'.')
}
}

// for import-y declarations
function checkSource(node) {
checkSourceValue(node.source)
}

// for CommonJS `require` calls
// adapted from @mctep: http://git.io/v4rAu
function checkCommon(call) {
if (call.callee.type !== 'Identifier') return
if (call.callee.name !== 'require') return
if (call.arguments.length !== 1) return

const modulePath = call.arguments[0]
if (modulePath.type !== 'Literal') return
if (typeof modulePath.value !== 'string') return

checkSourceValue(modulePath)
}

function checkAMD(call) {
if (call.callee.type !== 'Identifier') return
if (call.callee.name !== 'require' &&
call.callee.name !== 'define') return
if (call.arguments.length !== 2) return

const modules = call.arguments[0]
if (modules.type !== 'ArrayExpression') return

for (let element of modules.elements) {
if (element.type !== 'Literal') continue
if (typeof element.value !== 'string') continue

if (element.value === 'require' ||
element.value === 'exports') continue // magic modules: http://git.io/vByan

checkSourceValue(element)
}
}

const visitors = {
'ImportDeclaration': checkSource,
'ExportNamedDeclaration': checkSource,
'ExportAllDeclaration': checkSource,
}

if (context.options[0] != null) {
const { commonjs, amd } = context.options[0]

if (commonjs || amd) {
visitors['CallExpression'] = function (call) {
if (commonjs) checkCommon(call)
if (amd) checkAMD(call)
}
}
}
return moduleVisitor(checkSourceValue, context.options[0])

return visitors
}

module.exports.schema = [
{
'type': 'object',
'properties': {
'commonjs': { 'type': 'boolean' },
'amd': { 'type': 'boolean' },
'ignore': {
'type': 'array',
'minItems': 1,
'items': { 'type': 'string' },
'uniqueItems': true,
},
},
'additionalProperties': false,
},
]
module.exports.schema = [ optionsSchema ]
109 changes: 109 additions & 0 deletions utils/moduleVisitor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"use strict"
exports.__esModule = true

/**
* Returns an object of node visitors that will call
* 'visitor' with every discovered module path.
*
* todo: correct function prototype for visitor
* @param {Function(String)} visitor [description]
* @param {[type]} options [description]
* @return {object}
*/
exports.default = function visitModules(visitor, options) {
// if esmodule is not explicitly disabled, it is assumed to be enabled
options = Object.assign({ esmodule: true }, options)

let ignoreRegExps = []
if (options.ignore != null) {
ignoreRegExps = options.ignore.map(p => new RegExp(p))
}

function checkSourceValue(source) {
if (source == null) return //?

// handle ignore
if (ignoreRegExps.some(re => re.test(source.value))) return

// fire visitor
visitor(source)
}

// for import-y declarations
function checkSource(node) {
checkSourceValue(node.source)
}

// for CommonJS `require` calls
// adapted from @mctep: http://git.io/v4rAu
function checkCommon(call) {
if (call.callee.type !== 'Identifier') return
if (call.callee.name !== 'require') return
if (call.arguments.length !== 1) return

const modulePath = call.arguments[0]
if (modulePath.type !== 'Literal') return
if (typeof modulePath.value !== 'string') return

checkSourceValue(modulePath)
}

function checkAMD(call) {
if (call.callee.type !== 'Identifier') return
if (call.callee.name !== 'require' &&
call.callee.name !== 'define') return
if (call.arguments.length !== 2) return

const modules = call.arguments[0]
if (modules.type !== 'ArrayExpression') return

for (let element of modules.elements) {
if (element.type !== 'Literal') continue
if (typeof element.value !== 'string') continue

if (element.value === 'require' ||
element.value === 'exports') continue // magic modules: http://git.io/vByan

checkSourceValue(element)
}
}

const visitors = {}
if (options.esmodule) {
Object.assign(visitors, {
'ImportDeclaration': checkSource,
'ExportNamedDeclaration': checkSource,
'ExportAllDeclaration': checkSource,
})
}

if (options.commonjs || options.amd) {
visitors['CallExpression'] = function (call) {
if (options.commonjs) checkCommon(call)
if (options.amd) checkAMD(call)
}
}

return visitors
}

/**
* json schema object for options parameter. can be used to build
* rule options schema object.
* @type {Object}
*/
exports.optionsSchema = {
'type': 'object',
'properties': {
'commonjs': { 'type': 'boolean' },
'amd': { 'type': 'boolean' },
'esmodule': { 'type': 'boolean' },
'ignore': {
'type': 'array',
'minItems': 1,
'items': { 'type': 'string' },
'uniqueItems': true,
},
},
'additionalProperties': false,
}

0 comments on commit 96b5ef9

Please sign in to comment.