1// https://github.com/vuejs/core/blob/main/packages/compiler-core/src/babelUtils.ts23// should only use types from @babel/types4// do not import runtime methods5import type {6 Identifier,7 Node,8 Function,9 ObjectProperty,10 BlockStatement,11 Program12} from '@babel/types'13import { walk } from 'estree-walker'1415export function walkIdentifiers(16 root: Node,17 onIdentifier: (18 node: Identifier,19 parent: Node,20 parentStack: Node[],21 isReference: boolean,22 isLocal: boolean23 ) => void,24 onNode?: (node: Node) => void25) {26 const includeAll = false27 const parentStack: Node[] = []28 const knownIds: Record<string, number> = Object.create(null)2930 const rootExp =31 root.type === 'Program' &&32 root.body[0].type === 'ExpressionStatement' &&33 root.body[0].expression3435 ;(walk as any)(root, {36 enter(node: Node & { scopeIds?: Set<string> }, parent: Node | undefined) {37 parent && parentStack.push(parent)38 if (39 parent &&40 parent.type.startsWith('TS') &&41 parent.type !== 'TSAsExpression' &&42 parent.type !== 'TSNonNullExpression' &&43 parent.type !== 'TSTypeAssertion'44 ) {45 return this.skip()46 }4748 if (onNode) onNode(node)4950 if (node.type === 'Identifier') {51 const isLocal = !!knownIds[node.name]52 const isRefed = isReferencedIdentifier(node, parent!, parentStack)53 if (includeAll || (isRefed && !isLocal)) {54 onIdentifier(node, parent!, parentStack, isRefed, isLocal)55 }56 } else if (57 node.type === 'ObjectProperty' &&58 parent!.type === 'ObjectPattern'59 ) {60 // mark property in destructure pattern61 ;(node as any).inPattern = true62 } else if (isFunctionType(node)) {63 // walk function expressions and add its arguments to known identifiers64 // so that we don't prefix them65 walkFunctionParams(node, id => markScopeIdentifier(node, id, knownIds))66 } else if (node.type === 'BlockStatement') {67 // #3445 record block-level local variables68 walkBlockDeclarations(node, id =>69 markScopeIdentifier(node, id, knownIds)70 )71 }72 },73 leave(node: Node & { scopeIds?: Set<string> }, parent: Node | undefined) {74 parent && parentStack.pop()75 if (node !== rootExp && node.scopeIds) {76 for (const id of node.scopeIds) {77 knownIds[id]--78 if (knownIds[id] === 0) {79 delete knownIds[id]80 }81 }82 }83 }84 })85}8687export function isReferencedIdentifier(88 id: Identifier,89 parent: Node | null,90 parentStack: Node[]91) {92 if (!parent) {93 return true94 }9596 // is a special keyword but parsed as identifier97 if (id.name === 'arguments') {98 return false99 }100101 if (isReferenced(id, parent)) {102 return true103 }104105 // babel's isReferenced check returns false for ids being assigned to, so we106 // need to cover those cases here107 switch (parent.type) {108 case 'AssignmentExpression':109 case 'AssignmentPattern':110 return true111 case 'ObjectPattern':112 case 'ArrayPattern':113 return isInDestructureAssignment(parent, parentStack)114 }115116 return false117}118119export function isInDestructureAssignment(120 parent: Node,121 parentStack: Node[]122): boolean {123 if (124 parent &&125 (parent.type === 'ObjectProperty' || parent.type === 'ArrayPattern')126 ) {127 let i = parentStack.length128 while (i--) {129 const p = parentStack[i]130 if (p.type === 'AssignmentExpression') {131 return true132 } else if (p.type !== 'ObjectProperty' && !p.type.endsWith('Pattern')) {133 break134 }135 }136 }137 return false138}139140export function walkFunctionParams(141 node: Function,142 onIdent: (id: Identifier) => void143) {144 for (const p of node.params) {145 for (const id of extractIdentifiers(p)) {146 onIdent(id)147 }148 }149}150151export function walkBlockDeclarations(152 block: BlockStatement | Program,153 onIdent: (node: Identifier) => void154) {155 for (const stmt of block.body) {156 if (stmt.type === 'VariableDeclaration') {157 if (stmt.declare) continue158 for (const decl of stmt.declarations) {159 for (const id of extractIdentifiers(decl.id)) {160 onIdent(id)161 }162 }163 } else if (164 stmt.type === 'FunctionDeclaration' ||165 stmt.type === 'ClassDeclaration'166 ) {167 if (stmt.declare || !stmt.id) continue168 onIdent(stmt.id)169 }170 }171}172173export function extractIdentifiers(174 param: Node,175 nodes: Identifier[] = []176): Identifier[] {177 switch (param.type) {178 case 'Identifier':179 nodes.push(param)180 break181182 case 'MemberExpression':183 let object: any = param184 while (object.type === 'MemberExpression') {185 object = object.object186 }187 nodes.push(object)188 break189190 case 'ObjectPattern':191 for (const prop of param.properties) {192 if (prop.type === 'RestElement') {193 extractIdentifiers(prop.argument, nodes)194 } else {195 extractIdentifiers(prop.value, nodes)196 }197 }198 break199200 case 'ArrayPattern':201 param.elements.forEach(element => {202 if (element) extractIdentifiers(element, nodes)203 })204 break205206 case 'RestElement':207 extractIdentifiers(param.argument, nodes)208 break209210 case 'AssignmentPattern':211 extractIdentifiers(param.left, nodes)212 break213 }214215 return nodes216}217218function markScopeIdentifier(219 node: Node & { scopeIds?: Set<string> },220 child: Identifier,221 knownIds: Record<string, number>222) {223 const { name } = child224 if (node.scopeIds && node.scopeIds.has(name)) {225 return226 }227 if (name in knownIds) {228 knownIds[name]++229 } else {230 knownIds[name] = 1231 }232 ;(node.scopeIds || (node.scopeIds = new Set())).add(name)233}234235export const isFunctionType = (node: Node): node is Function => {236 return /Function(?:Expression|Declaration)$|Method$/.test(node.type)237}238239export const isStaticProperty = (node: Node): node is ObjectProperty =>240 node &&241 (node.type === 'ObjectProperty' || node.type === 'ObjectMethod') &&242 !node.computed243244export const isStaticPropertyKey = (node: Node, parent: Node) =>245 isStaticProperty(parent) && parent.key === node246247/**248 * Copied from https://github.com/babel/babel/blob/main/packages/babel-types/src/validators/isReferenced.ts249 * To avoid runtime dependency on @babel/types (which includes process references)250 * This file should not change very often in babel but we may need to keep it251 * up-to-date from time to time.252 *253 * https://github.com/babel/babel/blob/main/LICENSE254 *255 */256function isReferenced(node: Node, parent: Node, grandparent?: Node): boolean {257 switch (parent.type) {258 // yes: PARENT[NODE]259 // yes: NODE.child260 // no: parent.NODE261 case 'MemberExpression':262 case 'OptionalMemberExpression':263 if (parent.property === node) {264 return !!parent.computed265 }266 return parent.object === node267268 case 'JSXMemberExpression':269 return parent.object === node270 // no: let NODE = init;271 // yes: let id = NODE;272 case 'VariableDeclarator':273 return parent.init === node274275 // yes: () => NODE276 // no: (NODE) => {}277 case 'ArrowFunctionExpression':278 return parent.body === node279280 // no: class { #NODE; }281 // no: class { get #NODE() {} }282 // no: class { #NODE() {} }283 // no: class { fn() { return this.#NODE; } }284 case 'PrivateName':285 return false286287 // no: class { NODE() {} }288 // yes: class { [NODE]() {} }289 // no: class { foo(NODE) {} }290 case 'ClassMethod':291 case 'ClassPrivateMethod':292 case 'ObjectMethod':293 if (parent.key === node) {294 return !!parent.computed295 }296 return false297298 // yes: { [NODE]: "" }299 // no: { NODE: "" }300 // depends: { NODE }301 // depends: { key: NODE }302 case 'ObjectProperty':303 if (parent.key === node) {304 return !!parent.computed305 }306 // parent.value === node307 return !grandparent || grandparent.type !== 'ObjectPattern'308 // no: class { NODE = value; }309 // yes: class { [NODE] = value; }310 // yes: class { key = NODE; }311 case 'ClassProperty':312 if (parent.key === node) {313 return !!parent.computed314 }315 return true316 case 'ClassPrivateProperty':317 return parent.key !== node318319 // no: class NODE {}320 // yes: class Foo extends NODE {}321 case 'ClassDeclaration':322 case 'ClassExpression':323 return parent.superClass === node324325 // yes: left = NODE;326 // no: NODE = right;327 case 'AssignmentExpression':328 return parent.right === node329330 // no: [NODE = foo] = [];331 // yes: [foo = NODE] = [];332 case 'AssignmentPattern':333 return parent.right === node334335 // no: NODE: for (;;) {}336 case 'LabeledStatement':337 return false338339 // no: try {} catch (NODE) {}340 case 'CatchClause':341 return false342343 // no: function foo(...NODE) {}344 case 'RestElement':345 return false346347 case 'BreakStatement':348 case 'ContinueStatement':349 return false350351 // no: function NODE() {}352 // no: function foo(NODE) {}353 case 'FunctionDeclaration':354 case 'FunctionExpression':355 return false356357 // no: export NODE from "foo";358 // no: export * as NODE from "foo";359 case 'ExportNamespaceSpecifier':360 case 'ExportDefaultSpecifier':361 return false362363 // no: export { foo as NODE };364 // yes: export { NODE as foo };365 // no: export { NODE as foo } from "foo";366 case 'ExportSpecifier':367 // @ts-expect-error368 if (grandparent?.source) {369 return false370 }371 return parent.local === node372373 // no: import NODE from "foo";374 // no: import * as NODE from "foo";375 // no: import { NODE as foo } from "foo";376 // no: import { foo as NODE } from "foo";377 // no: import NODE from "bar";378 case 'ImportDefaultSpecifier':379 case 'ImportNamespaceSpecifier':380 case 'ImportSpecifier':381 return false382383 // no: import "foo" assert { NODE: "json" }384 case 'ImportAttribute':385 return false386387 // no: <div NODE="foo" />388 case 'JSXAttribute':389 return false390391 // no: [NODE] = [];392 // no: ({ NODE }) = [];393 case 'ObjectPattern':394 case 'ArrayPattern':395 return false396397 // no: new.NODE398 // no: NODE.target399 case 'MetaProperty':400 return false401402 // yes: type X = { someProperty: NODE }403 // no: type X = { NODE: OtherType }404 case 'ObjectTypeProperty':405 return parent.key !== node406407 // yes: enum X { Foo = NODE }408 // no: enum X { NODE }409 case 'TSEnumMember':410 return parent.id !== node411412 // yes: { [NODE]: value }413 // no: { NODE: value }414 case 'TSPropertySignature':415 if (parent.key === node) {416 return !!parent.computed417 }418419 return true420 }421422 return true423}
Findings
✓ No findings reported for this file.