* Resolve a Lua/Luau `require(...)` to its module file. The reference name is * either a dotted module path (`telescope.config` → `telescope/config.lua`) or a * Roblox instance-path leaf (`Signal` from `require(script.Parent.Signal)` → * `Signal.luau`). We try ` .lua|.luau` and ` /init.
(ref: UnresolvedRef, context: ResolutionContext)
| 1671 | * requiring file wins (instance-path requires resolve within the same package). |
| 1672 | */ |
| 1673 | function resolveLuaRequire(ref: UnresolvedRef, context: ResolutionContext): ResolvedRef | null { |
| 1674 | const name = ref.referenceName; |
| 1675 | if (!name) return null; |
| 1676 | const base = name.includes('.') ? name.replace(/\./g, '/') : name; |
| 1677 | const suffixes = [`${base}.lua`, `${base}.luau`, `${base}/init.lua`, `${base}/init.luau`]; |
| 1678 | const byBasename = luaBasenameIndex(context); |
| 1679 | const shared = (a: string, b: string): number => { |
| 1680 | let i = 0; |
| 1681 | while (i < a.length && i < b.length && a[i] === b[i]) i++; |
| 1682 | return i; |
| 1683 | }; |
| 1684 | for (const suffix of suffixes) { |
| 1685 | // Only files sharing the suffix's basename can match — the bucket is in |
| 1686 | // getAllFiles() order, so this filter yields exactly what the full-list |
| 1687 | // scan did. |
| 1688 | const candidates = byBasename.get(suffix.split('/').pop() ?? '') ?? []; |
| 1689 | const matches = candidates.filter((f) => f === suffix || f.endsWith('/' + suffix)); |
| 1690 | if (matches.length === 0) continue; |
| 1691 | matches.sort((x, y) => shared(y, ref.filePath) - shared(x, ref.filePath)); |
| 1692 | const best = matches[0]!; |
| 1693 | if (best === ref.filePath) continue; |
| 1694 | const fileNode = context.getNodesInFile(best).find((n) => n.kind === 'file'); |
| 1695 | if (fileNode) { |
| 1696 | // Confidence ≥ 0.9 so this deterministic path/suffix match wins over |
| 1697 | // name-matching, which otherwise resolves the require to the import node |
| 1698 | // itself (a same-name self-match). |
| 1699 | return { original: ref, targetNodeId: fileNode.id, confidence: 0.9, resolvedBy: 'import' }; |
| 1700 | } |
| 1701 | } |
| 1702 | return null; |
| 1703 | } |
| 1704 | |
| 1705 | function resolveModuleImportToFile( |
| 1706 | ref: UnresolvedRef, |
no test coverage detected