babelUtils.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. // should only use types from @babel/types
  2. // do not import runtime methods
  3. import type {
  4. BlockStatement,
  5. Function,
  6. Identifier,
  7. Node,
  8. ObjectProperty,
  9. Program,
  10. } from '@babel/types'
  11. import { walk } from 'estree-walker'
  12. /**
  13. * Return value indicates whether the AST walked can be a constant
  14. */
  15. export function walkIdentifiers(
  16. root: Node,
  17. onIdentifier: (
  18. node: Identifier,
  19. parent: Node,
  20. parentStack: Node[],
  21. isReference: boolean,
  22. isLocal: boolean,
  23. ) => void,
  24. includeAll = false,
  25. parentStack: Node[] = [],
  26. knownIds: Record<string, number> = Object.create(null),
  27. ) {
  28. if (__BROWSER__) {
  29. return
  30. }
  31. const rootExp =
  32. root.type === 'Program'
  33. ? root.body[0].type === 'ExpressionStatement' && root.body[0].expression
  34. : root
  35. walk(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. !TS_NODE_TYPES.includes(parent.type)
  42. ) {
  43. return this.skip()
  44. }
  45. if (node.type === 'Identifier') {
  46. const isLocal = !!knownIds[node.name]
  47. const isRefed = isReferencedIdentifier(node, parent!, parentStack)
  48. if (includeAll || (isRefed && !isLocal)) {
  49. onIdentifier(node, parent!, parentStack, isRefed, isLocal)
  50. }
  51. } else if (
  52. node.type === 'ObjectProperty' &&
  53. parent?.type === 'ObjectPattern'
  54. ) {
  55. // mark property in destructure pattern
  56. ;(node as any).inPattern = true
  57. } else if (isFunctionType(node)) {
  58. if (node.scopeIds) {
  59. node.scopeIds.forEach(id => markKnownIds(id, knownIds))
  60. } else {
  61. // walk function expressions and add its arguments to known identifiers
  62. // so that we don't prefix them
  63. walkFunctionParams(node, id =>
  64. markScopeIdentifier(node, id, knownIds),
  65. )
  66. }
  67. } else if (node.type === 'BlockStatement') {
  68. if (node.scopeIds) {
  69. node.scopeIds.forEach(id => markKnownIds(id, knownIds))
  70. } else {
  71. // #3445 record block-level local variables
  72. walkBlockDeclarations(node, id =>
  73. markScopeIdentifier(node, id, knownIds),
  74. )
  75. }
  76. }
  77. },
  78. leave(node: Node & { scopeIds?: Set<string> }, parent: Node | undefined) {
  79. parent && parentStack.pop()
  80. if (node !== rootExp && node.scopeIds) {
  81. for (const id of node.scopeIds) {
  82. knownIds[id]--
  83. if (knownIds[id] === 0) {
  84. delete knownIds[id]
  85. }
  86. }
  87. }
  88. },
  89. })
  90. }
  91. export function isReferencedIdentifier(
  92. id: Identifier,
  93. parent: Node | null,
  94. parentStack: Node[],
  95. ) {
  96. if (__BROWSER__) {
  97. return false
  98. }
  99. if (!parent) {
  100. return true
  101. }
  102. // is a special keyword but parsed as identifier
  103. if (id.name === 'arguments') {
  104. return false
  105. }
  106. if (isReferenced(id, parent)) {
  107. return true
  108. }
  109. // babel's isReferenced check returns false for ids being assigned to, so we
  110. // need to cover those cases here
  111. switch (parent.type) {
  112. case 'AssignmentExpression':
  113. case 'AssignmentPattern':
  114. return true
  115. case 'ObjectPattern':
  116. case 'ArrayPattern':
  117. return isInDestructureAssignment(parent, parentStack)
  118. }
  119. return false
  120. }
  121. export function isInDestructureAssignment(
  122. parent: Node,
  123. parentStack: Node[],
  124. ): boolean {
  125. if (
  126. parent &&
  127. (parent.type === 'ObjectProperty' || parent.type === 'ArrayPattern')
  128. ) {
  129. let i = parentStack.length
  130. while (i--) {
  131. const p = parentStack[i]
  132. if (p.type === 'AssignmentExpression') {
  133. return true
  134. } else if (p.type !== 'ObjectProperty' && !p.type.endsWith('Pattern')) {
  135. break
  136. }
  137. }
  138. }
  139. return false
  140. }
  141. export function isInNewExpression(parentStack: Node[]): boolean {
  142. let i = parentStack.length
  143. while (i--) {
  144. const p = parentStack[i]
  145. if (p.type === 'NewExpression') {
  146. return true
  147. } else if (p.type !== 'MemberExpression') {
  148. break
  149. }
  150. }
  151. return false
  152. }
  153. export function walkFunctionParams(
  154. node: Function,
  155. onIdent: (id: Identifier) => void,
  156. ) {
  157. for (const p of node.params) {
  158. for (const id of extractIdentifiers(p)) {
  159. onIdent(id)
  160. }
  161. }
  162. }
  163. export function walkBlockDeclarations(
  164. block: BlockStatement | Program,
  165. onIdent: (node: Identifier) => void,
  166. ) {
  167. for (const stmt of block.body) {
  168. if (stmt.type === 'VariableDeclaration') {
  169. if (stmt.declare) continue
  170. for (const decl of stmt.declarations) {
  171. for (const id of extractIdentifiers(decl.id)) {
  172. onIdent(id)
  173. }
  174. }
  175. } else if (
  176. stmt.type === 'FunctionDeclaration' ||
  177. stmt.type === 'ClassDeclaration'
  178. ) {
  179. if (stmt.declare || !stmt.id) continue
  180. onIdent(stmt.id)
  181. } else if (
  182. stmt.type === 'ForOfStatement' ||
  183. stmt.type === 'ForInStatement' ||
  184. stmt.type === 'ForStatement'
  185. ) {
  186. const variable = stmt.type === 'ForStatement' ? stmt.init : stmt.left
  187. if (variable && variable.type === 'VariableDeclaration') {
  188. for (const decl of variable.declarations) {
  189. for (const id of extractIdentifiers(decl.id)) {
  190. onIdent(id)
  191. }
  192. }
  193. }
  194. }
  195. }
  196. }
  197. export function extractIdentifiers(
  198. param: Node,
  199. nodes: Identifier[] = [],
  200. ): Identifier[] {
  201. switch (param.type) {
  202. case 'Identifier':
  203. nodes.push(param)
  204. break
  205. case 'MemberExpression':
  206. let object: any = param
  207. while (object.type === 'MemberExpression') {
  208. object = object.object
  209. }
  210. nodes.push(object)
  211. break
  212. case 'ObjectPattern':
  213. for (const prop of param.properties) {
  214. if (prop.type === 'RestElement') {
  215. extractIdentifiers(prop.argument, nodes)
  216. } else {
  217. extractIdentifiers(prop.value, nodes)
  218. }
  219. }
  220. break
  221. case 'ArrayPattern':
  222. param.elements.forEach(element => {
  223. if (element) extractIdentifiers(element, nodes)
  224. })
  225. break
  226. case 'RestElement':
  227. extractIdentifiers(param.argument, nodes)
  228. break
  229. case 'AssignmentPattern':
  230. extractIdentifiers(param.left, nodes)
  231. break
  232. }
  233. return nodes
  234. }
  235. function markKnownIds(name: string, knownIds: Record<string, number>) {
  236. if (name in knownIds) {
  237. knownIds[name]++
  238. } else {
  239. knownIds[name] = 1
  240. }
  241. }
  242. function markScopeIdentifier(
  243. node: Node & { scopeIds?: Set<string> },
  244. child: Identifier,
  245. knownIds: Record<string, number>,
  246. ) {
  247. const { name } = child
  248. if (node.scopeIds && node.scopeIds.has(name)) {
  249. return
  250. }
  251. markKnownIds(name, knownIds)
  252. ;(node.scopeIds || (node.scopeIds = new Set())).add(name)
  253. }
  254. export const isFunctionType = (node: Node): node is Function => {
  255. return /Function(?:Expression|Declaration)$|Method$/.test(node.type)
  256. }
  257. export const isStaticProperty = (node?: Node): node is ObjectProperty =>
  258. !!node &&
  259. (node.type === 'ObjectProperty' || node.type === 'ObjectMethod') &&
  260. !node.computed
  261. export const isStaticPropertyKey = (node: Node, parent: Node) =>
  262. isStaticProperty(parent) && parent.key === node
  263. /**
  264. * Copied from https://github.com/babel/babel/blob/main/packages/babel-types/src/validators/isReferenced.ts
  265. * To avoid runtime dependency on @babel/types (which includes process references)
  266. * This file should not change very often in babel but we may need to keep it
  267. * up-to-date from time to time.
  268. *
  269. * https://github.com/babel/babel/blob/main/LICENSE
  270. *
  271. */
  272. function isReferenced(node: Node, parent: Node, grandparent?: Node): boolean {
  273. switch (parent.type) {
  274. // yes: PARENT[NODE]
  275. // yes: NODE.child
  276. // no: parent.NODE
  277. case 'MemberExpression':
  278. case 'OptionalMemberExpression':
  279. if (parent.property === node) {
  280. return !!parent.computed
  281. }
  282. return parent.object === node
  283. case 'JSXMemberExpression':
  284. return parent.object === node
  285. // no: let NODE = init;
  286. // yes: let id = NODE;
  287. case 'VariableDeclarator':
  288. return parent.init === node
  289. // yes: () => NODE
  290. // no: (NODE) => {}
  291. case 'ArrowFunctionExpression':
  292. return parent.body === node
  293. // no: class { #NODE; }
  294. // no: class { get #NODE() {} }
  295. // no: class { #NODE() {} }
  296. // no: class { fn() { return this.#NODE; } }
  297. case 'PrivateName':
  298. return false
  299. // no: class { NODE() {} }
  300. // yes: class { [NODE]() {} }
  301. // no: class { foo(NODE) {} }
  302. case 'ClassMethod':
  303. case 'ClassPrivateMethod':
  304. case 'ObjectMethod':
  305. if (parent.key === node) {
  306. return !!parent.computed
  307. }
  308. return false
  309. // yes: { [NODE]: "" }
  310. // no: { NODE: "" }
  311. // depends: { NODE }
  312. // depends: { key: NODE }
  313. case 'ObjectProperty':
  314. if (parent.key === node) {
  315. return !!parent.computed
  316. }
  317. // parent.value === node
  318. return !grandparent || grandparent.type !== 'ObjectPattern'
  319. // no: class { NODE = value; }
  320. // yes: class { [NODE] = value; }
  321. // yes: class { key = NODE; }
  322. case 'ClassProperty':
  323. if (parent.key === node) {
  324. return !!parent.computed
  325. }
  326. return true
  327. case 'ClassPrivateProperty':
  328. return parent.key !== node
  329. // no: class NODE {}
  330. // yes: class Foo extends NODE {}
  331. case 'ClassDeclaration':
  332. case 'ClassExpression':
  333. return parent.superClass === node
  334. // yes: left = NODE;
  335. // no: NODE = right;
  336. case 'AssignmentExpression':
  337. return parent.right === node
  338. // no: [NODE = foo] = [];
  339. // yes: [foo = NODE] = [];
  340. case 'AssignmentPattern':
  341. return parent.right === node
  342. // no: NODE: for (;;) {}
  343. case 'LabeledStatement':
  344. return false
  345. // no: try {} catch (NODE) {}
  346. case 'CatchClause':
  347. return false
  348. // no: function foo(...NODE) {}
  349. case 'RestElement':
  350. return false
  351. case 'BreakStatement':
  352. case 'ContinueStatement':
  353. return false
  354. // no: function NODE() {}
  355. // no: function foo(NODE) {}
  356. case 'FunctionDeclaration':
  357. case 'FunctionExpression':
  358. return false
  359. // no: export NODE from "foo";
  360. // no: export * as NODE from "foo";
  361. case 'ExportNamespaceSpecifier':
  362. case 'ExportDefaultSpecifier':
  363. return false
  364. // no: export { foo as NODE };
  365. // yes: export { NODE as foo };
  366. // no: export { NODE as foo } from "foo";
  367. case 'ExportSpecifier':
  368. // @ts-expect-error
  369. if (grandparent?.source) {
  370. return false
  371. }
  372. return parent.local === node
  373. // 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 false
  382. // no: import "foo" assert { NODE: "json" }
  383. case 'ImportAttribute':
  384. return false
  385. // no: <div NODE="foo" />
  386. case 'JSXAttribute':
  387. return false
  388. // no: [NODE] = [];
  389. // no: ({ NODE }) = [];
  390. case 'ObjectPattern':
  391. case 'ArrayPattern':
  392. return false
  393. // no: new.NODE
  394. // no: NODE.target
  395. case 'MetaProperty':
  396. return false
  397. // yes: type X = { someProperty: NODE }
  398. // no: type X = { NODE: OtherType }
  399. case 'ObjectTypeProperty':
  400. return parent.key !== node
  401. // yes: enum X { Foo = NODE }
  402. // no: enum X { NODE }
  403. case 'TSEnumMember':
  404. return parent.id !== node
  405. // yes: { [NODE]: value }
  406. // no: { NODE: value }
  407. case 'TSPropertySignature':
  408. if (parent.key === node) {
  409. return !!parent.computed
  410. }
  411. return true
  412. }
  413. return true
  414. }
  415. export const TS_NODE_TYPES = [
  416. 'TSAsExpression', // foo as number
  417. 'TSTypeAssertion', // (<number>foo)
  418. 'TSNonNullExpression', // foo!
  419. 'TSInstantiationExpression', // foo<string>
  420. 'TSSatisfiesExpression', // foo satisfies T
  421. ]
  422. export function unwrapTSNode(node: Node): Node {
  423. if (TS_NODE_TYPES.includes(node.type)) {
  424. return unwrapTSNode((node as any).expression)
  425. } else {
  426. return node
  427. }
  428. }