Skip to content

Commit 43c2b3a

Browse files
authored
Phase 2: convert lib/, index.js and tests to native ES modules (#1530)
**Overview:** 189 files changed, +957 / −1162. Converts `lib/`, `index.js`, `bin/`, `rosocket/` and the full test suite to native ES modules; keeps build/codegen tooling as `.cjs`; updates CI to ROS 2 Lyrical (Ubuntu 26.04); and fixes a native-addon debug-build compile error. ### Core ESM conversion - **`index.js`** — converted to ESM entrypoint (`import`/`export`), module wiring reworked. - **`lib/**`** — all library modules converted from `require`/`module.exports` to `import`/`export` (node, client, service, publisher, subscription, action/*, clock*, parameter*, logging*, runtime/*, serialization, time*, timer, qos*, utils, validator, native_loader, etc.). - **`bin/rclnodejs-web.js`**, **`rosocket/index.js`**, **`rosocket/cli.js`** — converted to ESM. ### ESM/CJS boundary fixes (from Copilot review) - **`lib/native_loader.js`** — load the CommonJS `bindings` helper via `createRequire`'s `require('bindings')` instead of `import bindings from 'bindings'`, preserving CJS caller context for addon resolution. - **`lib/type_description_service.js`** — corrected sibling import from `'../lib/parameter.js'` to `'./parameter.js'`. ### CommonJS files kept as `.cjs` - **`rosidl_gen/*.cjs`** (deallocator, idl_generator, index, primitive_types, templates/message-template), **`rostsd_gen/index.cjs`**, **`scripts/ros_distro.cjs`**, **`scripts/run_test.cjs`** — build/codegen tooling stays CJS (loads ESM helpers via supported `require(esm)` / `.default`). - **`third_party/ref-napi/`** — `lib/ref.js` and `package.json` tweaks for module resolution. ### Tooling / config - **`package.json`** — `"type": "module"` and script adjustments. - **`eslint.config.mjs`** — ESM lint rules. - **`.c8rc.json`** added, **`.nycrc.yml`** removed — switch coverage from nyc to c8. ### CI - **`.github/workflows/linux-x64-asan-test.yml`**, **`.github/workflows/linux-x64-build-and-test.yml`** — migrated from ROS 2 Jazzy to Lyrical (Ubuntu 26.04). - **`.github/actions/setup-ros2-apt/action.yml`** (new) — composite action to install Lyrical from apt (setup-ros doesn't support it yet). ### Native addon fix - **`src/macros.h`** — replaced removed internal rcutils macros (`RCUTILS_LOG_COND_NAMED` / `RCUTILS_LOG_CONDITION_EMPTY`) in `RCLNODEJS_DEBUG` with the stable public `RCUTILS_LOG_DEBUG_NAMED`, fixing the ASan debug-build compile failure under Lyrical. ### Tests - All `test/test-*.js` converted to ESM; `tutorials/.../test_usability.js` renamed to `.cjs`; `test/utils.js` updated. Fix: #1358
1 parent 9488c15 commit 43c2b3a

189 files changed

Lines changed: 956 additions & 1162 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.c8rc.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extension": [".js"],
3+
"include": ["lib/**", "index.js"],
4+
"reporter": ["lcov"],
5+
"report-dir": "./coverage"
6+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: 'Setup ROS2 apt repository'
2+
description: >-
3+
Enable the official ros-apt-source repository (and common build deps) on
4+
Ubuntu, and optionally install the Lyrical Luth desktop. Shared by the
5+
build-and-test and ASan workflows so the lyrical/rolling apt setup lives in
6+
one place.
7+
8+
inputs:
9+
install-desktop:
10+
description: >-
11+
When 'true' (default) install ros-lyrical-desktop. Set to 'false' to only
12+
enable the apt repository and build deps (e.g. the rolling lane installs
13+
ROS2 from a binary tarball after this action runs).
14+
required: false
15+
default: 'true'
16+
17+
runs:
18+
using: 'composite'
19+
steps:
20+
- name: Enable ROS2 apt repository
21+
shell: bash
22+
run: |
23+
apt-get update
24+
apt-get install -y sudo software-properties-common curl
25+
26+
# Enable required repositories. The same setup applies to both the
27+
# rolling nightly tarball and the lyrical apt-deb install; see the
28+
# per-distro install docs for reference:
29+
# rolling: https://docs.ros.org/en/rolling/Installation/Alternatives/Ubuntu-Install-Binary.html
30+
# lyrical: https://docs.ros.org/en/lyrical/Installation/Ubuntu-Install-Debs.html
31+
add-apt-repository universe
32+
ROS_APT_SOURCE_VERSION=$(curl -s https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest | grep -F "tag_name" | awk -F'"' '{print $4}')
33+
curl -L -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo ${UBUNTU_CODENAME:-${VERSION_CODENAME}})_all.deb"
34+
dpkg -i /tmp/ros2-apt-source.deb
35+
apt-get update
36+
# Common build deps for both rolling and lyrical. Rolling-only tarball
37+
# tooling (tar, bzip2, python3-rosdep) is installed by the caller so the
38+
# lyrical lane stays minimal.
39+
apt-get install -y build-essential cmake python3 python3-colcon-common-extensions
40+
41+
- name: Install ROS2 from apt (lyrical)
42+
if: ${{ inputs.install-desktop == 'true' }}
43+
shell: bash
44+
run: |
45+
# Per https://docs.ros.org/en/lyrical/Installation/Ubuntu-Install-Debs.html
46+
apt-get install -y ros-lyrical-desktop

.github/workflows/linux-x64-asan-test.yml

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,30 @@ jobs:
1717
asan:
1818
runs-on: ubuntu-latest
1919
container:
20-
image: ubuntu:noble
20+
# Lyrical Luth is the latest ROS2 LTS and targets Ubuntu 26.04.
21+
image: ubuntu:26.04
2122
steps:
2223
- name: Setup Node.js 24.X
2324
uses: actions/setup-node@v6
2425
with:
2526
node-version: 24.X
2627
architecture: x64
2728

28-
- name: Setup ROS2 Jazzy
29-
uses: ros-tooling/setup-ros@v0.7
30-
with:
31-
required-ros-distributions: jazzy
29+
- uses: actions/checkout@v6
30+
31+
- name: Setup ROS2 Lyrical from apt
32+
# ros-tooling/setup-ros does not support lyrical yet, so the shared
33+
# composite action installs it from the official apt repo. See
34+
# https://docs.ros.org/en/lyrical/Installation/Ubuntu-Install-Debs.html
35+
uses: ./.github/actions/setup-ros2-apt
3236

3337
- name: Install test dependencies
3438
run: |
35-
sudo apt install -y ros-jazzy-test-msgs ros-jazzy-mrpt-msgs
36-
37-
- uses: actions/checkout@v6
39+
sudo apt install -y ros-lyrical-test-msgs ros-lyrical-mrpt-msgs
3840
3941
- name: Install npm dependencies
4042
run: |
41-
source /opt/ros/jazzy/setup.bash
43+
source /opt/ros/lyrical/setup.bash
4244
npm i
4345
4446
# Wrap the asan test step in nick-fields/retry@v4 to absorb transient
@@ -52,5 +54,5 @@ jobs:
5254
retry_wait_seconds: 10
5355
timeout_minutes: 60
5456
command: |
55-
source /opt/ros/jazzy/setup.bash
57+
source /opt/ros/lyrical/setup.bash
5658
npm run test:asan

.github/workflows/linux-x64-build-and-test.yml

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ jobs:
5454
node-version: ${{ matrix.node-version }}
5555
architecture: ${{ matrix.architecture }}
5656

57+
# Checkout early so the local composite action under .github/actions is
58+
# available to the rolling / lyrical apt setup step below.
59+
- uses: actions/checkout@v6
60+
5761
- name: Setup ROS2
5862
if: ${{ matrix.ros_distribution != 'rolling' && matrix.ros_distribution != 'lyrical' }}
5963
uses: ros-tooling/setup-ros@v0.7
@@ -62,30 +66,11 @@ jobs:
6266

6367
- name: Enable ROS2 apt repository (rolling / lyrical)
6468
if: ${{ matrix.ros_distribution == 'rolling' || matrix.ros_distribution == 'lyrical' }}
65-
run: |
66-
apt-get update
67-
apt-get install -y sudo software-properties-common curl
68-
69-
# Enable required repositories. The same setup applies to both the
70-
# rolling nightly tarball and the lyrical apt-deb install; see the
71-
# per-distro install docs for reference:
72-
# rolling: https://docs.ros.org/en/rolling/Installation/Alternatives/Ubuntu-Install-Binary.html
73-
# lyrical: https://docs.ros.org/en/lyrical/Installation/Ubuntu-Install-Debs.html
74-
add-apt-repository universe
75-
ROS_APT_SOURCE_VERSION=$(curl -s https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest | grep -F "tag_name" | awk -F'"' '{print $4}')
76-
curl -L -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo ${UBUNTU_CODENAME:-${VERSION_CODENAME}})_all.deb"
77-
dpkg -i /tmp/ros2-apt-source.deb
78-
apt-get update
79-
# Common build deps for both rolling and lyrical. Rolling-only tarball
80-
# tooling (tar, bzip2, python3-rosdep) is installed in the rolling step
81-
# below so the lyrical lane stays minimal.
82-
apt-get install -y build-essential cmake python3 python3-colcon-common-extensions
83-
84-
- name: Install ROS2 from apt (lyrical)
85-
if: ${{ matrix.ros_distribution == 'lyrical' }}
86-
run: |
87-
# Per https://docs.ros.org/en/lyrical/Installation/Ubuntu-Install-Debs.html
88-
apt-get install -y ros-lyrical-desktop
69+
uses: ./.github/actions/setup-ros2-apt
70+
with:
71+
# Lyrical installs the desktop from apt here; rolling only needs the
72+
# repository enabled and installs ROS2 from a tarball in the next step.
73+
install-desktop: ${{ matrix.ros_distribution == 'lyrical' }}
8974

9075
- name: Install ROS2 from binary tarball (rolling)
9176
if: ${{ matrix.ros_distribution == 'rolling' }}
@@ -121,8 +106,6 @@ jobs:
121106
fi
122107
sudo apt install -y xvfb libgtk-3-0 libnss3 $LIBASOUND_PKG libgbm-dev
123108
124-
- uses: actions/checkout@v6
125-
126109
# The mocha suite and the Electron postinstall (extract-zip 2.0.1) are
127110
# known to be intermittently flaky on this runner. Wrap both test
128111
# invocations in nick-fields/retry@v4 so a single transient failure does
@@ -140,7 +123,15 @@ jobs:
140123
source /opt/ros/${{ matrix.ros_distribution }}/setup.bash
141124
npm i
142125
npm run lint
143-
npm test
126+
# c8 (coverage) depends on yargs 17, whose extensionless `yargs/yargs`
127+
# entry fails to load under `require()` on Node 26. Coverage is only
128+
# uploaded from the Node 24.X leg, so wrap with c8 there and run the
129+
# plain suite everywhere else to keep the Node 26 lane green.
130+
if [ "${{ matrix.node-version }}" = "24.X" ]; then
131+
npm run test:coverage
132+
else
133+
npm test
134+
fi
144135
npm run clean
145136
146137
- name: Test with IDL ROS messages (rolling / lyrical)

.nycrc.yml

Lines changed: 0 additions & 7 deletions
This file was deleted.

bin/rclnodejs-web.js

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,20 @@
1111
//
1212
// For frontend developers who don't want to write a Node.js server.
1313
// Run `rclnodejs-web --help` or `npx rclnodejs-web web.json` to start
14-
// the runtime. See demo/web/javascript/README.md for the full demo.
14+
// the runtime.
1515

16-
'use strict';
16+
import path from 'node:path';
17+
import fs from 'node:fs';
18+
import { fileURLToPath } from 'node:url';
1719

18-
const path = require('node:path');
19-
const fs = require('node:fs');
20-
21-
const {
20+
import {
2221
parseArgv,
2322
loadConfigFile,
2423
mergeConfig,
25-
CliError,
2624
HELP,
27-
} = require('../lib/runtime/cli-config.js');
25+
} from '../lib/runtime/cli-config.js';
26+
27+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
2828

2929
const argv = process.argv.slice(2);
3030

@@ -55,14 +55,11 @@ const argv = process.argv.slice(2);
5555
fail(e);
5656
}
5757

58-
// Defer the rclnodejs require until after argv parsing so --help / --version
58+
// Defer the rclnodejs import until after argv parsing so --help / --version
5959
// never pay the (slow, native) load cost.
60-
const rclnodejs = require('../index.js');
61-
const {
62-
createRuntime,
63-
WebSocketTransport,
64-
HttpTransport,
65-
} = require('../lib/runtime');
60+
const rclnodejs = (await import('../index.js')).default;
61+
const { createRuntime, WebSocketTransport, HttpTransport } =
62+
await import('../lib/runtime/index.js');
6663

6764
// Track partial init so the catch block can clean up native handles before
6865
// process.exit() — without this, a startup failure (e.g. EADDRINUSE on the

eslint.config.mjs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ export default [
1616
"**/benchmark/",
1717
"**/docs/",
1818
"**/demo/electron/",
19+
"**/coverage/",
20+
"**/dist/",
21+
"**/prebuilds/",
22+
"**/build/",
1923
],
2024
},
2125
{
@@ -46,6 +50,22 @@ export default [
4650
"@typescript-eslint/triple-slash-reference": "off",
4751
},
4852
},
53+
{
54+
plugins: {
55+
prettier,
56+
},
57+
languageOptions: {
58+
globals: {
59+
...globals.node,
60+
},
61+
ecmaVersion: "latest",
62+
sourceType: "module",
63+
},
64+
files: ["lib/**/*.js", "test/**/*.js", "bin/**/*.js", "rosocket/**/*.js", "index.js"],
65+
rules: {
66+
...eslintPluginPrettierRecommended.rules,
67+
},
68+
},
4969
{
5070
plugins: {
5171
prettier,
@@ -57,8 +77,8 @@ export default [
5777
ecmaVersion: "latest",
5878
sourceType: "commonjs",
5979
},
60-
files: ["lib/**/*.js", "rosidl_parser/**/*.{js,cjs}", "rosidl_gen/**/*.{js,cjs}",
61-
"rostsd_gen/**/*.{js,cjs}", "test/**/*.js", "example/**/*.js", "index.js"],
80+
files: ["rosidl_parser/**/*.{js,cjs}", "rosidl_gen/**/*.{js,cjs}",
81+
"rostsd_gen/**/*.{js,cjs}", "example/**/*.js"],
6282
rules: {
6383
...eslintPluginPrettierRecommended.rules,
6484
},

0 commit comments

Comments
 (0)