packages/compiler-sfc/src/babelUtils.ts TYPESCRIPT 424 lines View on github.com → Search inside
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.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.