Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion backend/app/app_compile_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,17 @@ def runtime_library_aliases() -> tuple[tuple[str, Path], ...]:
``app_runtime_inject.js``. React then sees two dispatchers and every hook
fails at first render. Package-root aliases apply to the root and its subpaths
(for example ``react/jsx-runtime``), keeping each supported library singular.

Three's documented ``three/addons/*`` export is backed by the physical
``examples/jsm`` directory rather than an ``addons`` directory. Once the
package root is replaced with an absolute alias, esbuild no longer consults
Three's package exports for that subpath. Pin the public addons spelling to
its runtime-owned physical directory before adding the package roots.
"""
node_path = runtime_node_path()
roots = sorted({_package_root(specifier) for specifier in BUNDLED_RUNTIME_LIBS})
return tuple((root, node_path / root) for root in roots)
subpaths = (("three/addons", node_path / "three" / "examples" / "jsm"),)
return subpaths + tuple((root, node_path / root) for root in roots)


NO_DEFAULT_EXPORT_ERROR = (
Expand Down
45 changes: 45 additions & 0 deletions backend/tests/test_runtime_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
mobius_runtime_path,
runtime_library_aliases,
runtime_inject_path,
runtime_node_path,
)


Expand Down Expand Up @@ -121,6 +122,50 @@ def test_app_local_or_transitive_react_cannot_shadow_platform_runtime(tmp_path):
assert "shadow-react-copy" not in output.read_text()


def test_three_addons_resolve_from_the_pinned_runtime(tmp_path):
"""Documented addons imports must survive package-root runtime pinning."""
aliases = dict(runtime_library_aliases())
assert aliases["three"] == runtime_node_path() / "three"
assert (
aliases["three/addons"]
== runtime_node_path() / "three" / "examples" / "jsm"
)

entry = tmp_path / "three-addons.jsx"
output = tmp_path / "three-addons.js"
metafile = tmp_path / "three-addons-meta.json"
entry.write_text(
"""import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { STLLoader } from 'three/addons/loaders/STLLoader.js'

export default function ThreeAddonsFixture() {
return [OrbitControls.name, STLLoader.name]
}
"""
)

completed = subprocess.run(
esbuild_command(entry, output, metafile=metafile),
capture_output=True,
check=False,
env=esbuild_environment(),
text=True,
timeout=ESBUILD_TIMEOUT_SECS,
)
assert completed.returncode == 0, completed.stderr

metadata = json.loads(metafile.read_text())
entry_outputs = [
details for details in metadata["outputs"].values()
if details.get("entryPoint")
]
assert len(entry_outputs) == 1
assert entry_outputs[0].get("imports") == [], (
"Three addons escaped the pinned self-contained app bundle"
)
assert output.is_file() and output.stat().st_size > 0


def test_app_hosts_have_no_runtime_import_map_or_static_module_imports():
frame = FRAME.read_text()
standalone = STANDALONE.read_text()
Expand Down
Loading