123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- var xtend = require('xtend')
- var acorn = require('acorn-node')
- var dash = require('dash-ast')
- var getAssignedIdentifiers = require('get-assigned-identifiers')
- function visitFunction (node, state, ancestors) {
- if (node.params.length > 0) {
- var idents = []
- for (var i = 0; i < node.params.length; i++) {
- var sub = getAssignedIdentifiers(node.params[i])
- for (var j = 0; j < sub.length; j++) idents.push(sub[j])
- }
- declareNames(node, idents)
- }
- if (node.type === 'FunctionDeclaration') {
- var parent = getScopeNode(ancestors, 'const')
- declareNames(parent, [node.id])
- } else if (node.type === 'FunctionExpression' && node.id) {
- declareNames(node, [node.id])
- }
- }
- var scopeVisitor = {
- VariableDeclaration: function (node, state, ancestors) {
- var parent = getScopeNode(ancestors, node.kind)
- for (var i = 0; i < node.declarations.length; i++) {
- declareNames(parent, getAssignedIdentifiers(node.declarations[i].id))
- }
- },
- FunctionExpression: visitFunction,
- FunctionDeclaration: visitFunction,
- ArrowFunctionExpression: visitFunction,
- ClassDeclaration: function (node, state, ancestors) {
- var parent = getScopeNode(ancestors, 'const')
- if (node.id) {
- declareNames(parent, [node.id])
- }
- },
- ImportDeclaration: function (node, state, ancestors) {
- declareNames(ancestors[0] /* root */, getAssignedIdentifiers(node))
- },
- CatchClause: function (node) {
- if (node.param) declareNames(node, [node.param])
- }
- }
- var bindingVisitor = {
- Identifier: function (node, state, ancestors) {
- if (!state.identifiers) return
- var parent = ancestors[ancestors.length - 1]
- if (parent.type === 'MemberExpression' && parent.property === node) return
- if (parent.type === 'Property' && !parent.computed && parent.key === node) return
- if (parent.type === 'MethodDefinition' && !parent.computed && parent.key === node) return
- if (parent.type === 'LabeledStatement' && parent.label === node) return
- if (!has(state.undeclared, node.name)) {
- for (var i = ancestors.length - 1; i >= 0; i--) {
- if (ancestors[i]._names !== undefined && has(ancestors[i]._names, node.name)) {
- return
- }
- }
- state.undeclared[node.name] = true
- }
- if (state.wildcard &&
- !(parent.type === 'MemberExpression' && parent.object === node) &&
- !(parent.type === 'VariableDeclarator' && parent.id === node) &&
- !(parent.type === 'AssignmentExpression' && parent.left === node)) {
- state.undeclaredProps[node.name + '.*'] = true
- }
- },
- MemberExpression: function (node, state) {
- if (!state.properties) return
- if (node.object.type === 'Identifier' && has(state.undeclared, node.object.name)) {
- var prop = !node.computed && node.property.type === 'Identifier'
- ? node.property.name
- : node.computed && node.property.type === 'Literal'
- ? node.property.value
- : null
- if (prop) state.undeclaredProps[node.object.name + '.' + prop] = true
- }
- }
- }
- module.exports = function findUndeclared (src, opts) {
- opts = xtend({
- identifiers: true,
- properties: true,
- wildcard: false
- }, opts)
- var state = {
- undeclared: {},
- undeclaredProps: {},
- identifiers: opts.identifiers,
- properties: opts.properties,
- wildcard: opts.wildcard
- }
- // Parse if `src` is not already an AST.
- var ast = typeof src === 'object' && src !== null && typeof src.type === 'string'
- ? src
- : acorn.parse(src)
- var parents = []
- dash(ast, {
- enter: function (node, parent) {
- if (parent) parents.push(parent)
- var visit = scopeVisitor[node.type]
- if (visit) visit(node, state, parents)
- },
- leave: function (node, parent) {
- var visit = bindingVisitor[node.type]
- if (visit) visit(node, state, parents)
- if (parent) parents.pop()
- }
- })
- return {
- identifiers: Object.keys(state.undeclared),
- properties: Object.keys(state.undeclaredProps)
- }
- }
- function getScopeNode (parents, kind) {
- for (var i = parents.length - 1; i >= 0; i--) {
- if (parents[i].type === 'FunctionDeclaration' || parents[i].type === 'FunctionExpression' ||
- parents[i].type === 'ArrowFunctionExpression' || parents[i].type === 'Program') {
- return parents[i]
- }
- if (kind !== 'var' && parents[i].type === 'BlockStatement') {
- return parents[i]
- }
- }
- }
- function declareNames (node, names) {
- if (node._names === undefined) {
- node._names = Object.create(null)
- }
- for (var i = 0; i < names.length; i++) {
- node._names[names[i].name] = true
- }
- }
- function has (obj, name) { return Object.prototype.hasOwnProperty.call(obj, name) }
|