Skip to content

Commit dbba4c3

Browse files
committed
fix(parser): recognize Gleam dev/ directories as source roots
Modern Gleam packages have src/, test/ and dev/ module directories. Without dev/ roots, imports from or into dev/ modules resolved into foreign packages when module names collided. Adds dev to both the structural and the gleam.toml-derived root derivation, with tests.
1 parent 8fbbc57 commit dbba4c3

2 files changed

Lines changed: 21 additions & 14 deletions

File tree

src/jcodemunch_mcp/parser/imports.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -937,14 +937,15 @@ def _gleam_source_roots(source_files) -> tuple[str, ...]:
937937
"""Detect Gleam source roots from the indexed file set.
938938
939939
A Gleam module path like ``webse/config_types`` is relative to a
940-
package's ``src/`` or ``test/`` directory, so those directories are the
941-
source roots. They are derived structurally: every path prefix of an
942-
indexed ``.gleam`` file that ends in a ``src`` or ``test`` segment
943-
(``cells/webse/src/webse/config.gleam`` -> ``cells/webse/src``), plus
944-
``<dir>/src`` and ``<dir>/test`` for every indexed ``gleam.toml``. The
945-
two signals overlap for normal packages; the gleam.toml one also covers
946-
packages whose files did not make it into the index, and the structural
947-
one covers indexes that exclude toml files.
940+
package's ``src/``, ``test/`` or ``dev/`` directory, so those
941+
directories are the source roots. They are derived structurally: every
942+
path prefix of an indexed ``.gleam`` file that ends in a ``src``,
943+
``test`` or ``dev`` segment (``cells/webse/src/webse/config.gleam`` ->
944+
``cells/webse/src``), plus ``<dir>/src``, ``<dir>/test`` and
945+
``<dir>/dev`` for every indexed ``gleam.toml``. The two signals overlap
946+
for normal packages; the gleam.toml one also covers packages whose
947+
files did not make it into the index, and the structural one covers
948+
indexes that exclude toml files.
948949
"""
949950
cache_key = source_files if isinstance(source_files, frozenset) else frozenset(source_files)
950951
cached = _gleam_roots_cache.get(cache_key)
@@ -956,11 +957,11 @@ def _gleam_source_roots(source_files) -> tuple[str, ...]:
956957
if f.endswith(".gleam"):
957958
parts = f.split("/")
958959
for i, seg in enumerate(parts[:-1]):
959-
if seg in ("src", "test"):
960+
if seg in ("src", "test", "dev"):
960961
roots.add("/".join(parts[: i + 1]))
961962
elif f == "gleam.toml" or f.endswith("/gleam.toml"):
962963
pkg_dir = f[: -len("/gleam.toml")] if "/" in f else ""
963-
for sub in ("src", "test"):
964+
for sub in ("src", "test", "dev"):
964965
roots.add(f"{pkg_dir}/{sub}" if pkg_dir else sub)
965966

966967
result = tuple(sorted(roots))
@@ -1356,9 +1357,9 @@ def resolve_specifier(
13561357

13571358
# Gleam module-style import: 'webse/config_types' →
13581359
# 'cells/webse/src/webse/config_types.gleam'. Gleam module paths are
1359-
# slash-joined lowercase segments resolved against a package's src/ or
1360-
# test/ root; the target extension is always .gleam, so candidates are
1361-
# built directly instead of via _candidates. The importer's own package
1360+
# slash-joined lowercase segments resolved against a package's src/,
1361+
# test/ or dev/ root; the target extension is always .gleam, so candidates
1362+
# are built directly instead of via _candidates. The importer's own package
13621363
# root is tried first: module paths are only unique per package, and
13631364
# same-package imports are the common case. Stdlib and hex-dependency
13641365
# imports (gleam/io, gleam/list) resolve to nothing — no edge is created.

tests/test_gleam_imports.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ def test_no_false_positives_from_non_import_lines(self):
8787
"cells/webse/src/webse/config_types.gleam",
8888
"cells/webse/test/webse_test.gleam",
8989
"cells/webse/test/support/helper.gleam",
90+
"cells/webse/dev/seed_data.gleam",
9091
"soma/src/soma/config_yaml.gleam",
9192
"cx/src/cx/cluster_config_yaml.gleam",
9293
"cells/webse/gleam.toml",
@@ -101,6 +102,7 @@ def test_structural_roots_from_gleam_files(self):
101102
assert "packages/yamlutils/src" in roots
102103
assert "cells/webse/src" in roots
103104
assert "cells/webse/test" in roots
105+
assert "cells/webse/dev" in roots
104106
assert "soma/src" in roots
105107
assert "cx/src" in roots
106108

@@ -109,6 +111,7 @@ def test_gleam_toml_derived_roots(self):
109111
roots = _gleam_source_roots(files)
110112
assert "cells/empty/src" in roots
111113
assert "cells/empty/test" in roots
114+
assert "cells/empty/dev" in roots
112115

113116
def test_root_level_package(self):
114117
files = frozenset({"src/app.gleam", "test/app_test.gleam", "gleam.toml"})
@@ -132,12 +135,15 @@ class TestGleamSpecifierResolution:
132135
# Test module importing a test-only helper module
133136
("support/helper", "cells/webse/test/webse_test.gleam",
134137
"cells/webse/test/support/helper.gleam"),
138+
# Dev module importing its package's src module
139+
("webse/config_types", "cells/webse/dev/seed_data.gleam",
140+
"cells/webse/src/webse/config_types.gleam"),
135141
# Stdlib / hex dependency: unresolvable, no edge
136142
("gleam/io", "cells/webse/src/webse/config_yaml.gleam", None),
137143
("gleam/list", "soma/src/soma/config_yaml.gleam", None),
138144
], ids=[
139145
"cross_package", "same_package", "test_to_src", "test_helper",
140-
"stdlib_io", "stdlib_list",
146+
"dev_to_src", "stdlib_io", "stdlib_list",
141147
])
142148
def test_resolution(self, specifier, importer, expected):
143149
result = resolve_specifier(specifier, importer, set(WEVE_STYLE_FILES))

0 commit comments

Comments
 (0)