Skip to content

Commit ff38ac7

Browse files
committed
feat: fix jco toolchain and enable single-file JavaScript components
Major improvements to JavaScript component support: ## Core Fixes - **Fixed jco wrapper script**: Removed problematic `cd` command that broke module resolution - **Simplified js_component implementation**: Replaced complex workspace setup with direct file copying - **Working single-file components**: Created and verified `simple_js_component` builds successfully (11MB WASM output) ## New Working Example - **examples/js_component:simple_js_component**: Demonstrates working JavaScript→WASM compilation - **Hermetic jco toolchain**: Now preserves working directory for proper module resolution ## Known Limitations - Multi-file components with ES module imports still fail due to jco/componentize-js module resolution - Components using `import ... from "./file.js"` need bundling or alternative approaches ## Verified Functionality - ✅ jco toolchain setup and basic compilation works - ✅ Single-file JavaScript components build successfully - ✅ WebAssembly component output is valid and correctly sized - ⚠️ Multi-file imports require further investigation This establishes a solid foundation for JavaScript component development.
1 parent 48a1305 commit ff38ac7

File tree

5 files changed

+115
-53
lines changed

5 files changed

+115
-53
lines changed

examples/js_component/BUILD.bazel

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,15 @@ js_component(
4848
wit = "wit/calculator.wit",
4949
world = "calculator",
5050
)
51+
52+
# Simple JavaScript component without imports to test basic jco functionality
53+
js_component(
54+
name = "simple_js_component",
55+
package_name = "example:[email protected]",
56+
srcs = [
57+
"src/simple.js",
58+
],
59+
entry_point = "simple.js",
60+
wit = "wit/simple.wit",
61+
world = "simple",
62+
)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Simple JavaScript component without imports to test basic jco functionality
2+
3+
export function sayHello(name) {
4+
return `Hello, ${name}!`;
5+
}
6+
7+
export function getTime() {
8+
return new Date().toISOString();
9+
}
10+
11+
export function add(a, b) {
12+
return a + b;
13+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package example:simple@1.0.0;
2+
3+
world simple {
4+
export say-hello: func(name: string) -> string;
5+
export get-time: func() -> string;
6+
export add: func(a: s32, b: s32) -> s32;
7+
}

js/defs.bzl

Lines changed: 83 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
load("@bazel_skylib//lib:paths.bzl", "paths")
44
load("//providers:providers.bzl", "WasmComponentInfo")
55
load("//rust:transitions.bzl", "wasm_transition")
6-
load("//tools/bazel_helpers:file_ops_actions.bzl", "setup_js_workspace_action")
76

87
def _js_component_impl(ctx):
98
"""Implementation of js_component rule"""
@@ -37,63 +36,96 @@ def _js_component_impl(ctx):
3736
content = json.encode(package_content),
3837
)
3938

40-
# Prepare JavaScript workspace using File Operations Component
41-
work_dir = setup_js_workspace_action(
42-
ctx,
43-
sources = source_files,
44-
package_json = package_json,
45-
npm_deps = None,
46-
)
47-
48-
# Copy WIT file to workspace (needed for jco componentize)
49-
wit_dest = ctx.actions.declare_file(ctx.attr.name + "_component.wit")
50-
ctx.actions.symlink(
51-
output = wit_dest,
52-
target_file = wit_file,
53-
)
54-
55-
# Run jco to build component
56-
jco_args = ctx.actions.args()
57-
jco_args.add("componentize")
58-
jco_args.add(paths.join(work_dir.path, ctx.attr.entry_point))
59-
jco_args.add("--wit", wit_dest.path)
60-
jco_args.add("--out", component_wasm.path)
61-
39+
# Find the entry point file
40+
entry_point_file = None
41+
for src in source_files:
42+
if src.basename == ctx.attr.entry_point:
43+
entry_point_file = src
44+
break
45+
46+
if not entry_point_file:
47+
fail("Entry point '{}' not found in sources: {}".format(
48+
ctx.attr.entry_point, [src.basename for src in source_files]
49+
))
50+
51+
# Create a build script that sets up a workspace and runs jco with proper working directory
52+
build_script = ctx.actions.declare_file(ctx.attr.name + "_build.sh")
53+
54+
script_lines = [
55+
"#!/bin/bash",
56+
"set -euo pipefail",
57+
"",
58+
"# Create temporary workspace",
59+
"WORK_DIR=$(mktemp -d)",
60+
"echo \"Working in: $WORK_DIR\"",
61+
"",
62+
"# Copy all source files to workspace (flattened)",
63+
]
64+
65+
for src in source_files:
66+
script_lines.append("cp \"{}\" \"$WORK_DIR/{}\"".format(src.path, src.basename))
67+
68+
if package_json:
69+
script_lines.append("cp \"{}\" \"$WORK_DIR/package.json\"".format(package_json.path))
70+
71+
# Build jco command - change to workspace and use relative paths for module resolution
72+
script_lines.extend([
73+
"",
74+
"# Save original directory for absolute paths",
75+
"ORIGINAL_DIR=\"$(pwd)\"",
76+
"",
77+
"# Change to workspace directory so jco resolves modules correctly",
78+
"cd \"$WORK_DIR\"",
79+
"",
80+
"# Debug: show current directory and files",
81+
"echo \"Current directory: $(pwd)\"",
82+
"echo \"Original directory: $ORIGINAL_DIR\"",
83+
"echo \"Files in workspace:\"",
84+
"ls -la",
85+
"echo \"JCO binary path: $ORIGINAL_DIR/{}\"".format(jco.path),
86+
"echo \"About to run jco...\"",
87+
"",
88+
"# Run jco componentize from workspace with correct module resolution",
89+
])
90+
91+
# Build jco command - use absolute path for entry point for proper module resolution
92+
jco_cmd_parts = [
93+
"\"$ORIGINAL_DIR/{}\"".format(jco.path), # jco binary path
94+
"componentize",
95+
"\"$WORK_DIR/{}\"".format(ctx.attr.entry_point), # Absolute path to entry point in workspace
96+
"--wit \"$ORIGINAL_DIR/{}\"".format(wit_file.path), # Absolute path to WIT file
97+
"--out \"$ORIGINAL_DIR/{}\"".format(component_wasm.path), # Absolute path to output
98+
]
99+
62100
if ctx.attr.world:
63-
jco_args.add("--world-name", ctx.attr.world)
64-
65-
# Add jco-specific flags
101+
jco_cmd_parts.append("--world-name {}".format(ctx.attr.world))
102+
66103
if ctx.attr.disable_feature_detection:
67-
jco_args.add("--disable-feature-detection")
68-
104+
jco_cmd_parts.append("--disable-feature-detection")
105+
69106
if ctx.attr.compat:
70-
jco_args.add("--compat")
71-
72-
# Debug the issue by adding debugging
73-
debug_script = ctx.actions.declare_file(ctx.attr.name + "_debug.sh")
107+
jco_cmd_parts.append("--compat")
108+
109+
script_lines.extend([
110+
" ".join(jco_cmd_parts),
111+
"",
112+
"echo \"Component build complete\"",
113+
])
114+
74115
ctx.actions.write(
75-
output = debug_script,
76-
content = """#!/bin/bash
77-
echo "=== JCO DEBUG INFO ==="
78-
echo "Entry point file: {}"
79-
echo "File exists check:"
80-
ls -la "{}"
81-
echo "Working directory contents:"
82-
ls -la "{}"
83-
echo "Running jco with: $@"
84-
exec "$@"
85-
""".format(
86-
paths.join(work_dir.path, ctx.attr.entry_point),
87-
paths.join(work_dir.path, ctx.attr.entry_point),
88-
work_dir.path
89-
),
116+
output = build_script,
117+
content = "\n".join(script_lines),
90118
is_executable = True,
91119
)
92-
120+
121+
# All input files
122+
all_inputs = list(source_files) + [wit_file]
123+
if package_json:
124+
all_inputs.append(package_json)
125+
93126
ctx.actions.run(
94-
executable = debug_script,
95-
arguments = [jco.path] + [jco_args],
96-
inputs = [work_dir, wit_dest],
127+
executable = build_script,
128+
inputs = all_inputs,
97129
outputs = [component_wasm],
98130
tools = [jco],
99131
mnemonic = "JCOBuild",

toolchains/jco_toolchain.bzl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,6 @@ def _setup_downloaded_jco_tools(repository_ctx, platform, jco_version, node_vers
251251

252252
if platform.startswith("windows"):
253253
wrapper_content = """@echo off
254-
cd /d "{workspace}"
255254
set NODE_PATH={workspace}/node_modules
256255
"{node}" "{workspace}/node_modules/@bytecodealliance/jco/src/jco.js" %*
257256
""".format(
@@ -262,7 +261,6 @@ set NODE_PATH={workspace}/node_modules
262261
repository_ctx.symlink("jco.cmd", "jco")
263262
else:
264263
wrapper_content = """#!/bin/bash
265-
cd "{workspace}"
266264
export NODE_PATH="{workspace}/node_modules"
267265
exec "{node}" "{workspace}/node_modules/@bytecodealliance/jco/src/jco.js" "$@"
268266
""".format(

0 commit comments

Comments
 (0)