property-references.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  1. /**
  2. * @author Yosuke Ota
  3. * @copyright 2021 Yosuke Ota. All rights reserved.
  4. * See LICENSE file in root directory for full license.
  5. */
  6. 'use strict'
  7. const utils = require('./index')
  8. const eslintUtils = require('@eslint-community/eslint-utils')
  9. /**
  10. * @typedef {import('./style-variables').StyleVariablesContext} StyleVariablesContext
  11. */
  12. /**
  13. * @typedef {object} IHasPropertyOption
  14. * @property {boolean} [unknownCallAsAny]
  15. */
  16. /**
  17. * @typedef {object} NestPropertyNodeForExpression
  18. * @property {'expression'} type
  19. * @property {MemberExpression} node
  20. *
  21. * @typedef {object} NestPropertyNodeForPattern
  22. * @property {'pattern'} type
  23. * @property {MemberExpression | Identifier | ObjectPattern | ArrayPattern} node
  24. *
  25. * @typedef {NestPropertyNodeForExpression | NestPropertyNodeForPattern} NestPropertyNode
  26. */
  27. /**
  28. * @typedef {object} IPropertyReferences
  29. * @property { (name: string, option?: IHasPropertyOption) => boolean } hasProperty
  30. * @property { () => Map<string, {nodes:ASTNode[]}> } allProperties Get all properties.
  31. * @property { (name: string) => IPropertyReferences } getNest Get the nesting property references.
  32. * @property { (name: string) => Iterable<NestPropertyNode> } getNestNodes Get the nesting property nodes.
  33. */
  34. /** @type {IPropertyReferences} */
  35. const ANY = {
  36. hasProperty: () => true,
  37. allProperties: () => new Map(),
  38. getNest: () => ANY,
  39. getNestNodes: () => []
  40. }
  41. /** @type {IPropertyReferences} */
  42. const NEVER = {
  43. hasProperty: () => false,
  44. allProperties: () => new Map(),
  45. getNest: () => NEVER,
  46. getNestNodes: () => []
  47. }
  48. /**
  49. * @param {RuleContext} context
  50. * @param {Identifier} id
  51. * @returns {FunctionExpression | ArrowFunctionExpression | FunctionDeclaration | null}
  52. */
  53. function findFunction(context, id) {
  54. const calleeVariable = utils.findVariableByIdentifier(context, id)
  55. if (!calleeVariable) {
  56. return null
  57. }
  58. if (calleeVariable.defs.length === 1) {
  59. const def = calleeVariable.defs[0]
  60. if (def.node.type === 'FunctionDeclaration') {
  61. return def.node
  62. }
  63. if (
  64. def.type === 'Variable' &&
  65. def.parent.kind === 'const' &&
  66. def.node.init
  67. ) {
  68. if (
  69. def.node.init.type === 'FunctionExpression' ||
  70. def.node.init.type === 'ArrowFunctionExpression'
  71. ) {
  72. return def.node.init
  73. }
  74. if (def.node.init.type === 'Identifier') {
  75. return findFunction(context, def.node.init)
  76. }
  77. }
  78. }
  79. return null
  80. }
  81. module.exports = {
  82. definePropertyReferenceExtractor,
  83. mergePropertyReferences
  84. }
  85. /**
  86. * @param {RuleContext} context The rule context.
  87. */
  88. function definePropertyReferenceExtractor(context) {
  89. /** @type {Map<Expression, IPropertyReferences>} */
  90. const cacheForExpression = new Map()
  91. /** @type {Map<Pattern, IPropertyReferences>} */
  92. const cacheForPattern = new Map()
  93. /** @type {Map<FunctionExpression | ArrowFunctionExpression | FunctionDeclaration, Map<number, IPropertyReferences>>} */
  94. const cacheForFunction = new Map()
  95. /** @type {{ toRefNodes: Set<ESNode>, toRefsNodes: Set<ESNode>} | null} */
  96. let toRefSet = null
  97. let isFunctionalTemplate = false
  98. const templateBody = context.getSourceCode().ast.templateBody
  99. if (templateBody) {
  100. isFunctionalTemplate = utils.hasAttribute(templateBody, 'functional')
  101. }
  102. function getToRefSet() {
  103. if (toRefSet) {
  104. return toRefSet
  105. }
  106. const tracker = new eslintUtils.ReferenceTracker(
  107. context.getSourceCode().scopeManager.scopes[0]
  108. )
  109. const toRefNodes = new Set()
  110. for (const { node } of tracker.iterateEsmReferences(
  111. utils.createCompositionApiTraceMap({
  112. [eslintUtils.ReferenceTracker.ESM]: true,
  113. toRef: {
  114. [eslintUtils.ReferenceTracker.CALL]: true
  115. }
  116. })
  117. )) {
  118. toRefNodes.add(node)
  119. }
  120. const toRefsNodes = new Set()
  121. for (const { node } of tracker.iterateEsmReferences(
  122. utils.createCompositionApiTraceMap({
  123. [eslintUtils.ReferenceTracker.ESM]: true,
  124. toRefs: {
  125. [eslintUtils.ReferenceTracker.CALL]: true
  126. }
  127. })
  128. )) {
  129. toRefsNodes.add(node)
  130. }
  131. return (toRefSet = { toRefNodes, toRefsNodes })
  132. }
  133. /**
  134. * Collects the property references for member expr.
  135. * @implements {IPropertyReferences}
  136. */
  137. class PropertyReferencesForMember {
  138. /**
  139. *
  140. * @param {MemberExpression} node
  141. * @param {string} name
  142. * @param {boolean} withInTemplate
  143. */
  144. constructor(node, name, withInTemplate) {
  145. this.node = node
  146. this.name = name
  147. this.withInTemplate = withInTemplate
  148. }
  149. /**
  150. * @param {string} name
  151. */
  152. hasProperty(name) {
  153. return name === this.name
  154. }
  155. allProperties() {
  156. return new Map([[this.name, { nodes: [this.node.property] }]])
  157. }
  158. /**
  159. * @param {string} name
  160. * @returns {IPropertyReferences}
  161. */
  162. getNest(name) {
  163. return name === this.name
  164. ? extractFromExpression(this.node, this.withInTemplate)
  165. : NEVER
  166. }
  167. /**
  168. * @param {string} name
  169. * @returns {Iterable<NestPropertyNodeForExpression>}
  170. */
  171. *getNestNodes(name) {
  172. if (name === this.name) {
  173. yield {
  174. type: 'expression',
  175. node: this.node
  176. }
  177. }
  178. }
  179. }
  180. /**
  181. * Collects the property references for object.
  182. * @implements {IPropertyReferences}
  183. */
  184. class PropertyReferencesForObject {
  185. constructor() {
  186. /** @type {Record<string, AssignmentProperty[]>} */
  187. this.properties = Object.create(null)
  188. }
  189. /**
  190. * @param {string} name
  191. */
  192. hasProperty(name) {
  193. return Boolean(this.properties[name])
  194. }
  195. allProperties() {
  196. const result = new Map()
  197. for (const [name, nodes] of Object.entries(this.properties)) {
  198. result.set(name, { nodes: nodes.map((node) => node.key) })
  199. }
  200. return result
  201. }
  202. /**
  203. * @param {string} name
  204. * @returns {IPropertyReferences}
  205. */
  206. getNest(name) {
  207. const properties = this.properties[name]
  208. return properties
  209. ? mergePropertyReferences(
  210. properties.map((property) => getNestFromPattern(property.value))
  211. )
  212. : NEVER
  213. }
  214. /**
  215. * @param {string} name
  216. * @returns {Iterable<NestPropertyNodeForPattern>}
  217. */
  218. *getNestNodes(name) {
  219. const properties = this.properties[name]
  220. if (!properties) {
  221. return
  222. }
  223. const values = properties.map((property) => property.value)
  224. let node
  225. while ((node = values.shift())) {
  226. if (
  227. node.type === 'Identifier' ||
  228. node.type === 'MemberExpression' ||
  229. node.type === 'ObjectPattern' ||
  230. node.type === 'ArrayPattern'
  231. ) {
  232. yield {
  233. type: 'pattern',
  234. node
  235. }
  236. } else if (node.type === 'AssignmentPattern') {
  237. values.unshift(node.left)
  238. }
  239. }
  240. return properties ? properties.map((p) => p.value) : []
  241. }
  242. }
  243. /**
  244. * @param {Pattern} pattern
  245. * @returns {IPropertyReferences}
  246. */
  247. function getNestFromPattern(pattern) {
  248. if (pattern.type === 'ObjectPattern') {
  249. return extractFromObjectPattern(pattern)
  250. }
  251. if (pattern.type === 'Identifier') {
  252. return extractFromIdentifier(pattern)
  253. } else if (pattern.type === 'AssignmentPattern') {
  254. return getNestFromPattern(pattern.left)
  255. }
  256. return ANY
  257. }
  258. /**
  259. * Extract the property references from Expression.
  260. * @param {Identifier | MemberExpression | ChainExpression | ThisExpression | CallExpression} node
  261. * @param {boolean} withInTemplate
  262. * @returns {IPropertyReferences}
  263. */
  264. function extractFromExpression(node, withInTemplate) {
  265. const ref = cacheForExpression.get(node)
  266. if (ref) {
  267. return ref
  268. }
  269. cacheForExpression.set(node, ANY)
  270. const result = extractWithoutCache()
  271. cacheForExpression.set(node, result)
  272. return result
  273. function extractWithoutCache() {
  274. const parent = node.parent
  275. switch (parent.type) {
  276. case 'AssignmentExpression': {
  277. // `({foo} = arg)`
  278. return !withInTemplate &&
  279. parent.right === node &&
  280. parent.operator === '='
  281. ? extractFromPattern(parent.left)
  282. : NEVER
  283. }
  284. case 'VariableDeclarator': {
  285. // `const {foo} = arg`
  286. // `const foo = arg`
  287. return !withInTemplate && parent.init === node
  288. ? extractFromPattern(parent.id)
  289. : NEVER
  290. }
  291. case 'MemberExpression': {
  292. if (parent.object === node) {
  293. // `arg.foo`
  294. const name = utils.getStaticPropertyName(parent)
  295. return name
  296. ? new PropertyReferencesForMember(parent, name, withInTemplate)
  297. : ANY
  298. }
  299. return NEVER
  300. }
  301. case 'CallExpression': {
  302. const argIndex = parent.arguments.indexOf(node)
  303. // `foo(arg)`
  304. return !withInTemplate && argIndex > -1
  305. ? extractFromCall(parent, argIndex)
  306. : NEVER
  307. }
  308. case 'ChainExpression': {
  309. return extractFromExpression(parent, withInTemplate)
  310. }
  311. case 'ArrowFunctionExpression':
  312. case 'ReturnStatement':
  313. case 'VExpressionContainer':
  314. case 'Property':
  315. case 'ArrayExpression': {
  316. return maybeExternalUsed(parent) ? ANY : NEVER
  317. }
  318. }
  319. return NEVER
  320. }
  321. /**
  322. * @param {ASTNode} parentTarget
  323. * @returns {boolean}
  324. */
  325. function maybeExternalUsed(parentTarget) {
  326. if (
  327. parentTarget.type === 'ReturnStatement' ||
  328. parentTarget.type === 'VExpressionContainer'
  329. ) {
  330. return true
  331. }
  332. if (parentTarget.type === 'ArrayExpression') {
  333. return maybeExternalUsed(parentTarget.parent)
  334. }
  335. if (parentTarget.type === 'Property') {
  336. return maybeExternalUsed(parentTarget.parent.parent)
  337. }
  338. if (parentTarget.type === 'ArrowFunctionExpression') {
  339. return parentTarget.body === node
  340. }
  341. return false
  342. }
  343. }
  344. /**
  345. * Extract the property references from one parameter of the function.
  346. * @param {Pattern} node
  347. * @returns {IPropertyReferences}
  348. */
  349. function extractFromPattern(node) {
  350. const ref = cacheForPattern.get(node)
  351. if (ref) {
  352. return ref
  353. }
  354. cacheForPattern.set(node, ANY)
  355. const result = extractWithoutCache()
  356. cacheForPattern.set(node, result)
  357. return result
  358. function extractWithoutCache() {
  359. while (node.type === 'AssignmentPattern') {
  360. node = node.left
  361. }
  362. if (node.type === 'RestElement' || node.type === 'ArrayPattern') {
  363. // cannot check
  364. return NEVER
  365. }
  366. if (node.type === 'ObjectPattern') {
  367. return extractFromObjectPattern(node)
  368. }
  369. if (node.type === 'Identifier') {
  370. return extractFromIdentifier(node)
  371. }
  372. return NEVER
  373. }
  374. }
  375. /**
  376. * Extract the property references from ObjectPattern.
  377. * @param {ObjectPattern} node
  378. * @returns {IPropertyReferences}
  379. */
  380. function extractFromObjectPattern(node) {
  381. const refs = new PropertyReferencesForObject()
  382. for (const prop of node.properties) {
  383. if (prop.type === 'Property') {
  384. const name = utils.getStaticPropertyName(prop)
  385. if (name) {
  386. const list = refs.properties[name] || (refs.properties[name] = [])
  387. list.push(prop)
  388. } else {
  389. // If cannot trace name, everything is used!
  390. return ANY
  391. }
  392. } else {
  393. // If use RestElement, everything is used!
  394. return ANY
  395. }
  396. }
  397. return refs
  398. }
  399. /**
  400. * Extract the property references from id.
  401. * @param {Identifier} node
  402. * @returns {IPropertyReferences}
  403. */
  404. function extractFromIdentifier(node) {
  405. const variable = utils.findVariableByIdentifier(context, node)
  406. if (!variable) {
  407. return NEVER
  408. }
  409. return mergePropertyReferences(
  410. variable.references.map((reference) => {
  411. const id = reference.identifier
  412. return extractFromExpression(id, false)
  413. })
  414. )
  415. }
  416. /**
  417. * Extract the property references from call.
  418. * @param {CallExpression} node
  419. * @param {number} argIndex
  420. * @returns {IPropertyReferences}
  421. */
  422. function extractFromCall(node, argIndex) {
  423. if (node.callee.type !== 'Identifier') {
  424. return {
  425. hasProperty(_name, options) {
  426. return Boolean(options && options.unknownCallAsAny)
  427. },
  428. allProperties: () => new Map(),
  429. getNest: () => ANY,
  430. getNestNodes: () => []
  431. }
  432. }
  433. const fnNode = findFunction(context, node.callee)
  434. if (!fnNode) {
  435. if (argIndex === 0) {
  436. if (getToRefSet().toRefNodes.has(node)) {
  437. return extractFromToRef(node)
  438. } else if (getToRefSet().toRefsNodes.has(node)) {
  439. return extractFromToRefs(node)
  440. }
  441. }
  442. return {
  443. hasProperty(_name, options) {
  444. return Boolean(options && options.unknownCallAsAny)
  445. },
  446. allProperties: () => new Map(),
  447. getNest: () => ANY,
  448. getNestNodes: () => []
  449. }
  450. }
  451. return extractFromFunctionParam(fnNode, argIndex)
  452. }
  453. /**
  454. * Extract the property references from function param.
  455. * @param {FunctionExpression | ArrowFunctionExpression | FunctionDeclaration} node
  456. * @param {number} argIndex
  457. * @returns {IPropertyReferences}
  458. */
  459. function extractFromFunctionParam(node, argIndex) {
  460. let cacheForIndexes = cacheForFunction.get(node)
  461. if (!cacheForIndexes) {
  462. cacheForIndexes = new Map()
  463. cacheForFunction.set(node, cacheForIndexes)
  464. }
  465. const ref = cacheForIndexes.get(argIndex)
  466. if (ref) {
  467. return ref
  468. }
  469. cacheForIndexes.set(argIndex, NEVER)
  470. const arg = node.params[argIndex]
  471. if (!arg) {
  472. return NEVER
  473. }
  474. const result = extractFromPattern(arg)
  475. cacheForIndexes.set(argIndex, result)
  476. return result
  477. }
  478. /**
  479. * Extract the property references from path.
  480. * @param {string} pathString
  481. * @param {Identifier | Literal | TemplateLiteral} node
  482. * @returns {IPropertyReferences}
  483. */
  484. function extractFromPath(pathString, node) {
  485. return extractFromSegments(pathString.split('.'))
  486. /**
  487. * @param {string[]} segments
  488. * @returns {IPropertyReferences}
  489. */
  490. function extractFromSegments(segments) {
  491. if (segments.length === 0) {
  492. return ANY
  493. }
  494. const segmentName = segments[0]
  495. return {
  496. hasProperty: (name) => name === segmentName,
  497. allProperties: () => new Map([[segmentName, { nodes: [node] }]]),
  498. getNest: (name) =>
  499. name === segmentName ? extractFromSegments(segments.slice(1)) : NEVER,
  500. getNestNodes: () => []
  501. }
  502. }
  503. }
  504. /**
  505. * Extract the property references from name literal.
  506. * @param {Expression} node
  507. * @returns {IPropertyReferences}
  508. */
  509. function extractFromNameLiteral(node) {
  510. const referenceName =
  511. node.type === 'Literal' || node.type === 'TemplateLiteral'
  512. ? utils.getStringLiteralValue(node)
  513. : null
  514. return referenceName
  515. ? {
  516. hasProperty: (name) => name === referenceName,
  517. allProperties: () => new Map([[referenceName, { nodes: [node] }]]),
  518. getNest: (name) => (name === referenceName ? ANY : NEVER),
  519. getNestNodes: () => []
  520. }
  521. : NEVER
  522. }
  523. /**
  524. * Extract the property references from name.
  525. * @param {string} referenceName
  526. * @param {Expression|SpreadElement} nameNode
  527. * @param { () => IPropertyReferences } [getNest]
  528. * @returns {IPropertyReferences}
  529. */
  530. function extractFromName(referenceName, nameNode, getNest) {
  531. return {
  532. hasProperty: (name) => name === referenceName,
  533. allProperties: () => new Map([[referenceName, { nodes: [nameNode] }]]),
  534. getNest: (name) =>
  535. name === referenceName ? (getNest ? getNest() : ANY) : NEVER,
  536. getNestNodes: () => []
  537. }
  538. }
  539. /**
  540. * Extract the property references from toRef call.
  541. * @param {CallExpression} node
  542. * @returns {IPropertyReferences}
  543. */
  544. function extractFromToRef(node) {
  545. const nameNode = node.arguments[1]
  546. const refName =
  547. nameNode &&
  548. (nameNode.type === 'Literal' || nameNode.type === 'TemplateLiteral')
  549. ? utils.getStringLiteralValue(nameNode)
  550. : null
  551. if (!refName) {
  552. // unknown name
  553. return ANY
  554. }
  555. return extractFromName(refName, nameNode, () =>
  556. extractFromExpression(node, false).getNest('value')
  557. )
  558. }
  559. /**
  560. * Extract the property references from toRefs call.
  561. * @param {CallExpression} node
  562. * @returns {IPropertyReferences}
  563. */
  564. function extractFromToRefs(node) {
  565. const base = extractFromExpression(node, false)
  566. return {
  567. hasProperty: (name, option) => base.hasProperty(name, option),
  568. allProperties: () => base.allProperties(),
  569. getNest: (name) => base.getNest(name).getNest('value'),
  570. getNestNodes: (name) => base.getNest(name).getNestNodes('value')
  571. }
  572. }
  573. /**
  574. * Extract the property references from VExpressionContainer.
  575. * @param {VExpressionContainer} node
  576. * @param {object} [options]
  577. * @param {boolean} [options.ignoreGlobals]
  578. * @returns {IPropertyReferences}
  579. */
  580. function extractFromVExpressionContainer(node, options) {
  581. const ignoreGlobals = options && options.ignoreGlobals
  582. /** @type { (name:string)=>boolean } */
  583. let ignoreRef = () => false
  584. if (ignoreGlobals) {
  585. const globalScope =
  586. context.getSourceCode().scopeManager.globalScope ||
  587. context.getSourceCode().scopeManager.scopes[0]
  588. ignoreRef = (name) => globalScope.set.has(name)
  589. }
  590. /** @type {IPropertyReferences[]} */
  591. const references = []
  592. for (const id of node.references
  593. .filter((ref) => ref.variable == null)
  594. .map((ref) => ref.id)) {
  595. if (ignoreRef(id.name)) {
  596. continue
  597. }
  598. if (!isFunctionalTemplate) {
  599. references.push(
  600. extractFromName(id.name, id, () => extractFromExpression(id, true))
  601. )
  602. } else {
  603. if (id.name === 'props') {
  604. references.push(extractFromExpression(id, true))
  605. }
  606. }
  607. }
  608. return mergePropertyReferences(references)
  609. }
  610. /**
  611. * Extract the property references from StyleVariablesContext.
  612. * @param {StyleVariablesContext} ctx
  613. * @returns {IPropertyReferences}
  614. */
  615. function extractFromStyleVariablesContext(ctx) {
  616. const references = []
  617. for (const { id } of ctx.references) {
  618. references.push(
  619. extractFromName(id.name, id, () => extractFromExpression(id, true))
  620. )
  621. }
  622. return mergePropertyReferences(references)
  623. }
  624. return {
  625. extractFromExpression,
  626. extractFromPattern,
  627. extractFromFunctionParam,
  628. extractFromPath,
  629. extractFromName,
  630. extractFromNameLiteral,
  631. extractFromVExpressionContainer,
  632. extractFromStyleVariablesContext
  633. }
  634. }
  635. /**
  636. * @param {IPropertyReferences[]} references
  637. * @returns {IPropertyReferences}
  638. */
  639. function mergePropertyReferences(references) {
  640. if (references.length === 0) {
  641. return NEVER
  642. }
  643. if (references.length === 1) {
  644. return references[0]
  645. }
  646. return new PropertyReferencesForMerge(references)
  647. }
  648. /**
  649. * Collects the property references for merge.
  650. * @implements {IPropertyReferences}
  651. */
  652. class PropertyReferencesForMerge {
  653. /**
  654. * @param {IPropertyReferences[]} references
  655. */
  656. constructor(references) {
  657. this.references = references
  658. }
  659. /**
  660. * @param {string} name
  661. * @param {IHasPropertyOption} [option]
  662. */
  663. hasProperty(name, option) {
  664. return this.references.some((ref) => ref.hasProperty(name, option))
  665. }
  666. allProperties() {
  667. const result = new Map()
  668. for (const reference of this.references) {
  669. for (const [name, { nodes }] of reference.allProperties()) {
  670. const r = result.get(name)
  671. if (r) {
  672. r.nodes = [...new Set([...r.nodes, ...nodes])]
  673. } else {
  674. result.set(name, { nodes: [...nodes] })
  675. }
  676. }
  677. }
  678. return result
  679. }
  680. /**
  681. * @param {string} name
  682. * @returns {IPropertyReferences}
  683. */
  684. getNest(name) {
  685. /** @type {IPropertyReferences[]} */
  686. const nest = []
  687. for (const ref of this.references) {
  688. if (ref.hasProperty(name)) {
  689. nest.push(ref.getNest(name))
  690. }
  691. }
  692. return mergePropertyReferences(nest)
  693. }
  694. /**
  695. * @param {string} name
  696. * @returns {Iterable<NestPropertyNode>}
  697. */
  698. *getNestNodes(name) {
  699. for (const ref of this.references) {
  700. if (ref.hasProperty(name)) {
  701. yield* ref.getNestNodes(name)
  702. }
  703. }
  704. }
  705. }