Skip to content

Commit cf1df8c

Browse files
committed
chore: enhance npm start command to run compass sync and sandbox COMPASS-9851
1 parent 9c656bb commit cf1df8c

File tree

6 files changed

+367
-104
lines changed

6 files changed

+367
-104
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"package-compass": "npm run package-compass --workspace=mongodb-compass --",
3333
"package-compass-debug": "npm run package-compass-debug --workspace=mongodb-compass --",
3434
"package-compass-nocompile": "npm run package-compass-nocompile --workspace=mongodb-compass --",
35-
"start": "npm run start --workspace=mongodb-compass",
35+
"start": "node --experimental-strip-types --disable-warning=ExperimentalWarning scripts/start.mts",
3636
"start-web": "npm run start --workspace=@mongodb-js/compass-web",
3737
"test": "lerna run test --concurrency 1 --stream",
3838
"test-changed": "lerna run test --stream --concurrency 1 --since origin/HEAD",

packages/compass-web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
"start": "electron ./scripts/electron-proxy.js",
5050
"analyze": "npm run webpack -- --mode production --analyze",
5151
"watch": "npm run webpack -- --mode development --watch",
52-
"sync": "node scripts/sync-dist-to-mms.js",
52+
"sync": "node scripts/sync-dist-to-mms.mjs",
5353
"typecheck": "tsc -p tsconfig-lint.json --noEmit",
5454
"eslint": "eslint-compass",
5555
"prettier": "prettier-compass",

packages/compass-web/scripts/sync-dist-to-mms.js

Lines changed: 0 additions & 102 deletions
This file was deleted.
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import process from 'node:process';
2+
import fs from 'node:fs';
3+
import path from 'node:path';
4+
import child_process from 'node:child_process';
5+
import os from 'node:os';
6+
import util from 'node:util';
7+
import net from 'node:net';
8+
import timers from 'node:timers/promises';
9+
10+
if (!process.env.MMS_HOME) {
11+
throw new Error(
12+
'Missing required environment variable $MMS_HOME. Make sure you finished the "Cloud Developer Setup" process'
13+
);
14+
}
15+
16+
function isDevServerRunning(port, host = '127.0.0.1') {
17+
return new Promise((resolve) => {
18+
const socket = new net.Socket();
19+
socket.setTimeout(1000);
20+
socket.once('connect', () => {
21+
socket.destroy();
22+
resolve(true);
23+
});
24+
socket.once('timeout', () => {
25+
socket.destroy();
26+
resolve(false);
27+
});
28+
socket.once('error', () => {
29+
resolve(false);
30+
});
31+
socket.connect(port, host);
32+
});
33+
}
34+
35+
let devServer;
36+
if (!(await isDevServerRunning(8081))) {
37+
console.log('mms dev server is not running... launching!');
38+
child_process.execFileSync('pnpm', ['install'], {
39+
cwd: process.env.MMS_HOME,
40+
stdio: 'inherit',
41+
});
42+
child_process.execFileSync('pnpm', ['run', 'init'], {
43+
cwd: process.env.MMS_HOME,
44+
stdio: 'inherit',
45+
});
46+
const halfRamMb = Math.min(
47+
Math.floor(os.totalmem() / 2 / 1024 / 1024),
48+
16384
49+
);
50+
// Merge with existing NODE_OPTIONS if present
51+
const existingNodeOptions = process.env.NODE_OPTIONS ?? '';
52+
const mergedNodeOptions = [
53+
`--max_old_space_size=${halfRamMb}`,
54+
existingNodeOptions,
55+
]
56+
.filter(Boolean)
57+
.join(' ');
58+
59+
devServer = child_process.spawn('pnpm', ['run', 'start'], {
60+
cwd: process.env.MMS_HOME,
61+
env: {
62+
...process.env,
63+
NODE_OPTIONS: mergedNodeOptions,
64+
},
65+
stdio: 'inherit',
66+
});
67+
68+
// Wait for dev server to be ready before proceeding
69+
console.log('Waiting for dev server to start...');
70+
let retries = 30; // 30 seconds max
71+
while (retries > 0 && !(await isDevServerRunning(8081))) {
72+
await timers.setTimeout(1000);
73+
retries--;
74+
}
75+
76+
if (retries === 0) {
77+
console.warn('Dev server may not be fully ready, proceeding anyway...');
78+
} else {
79+
console.log('Dev server is ready!');
80+
}
81+
}
82+
83+
const srcDir = path.resolve(import.meta.dirname, '..', 'dist');
84+
85+
const destDir = path.dirname(
86+
child_process.execFileSync(
87+
'node',
88+
['-e', "console.log(require.resolve('@mongodb-js/compass-web'))"],
89+
{ cwd: process.env.MMS_HOME, encoding: 'utf-8' }
90+
)
91+
);
92+
93+
const tmpDir = path.join(
94+
os.tmpdir(),
95+
`mongodb-js--compass-web-${Date.now().toString(36)}`
96+
);
97+
98+
fs.mkdirSync(srcDir, { recursive: true });
99+
100+
// Create a copy of current dist that will be overriden by link, we'll restore
101+
// it when we are done
102+
fs.mkdirSync(tmpDir, { recursive: true });
103+
fs.cpSync(destDir, tmpDir, { recursive: true });
104+
105+
let oneSec = null;
106+
async function copyDist() {
107+
// If a copy is already in progress, return early (debounce)
108+
if (oneSec) return;
109+
fs.cpSync(srcDir, destDir, { recursive: true });
110+
oneSec = timers.setTimeout(1000);
111+
await oneSec;
112+
oneSec = null;
113+
}
114+
115+
// The existing approach of using `npm / pnpm link` commands doesn't play well
116+
// with webpack that will start to resolve other modules relative to the imports
117+
// from compass-web inevitably causing some modules to resolve from the compass
118+
// monorepo instead of mms one. To work around that we are just watching for any
119+
// file changes in the dist folder and copying them as-is to whatever place
120+
// compass-web was installed in mms node_modules
121+
const distWatcher = fs.watch(srcDir, () => void copyDist());
122+
123+
const webpackWatchProcess = child_process.spawn('npm', ['run', 'watch'], {
124+
stdio: 'inherit',
125+
});
126+
127+
const failProofRunner = () =>
128+
new (class FailProofRunner extends Array {
129+
append(...fns) {
130+
this.push(...fns);
131+
return this;
132+
}
133+
134+
run() {
135+
const errors = this.map((f) => {
136+
try {
137+
f();
138+
} catch (e) {
139+
return e;
140+
}
141+
}).filter((e) => e);
142+
143+
if (errors.length) {
144+
fs.writeSync(
145+
process.stdout.fd,
146+
util.inspect(errors, { depth: 20 }) + '\n'
147+
);
148+
}
149+
150+
return errors.length;
151+
}
152+
})();
153+
154+
function cleanup(signalName) {
155+
const errorCount = failProofRunner()
156+
.append(() => distWatcher.close())
157+
.append(() => webpackWatchProcess.kill(signalName))
158+
.append(() => devServer?.kill(signalName))
159+
.append(() => fs.cpSync(tmpDir, destDir, { recursive: true }))
160+
.append(() => fs.rmSync(tmpDir, { recursive: true, force: true }))
161+
.run();
162+
fs.writeSync(process.stdout.fd, 'Exit compass-web sync...\n');
163+
process.exit(errorCount);
164+
}
165+
166+
process.on('SIGINT', cleanup).on('SIGTERM', cleanup);

scripts/start.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Start Script Help
2+
3+
Usage: start.mts [-h/--help] [targets... [targetOptions...]]
4+
5+
## Options
6+
7+
- `-h, --help` Show this help message
8+
9+
## Targets (can be used in any combination)
10+
11+
- `desktop` Start MongoDB Compass Desktop (default if no targets specified)
12+
- `sandbox` Start MongoDB Compass Web Sandbox, useful for UI-only changes
13+
- `sync` Start Cloud Sync, in combination with redirector/redwood can be used to test data explorer changes
14+
15+
## Examples
16+
17+
start.mts # Start desktop (default)
18+
start.mts desktop --production # Start desktop explicitly
19+
start.mts web # Start web sandbox only
20+
start.mts desktop web # Start both desktop and web
21+
start.mts sync --flagA -b web -c # Start mms-sync and web

0 commit comments

Comments
 (0)