property-references.js 21 KB

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