Skip to content

Commit a298a27

Browse files
ggazzotassoevan
andauthored
chore: Enable Meteor's *Modern Build Stack* (RocketChat#37614)
Co-authored-by: Tasso <tasso.evangelista@rocket.chat>
1 parent c7744b1 commit a298a27

File tree

12 files changed

+207
-24
lines changed

12 files changed

+207
-24
lines changed

.github/actions/meteor-build/action.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,16 @@ runs:
157157
# check if BABEL_ENV is set to coverage
158158
if [[ $BABEL_ENV == "coverage" ]]; then
159159
echo -e "rocketchat:coverage\n" >> ./apps/meteor/.meteor/packages
160-
echo "Coverage enabled"
160+
# Inject swc-plugin-coverage-instrument into .swcrc for istanbul-compatible coverage
161+
node -e "
162+
const fs = require('fs');
163+
const swcrc = JSON.parse(fs.readFileSync('./apps/meteor/.swcrc', 'utf8'));
164+
swcrc.jsc.experimental = swcrc.jsc.experimental || {};
165+
swcrc.jsc.experimental.plugins = swcrc.jsc.experimental.plugins || [];
166+
swcrc.jsc.experimental.plugins.push(['swc-plugin-coverage-instrument', {}]);
167+
fs.writeFileSync('./apps/meteor/.swcrc', JSON.stringify(swcrc, null, 2) + '\n');
168+
"
169+
echo "Coverage enabled (SWC istanbul plugin injected)"
161170
fi
162171
163172
# Restore original package.json so meteor should not copy devDependencies
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
diff --git a/lib/index.mjs b/lib/index.mjs
2+
index 35c18fc129e349205d3db64bfb7b9c589c726d35..7fe10a375d6e6d4d46bb1c4a776fdc75f062c3d3 100644
3+
--- a/lib/index.mjs
4+
+++ b/lib/index.mjs
5+
@@ -1,4 +1,4 @@
6+
-import * as module from './module.mjs';
7+
+import * as _module from './module.mjs';
8+
export { assert, assertEquals, assertGuard, assertGuardEquals, createAssert, createAssertEquals, createAssertGuard, createAssertGuardEquals, createEquals, createIs, createRandom, createValidate, createValidateEquals, equals, is, random, validate, validateEquals } from './module.mjs';
9+
import * as functional from './functional.mjs';
10+
export { functional };
11+
@@ -22,5 +22,5 @@ export { TypeGuardError } from './TypeGuardError.mjs';
12+
13+
14+
15+
-export { module as default };
16+
+export { _module as default };
17+
//# sourceMappingURL=index.mjs.map

apps/meteor/.babelrc

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
11
{
22
"presets": [
33
"@babel/preset-env",
4-
"@babel/preset-react",
4+
["@babel/preset-react", { "runtime": "automatic" }],
55
[
66
"@babel/preset-typescript",
77
{
88
"allowDeclareFields": true
99
}
1010
]
1111
],
12-
"plugins": [
13-
[
14-
"@babel/plugin-transform-react-jsx",
15-
{
16-
"runtime": "automatic"
17-
}
18-
]
19-
],
2012
"env": {
2113
"coverage": {
2214
"plugins": [

apps/meteor/.meteor/packages

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,4 @@ autoupdate@2.0.1
6060
# photoswipe
6161

6262
zodern:types
63-
zodern:standard-minifier-js
63+
standard-minifier-js

apps/meteor/.meteor/platforms

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
browser
21
server
2+
browser
3+
modern

apps/meteor/.meteor/versions

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ meteor-base@1.5.2
4646
meteor-developer-oauth@1.3.3
4747
meteorhacks:inject-initial@1.0.5
4848
minifier-css@2.0.1
49+
minifier-js@3.1.0
4950
minimongo@2.0.5
5051
modern-browsers@0.2.3
5152
modules@0.20.3
@@ -85,6 +86,5 @@ underscore@1.6.4
8586
url@1.3.5
8687
webapp@2.1.0
8788
webapp-hashing@1.1.2
88-
zodern:caching-minifier@0.5.0
89-
zodern:standard-minifier-js@5.3.1
89+
standard-minifier-js@3.2.0
9090
zodern:types@1.0.13

apps/meteor/.swcrc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"jsc": {
3+
"target": "es2022",
4+
"transform": {
5+
"react": {
6+
"runtime": "automatic"
7+
}
8+
}
9+
}
10+
}

apps/meteor/package.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@
6262
"version": "node .scripts/version.js"
6363
},
6464
"browserslist": [
65-
"last 2 versions",
66-
"Firefox ESR"
65+
"defaults"
6766
],
6867
"dependencies": {
6968
"@aws-sdk/client-s3": "^3.862.0",
@@ -291,14 +290,15 @@
291290
"strict-uri-encode": "^2.0.0",
292291
"string-strip-html": "^8.5.0",
293292
"swagger-ui-express": "^5.0.1",
293+
"swc-plugin-coverage-instrument": "0.0.32",
294294
"swiper": "patch:swiper@npm%3A11.1.14#~/.yarn/patches/swiper-npm-11.1.14-8126fa478a.patch",
295295
"textarea-caret": "^3.1.0",
296296
"tinykeys": "^1.4.0",
297297
"tsyringe": "^4.10.0",
298298
"tweetnacl": "^1.0.3",
299299
"twilio": "^5.4.2",
300300
"twit": "^2.2.11",
301-
"typia": "~9.7.2",
301+
"typia": "patch:typia@npm%3A9.7.2#~/.yarn/patches/typia-npm-9.7.2-5c5d9c80b4.patch",
302302
"ua-parser-js": "~1.0.41",
303303
"underscore": "^1.13.7",
304304
"universal-perf-hooks": "^1.0.1",
@@ -459,10 +459,12 @@
459459
"hoistingLimits": "workspaces"
460460
},
461461
"meteor": {
462+
"disableLegacyBuild": true,
462463
"mainModule": {
463464
"client": "client/main.ts",
464465
"server": "server/main.ts"
465-
}
466+
},
467+
"modern": true
466468
},
467469
"rocketchat": {
468470
"minimumClientVersions": {

docs/coverage.md

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Code Coverage
2+
3+
This document explains how code coverage instrumentation works in Rocket.Chat's build and CI pipeline.
4+
5+
## Overview
6+
7+
Coverage is collected during E2E test runs (API, UI, Livechat) to measure how much of the server-side code is exercised by tests. The instrumentation uses [Istanbul](https://istanbul.js.org/)-compatible tooling, which injects counters (`__coverage__`) into the compiled code at build time.
8+
9+
## Architecture
10+
11+
The coverage pipeline has three components:
12+
13+
1. **Build-time instrumentation** - injects coverage counters into the code during Meteor build
14+
2. **Runtime collection** - the `rocketchat:coverage` Meteor package collects `__coverage__` data on process exit
15+
3. **CI reporting** - test workflows merge coverage data and upload reports
16+
17+
```
18+
Build (SWC + plugin) --> Run tests --> Process exit triggers report --> Merge & upload
19+
```
20+
21+
## Build-time instrumentation
22+
23+
### Modern build stack (SWC)
24+
25+
Rocket.Chat uses Meteor's modern build stack with [SWC](https://swc.rs/) as the transpiler. For coverage builds, the [`swc-plugin-coverage-instrument`](https://github.com/kwonoj/swc-plugin-coverage-instrument) plugin is injected into `.swcrc` at build time.
26+
27+
This is configured in `.github/actions/meteor-build/action.yml`:
28+
29+
```yaml
30+
env:
31+
BABEL_ENV: ${{ inputs.type }} # "production" or "coverage"
32+
```
33+
34+
When `BABEL_ENV=coverage`, the build script:
35+
36+
1. Adds `rocketchat:coverage` to `.meteor/packages`
37+
2. Injects `swc-plugin-coverage-instrument` into `.swcrc` via a node script:
38+
39+
```js
40+
const swcrc = JSON.parse(fs.readFileSync('./apps/meteor/.swcrc', 'utf8'));
41+
swcrc.jsc.experimental = swcrc.jsc.experimental || {};
42+
swcrc.jsc.experimental.plugins = swcrc.jsc.experimental.plugins || [];
43+
swcrc.jsc.experimental.plugins.push(['swc-plugin-coverage-instrument', {}]);
44+
fs.writeFileSync('./apps/meteor/.swcrc', JSON.stringify(swcrc, null, 2) + '\n');
45+
```
46+
47+
This approach ensures the same build pipeline (SWC) is used for both regular and coverage builds, avoiding behavioral differences between build modes.
48+
49+
### Legacy build stack (Babel)
50+
51+
Before the modern build stack, coverage was handled via `babel-plugin-istanbul` configured in `.babelrc`:
52+
53+
```json
54+
{
55+
"env": {
56+
"coverage": {
57+
"plugins": [
58+
["istanbul", { "exclude": ["**/*.spec.js", "**/*.test.js"] }]
59+
]
60+
}
61+
}
62+
}
63+
```
64+
65+
This section is still present in `.babelrc` as a fallback for files that fall back to Babel compilation (e.g., SWC-incompatible code).
66+
67+
## Runtime collection: `rocketchat:coverage`
68+
69+
The `rocketchat:coverage` Meteor package (`apps/meteor/packages/rocketchat-coverage/`) is only added to the build during coverage runs. It:
70+
71+
1. Registers a `process.on('exit')` handler
72+
2. Reads `globalThis['__coverage__']` (populated by the instrumentation)
73+
3. Generates a coverage report using `istanbul-lib-coverage` and `istanbul-reports`
74+
75+
Configuration via environment variables:
76+
77+
| Variable | Description | Example |
78+
|---|---|---|
79+
| `COVERAGE_DIR` | Output directory for reports | `/tmp/coverage/api` |
80+
| `COVERAGE_FILE_NAME` | Report filename | `api-1.json` |
81+
| `COVERAGE_REPORTER` | Istanbul reporter format | `json`, `lcov` |
82+
83+
## CI workflow
84+
85+
Coverage is collected in the `ci-test-e2e.yml` workflow:
86+
87+
1. **Build**: `meteor-build` action runs with `type: coverage`, producing a Docker image with instrumented code
88+
2. **Test**: E2E tests run against the instrumented server. On each test shard:
89+
- `COVERAGE_DIR`, `COVERAGE_FILE_NAME`, and `COVERAGE_REPORTER` are set
90+
- When the Rocket.Chat process exits after tests, the coverage plugin writes a JSON report
91+
3. **Merge**: `nyc merge` combines per-shard JSON reports into a single coverage file
92+
4. **Upload**: Coverage data is uploaded to Codecov
93+
94+
## Local development
95+
96+
To build with coverage locally:
97+
98+
```bash
99+
cd apps/meteor
100+
101+
# Inject the SWC coverage plugin into .swcrc
102+
node -e "
103+
const fs = require('fs');
104+
const swcrc = JSON.parse(fs.readFileSync('.swcrc', 'utf8'));
105+
swcrc.jsc.experimental = { plugins: [['swc-plugin-coverage-instrument', {}]] };
106+
fs.writeFileSync('.swcrc', JSON.stringify(swcrc, null, 2));
107+
"
108+
109+
# Add the coverage package
110+
echo -e "rocketchat:coverage\n" >> .meteor/packages
111+
112+
# Set env vars and run
113+
COVERAGE_DIR=/tmp/coverage COVERAGE_FILE_NAME=local.json COVERAGE_REPORTER=lcov yarn dev
114+
```
115+
116+
Remember to restore `.swcrc` and `.meteor/packages` after testing.
117+
118+
## Dependencies
119+
120+
| Package | Purpose |
121+
|---|---|
122+
| `swc-plugin-coverage-instrument` | SWC plugin for Istanbul-compatible instrumentation |
123+
| `istanbul-lib-coverage` | Coverage map creation (used by `rocketchat:coverage`) |
124+
| `istanbul-lib-report` | Report context creation |
125+
| `istanbul-reports` | Report formatters (json, lcov, etc.) |

packages/core-typings/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"@rocket.chat/icons": "~0.47.0",
2222
"@rocket.chat/message-parser": "workspace:^",
2323
"@rocket.chat/ui-kit": "workspace:~",
24-
"typia": "~9.7.2",
24+
"typia": "patch:typia@npm%3A9.7.2#~/.yarn/patches/typia-npm-9.7.2-5c5d9c80b4.patch",
2525
"zod": "~4.3.6"
2626
},
2727
"devDependencies": {

0 commit comments

Comments
 (0)