You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
JS: jsconfig.json baseUrl not honoured — non-relative imports (static AND dynamic) produce no edge, orphaning every module in a Rails/webpacker app #2153
JS imports written against jsconfig.json's compilerOptions.baseUrl produce no edge. Relative specifiers in the same file resolve fine. This affects static and dynamic imports equally — it is not a dynamic-import() issue.
Graphify already parsesjsconfig.json (it emits compilerOptions / baseUrl nodes), it just doesn't use it for module resolution.
Related but distinct: #147 asks for TypeScripttsconfigpaths. This is plain JS + jsconfigbaseUrl, which is the default layout for Rails/webpacker and Vue SPAs.
Reproduction
gfjs/
jsconfig.json {"compilerOptions": {"baseUrl": "app/javascript"}}
app/javascript/mods/Local.js export default class Local {}
app/javascript/mods/Widget.js export default class Widget {}
app/javascript/mods/Registrar.js
Registrar.js --imports_from --> Local.js
Registrar.js --imports --> Local
Registrar.js --contains --> Registrar
Registrar --method --> .lazy()
Widget.js --contains --> Widget <-- no inbound edge at all
jsconfig.json --contains --> compilerOptions
compilerOptions --contains --> baseUrl <-- config IS parsed
specifier
edge created?
'./Local.js' relative
✅
'mods/Widget.js' baseUrl static
❌
import('mods/Widget.js') baseUrl dynamic
❌
A separate matrix (no config) confirms relative works with and without the extension, so the variable is non-relative, not the extension.
Why it matters
In a real Rails + webpacker app (~17k nodes, jsconfig.json with baseUrl: app/javascript), every dashboard module and instruction is an orphan node. The registrar does:
and EvosepSystemSuitabilityModule.js has 0 inbound edges. So:
$ graphify affected "EvosepSystemSuitabilityModule.js" --depth 2
No affected nodes found.
…even though the registrar is a real dependency. On a "what will break if I change this?" question, a confident empty result is the worst possible failure mode — plain grep finds it in one call. This silently affects every module/instruction in the app.
Suggested fix
Honour jsconfig.json / tsconfig.jsoncompilerOptions.baseUrl (and paths) when resolving JS specifiers, same as #147 proposes for TS. Falling back to "try <baseUrl>/<specifier>" would cover the common case.
but only as the base that paths targets resolve against. _read_tsconfig_aliases returns onlypaths entries, so a config with baseUrl and no paths — exactly the webpacker layout — yields an empty alias map and every non-relative specifier dies. That is the primary defect.
The second is the one you spotted: _load_tsconfig_aliases only probes tsconfig.json (resolution.py:177), never jsconfig.json, even though json_config.py already indexes jsconfig.json — which is why you see compilerOptions/baseUrl nodes in the graph while resolution ignores the file.
Verified on 0.9.26 with your layout (jsconfig.json with "baseUrl": "app/javascript", packs/dashboard.js importing 'mods/Widget.js'). Before, dashboard.js has zero outbound edges. After, it has imports and re_exports edges into mods/Widget.js.
PR shortly. baseUrl becomes a resolution root of last resort, tried only after every declared alias misses, so existing paths precedence (#1269) is untouched — notably it is not modelled as a synthesized * alias, because that would outrank declared directory-prefix aliases and shadow them. Static and both dynamic-import paths are covered.
Activity
Rishet11 commentedon Jul 25, 2026
Confirmed, with one correction to the diagnosis that changes the fix.
baseUrlis not simply unparsed — it is read, atresolution.py:146-147:but only as the base that
pathstargets resolve against._read_tsconfig_aliasesreturns onlypathsentries, so a config withbaseUrland nopaths— exactly the webpacker layout — yields an empty alias map and every non-relative specifier dies. That is the primary defect.The second is the one you spotted:
_load_tsconfig_aliasesonly probestsconfig.json(resolution.py:177), neverjsconfig.json, even thoughjson_config.pyalready indexesjsconfig.json— which is why you seecompilerOptions/baseUrlnodes in the graph while resolution ignores the file.Verified on 0.9.26 with your layout (
jsconfig.jsonwith"baseUrl": "app/javascript",packs/dashboard.jsimporting'mods/Widget.js'). Before,dashboard.jshas zero outbound edges. After, it hasimportsandre_exportsedges intomods/Widget.js.PR shortly.
baseUrlbecomes a resolution root of last resort, tried only after every declared alias misses, so existingpathsprecedence (#1269) is untouched — notably it is not modelled as a synthesized*alias, because that would outrank declared directory-prefix aliases and shadow them. Static and both dynamic-import paths are covered.fix(resolution): honor jsconfig.json and compilerOptions.baseUrl (#2153)
test(resolution): cover jsconfig/baseUrl module resolution (#2153)
chore: bump to 0.9.27; changelog for #2167/#2169/#2154/#2153/#2147/#2168
test(resolution): assert canonical import-target ids in jsconfig test…
safishamsi commentedon Jul 26, 2026
Fixed in v0.9.27 (#2176, thanks @Rishet11). Non-relative JS/TS imports resolve through jsconfig.json/tsconfig.json baseUrl and paths. https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.27
Squashed commit of the following:
fix(resolution): honor jsconfig.json and compilerOptions.baseUrl (Gra…
test(resolution): cover jsconfig/baseUrl module resolution (Graphify-…
chore: bump to 0.9.27; changelog for Graphify-Labs#2167/Graphify-Labs…
test(resolution): assert canonical import-target ids in jsconfig test…