babelUtils.ts 12 KB

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