traverse - find all global references #11654
Answered
by
nicolo-ribaudo
kumavis
asked this question in
Show and tell
-
Is there a better way to do this? or perhaps an existing package? 'use strict';
const { default: traverse } = require('@babel/traverse')
const nonReferenceIdentifiers = [
'FunctionDeclaration',
'FunctionExpression',
'ClassMethod',
'LabeledStatement',
'BreakStatement',
'ContinueStatement',
'CatchClause',
'ArrayPatten',
'RestElement'
]
module.exports = { findGlobals }
function findGlobals (ast) {
const globals = new Map()
traverse(ast, {
// ReferencedIdentifier
Identifier: (path) => {
// skip if not being used as reference
const parentType = path.parent.type
if (nonReferenceIdentifiers.includes(parentType)) return
if (parentType === 'VariableDeclarator' && path.parent.id === path.node) return
// skip if this is the key side of a member expression
if (parentType === 'MemberExpression' && path.parent.property === path.node) return
// skip if this is the key side of an object pattern
if (parentType === 'ObjectProperty' && path.parent.key === path.node) return
// skip if it refers to an existing variable
const name = path.node.name
if (path.scope.hasBinding(name, true)) return
// check if arguments refers to a var, this shouldn't happen in strict mode
if (name === 'arguments') {
if (isInFunctionDeclaration(path)) return
}
// save global
saveGlobal(path)
},
ThisExpression: (path) => {
if (isInFunctionDeclaration(path)) return
saveGlobal(path, 'this')
}
})
return globals
function saveGlobal (path, name = path.node.name) {
// init entry if needed
if (!globals.has(name)) {
globals.set(name, [])
}
// append ref
const refsForName = globals.get(name)
refsForName.push(path)
}
}
function isInFunctionDeclaration (path) {
return getParents(path.parentPath).some(parent => parent.type === 'FunctionDeclaration')
}
function getParents (nodePath) {
const parents = []
let target = nodePath
while (target) {
parents.push(target.node)
target = target.parentPath
}
parents.reverse()
return parents
} |
Beta Was this translation helpful? Give feedback.
Answered by
nicolo-ribaudo
Jun 6, 2020
Replies: 2 comments
-
We have a function findGlobals (ast) {
const globals = new Map()
traverse(ast, {
// ReferencedIdentifier
ReferencedIdentifier: (path) => {
// skip if it refers to an existing variable
const name = path.node.name
if (path.scope.hasBinding(name, true)) return
// check if arguments refers to a var, this shouldn't happen in strict mode
if (name === 'arguments') {
if (isInFunctionDeclaration(path)) return
}
// save global
saveGlobal(path)
},
ThisExpression: (path) => {
if (isInFunctionDeclaration(path)) return
saveGlobal(path, 'this')
}
}) |
Beta Was this translation helpful? Give feedback.
0 replies
Answer selected by
kumavis
-
thanks! that simplifies things a bit. i would expect this functionality to be provided by the context object |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
We have a
ReferencedIdentifier
"virtual" type: maybe this would help?