Skip to content
Merged
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
2 changes: 0 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"package-compass": "npm run package-compass --workspace=mongodb-compass --",
"package-compass-debug": "npm run package-compass-debug --workspace=mongodb-compass --",
"package-compass-nocompile": "npm run package-compass-nocompile --workspace=mongodb-compass --",
"start": "npm run start --workspace=mongodb-compass",
"start": "node --experimental-strip-types --disable-warning=ExperimentalWarning scripts/start.mts",
"start-web": "npm run start --workspace=@mongodb-js/compass-web",
"test": "lerna run test --concurrency 1 --stream",
"test-changed": "lerna run test --stream --concurrency 1 --since origin/HEAD",
Expand Down
3 changes: 1 addition & 2 deletions packages/compass-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"start": "electron ./scripts/electron-proxy.js",
"analyze": "npm run webpack -- --mode production --analyze",
"watch": "npm run webpack -- --mode development --watch",
"sync": "node scripts/sync-dist-to-mms.js",
"sync": "node --experimental-strip-types --disable-warning=ExperimentalWarning scripts/sync-dist-to-mms.mts",
"typecheck": "tsc -p tsconfig.json --noEmit",
"eslint": "eslint-compass",
"prettier": "prettier-compass",
Expand Down Expand Up @@ -127,7 +127,6 @@
"express": "^4.21.1",
"express-http-proxy": "^2.0.0",
"is-ip": "^5.0.1",
"lodash": "^4.17.21",
"mocha": "^10.2.0",
"mongodb": "^6.19.0",
"mongodb-build-info": "^1.7.2",
Expand Down
102 changes: 0 additions & 102 deletions packages/compass-web/scripts/sync-dist-to-mms.js

This file was deleted.

184 changes: 184 additions & 0 deletions packages/compass-web/scripts/sync-dist-to-mms.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import process from 'node:process';
import fs, { promises as asyncFs } from 'node:fs';
import path from 'node:path';
import child_process from 'node:child_process';
import os from 'node:os';
import util from 'node:util';
import timers from 'node:timers/promises';

if (!process.env.MMS_HOME) {
throw new Error(
'Missing required environment variable $MMS_HOME. Make sure you finished the "Cloud Developer Setup" process'
);
}

// Set up early signal handling and cleanup
let devServer: child_process.ChildProcess | undefined;
let distWatcher: fs.FSWatcher | undefined;
let webpackWatchProcess: child_process.ChildProcess | undefined;

const tmpDir = path.join(
os.tmpdir(),
`mongodb-js--compass-web-${Date.now().toString(36)}`
);
const srcDir = path.resolve(import.meta.dirname, '..', 'dist');
const destDir = path.dirname(
child_process.execFileSync(
process.execPath,
['-e', "console.log(require.resolve('@mongodb-js/compass-web'))"],
{ cwd: process.env.MMS_HOME, encoding: 'utf-8' }
)
);

fs.mkdirSync(srcDir, { recursive: true });
// Create a copy of current dist that will be overridden by link, we'll restore
// it when we are done
fs.mkdirSync(tmpDir, { recursive: true });
fs.cpSync(destDir, tmpDir, { recursive: true });

const failProofRunner = () =>
new (class FailProofRunner extends Array {
append(...fns: any[]) {
this.push(...fns);
return this;
}

run() {
const errors = this.map((f) => {
try {
f();
} catch (e) {
return e;
}
}).filter((e) => e);

if (errors.length) {
fs.writeSync(
process.stdout.fd,
util.inspect(errors, { depth: 20 }) + '\n'
);
}

return errors.length;
}
})();

function cleanup(signalName: NodeJS.Signals): void {
console.log(`\nReceived ${signalName}, cleaning up...`);
const errorCount = failProofRunner()
.append(() => distWatcher?.close())
.append(() => webpackWatchProcess?.kill(signalName))
.append(() => devServer?.kill(signalName))
.append(() => fs.cpSync(tmpDir, destDir, { recursive: true }))
.append(() => fs.rmSync(tmpDir, { recursive: true, force: true }))
.run();
fs.writeSync(process.stdout.fd, 'Exit compass-web sync...\n');
process.exit(errorCount);
}

// Set up signal handlers immediately
process.on('SIGINT', () => cleanup('SIGINT'));
process.on('SIGTERM', () => cleanup('SIGTERM'));

async function isDevServerRunning(
port: number,
host: string = '127.0.0.1'
): Promise<boolean> {
try {
return (
await fetch(`http://${host}:${port}`, {
method: 'HEAD',
signal: AbortSignal.timeout(3000),
})
).ok;
} catch (error) {
return false;
}
}

if (!(await isDevServerRunning(8081))) {
console.log('mms dev server is not running... launching!');

const { engines } = JSON.parse(
await asyncFs.readFile(
path.join(process.env.MMS_HOME, 'package.json'),
'utf8'
)
);
const pnpmVersion = engines.pnpm ?? 'latest';

const halfRamMb = Math.min(
Math.floor(os.totalmem() / 2 / 1024 / 1024),
16384
);
// Merge with existing NODE_OPTIONS if present
const existingNodeOptions = process.env.NODE_OPTIONS ?? '';
const mergedNodeOptions = [
`--max_old_space_size=${halfRamMb}`,
existingNodeOptions,
]
.filter(Boolean)
.join(' ');

devServer = child_process.spawn(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're not adding some sort of explicit way to disable strict engine check here, would be nice to monitor for the error and print something along the lines that the check can be disabled with NPM_CONFIG_ENGINE_STRICT=false flag

'npx',
[`pnpm@${pnpmVersion}`, 'run', 'start'],
{
cwd: process.env.MMS_HOME,
env: {
...process.env,
NODE_OPTIONS: mergedNodeOptions,
npm_config_engine_strict: `${false}`,
},
stdio: 'inherit',
}
);

// Wait for dev server to be ready before proceeding
console.log('Waiting for dev server to start...');
let retries = 30; // 30 seconds max
while (retries > 0 && !(await isDevServerRunning(8081))) {
await timers.setTimeout(1000);
retries--;
}

if (retries === 0) {
console.warn('Dev server may not be fully ready, proceeding anyway...');
} else {
console.log('Dev server is ready!');
}
} else {
console.log('Skipping running MMS dev server...');
}

let oneSec: Promise<void> | null = null;
let pendingCopy = false;

async function copyDist(): Promise<void> {
// If a copy is already in progress, mark that we need another copy
if (oneSec) {
pendingCopy = true;
return;
}
// Keep copying until there are no more pending requests
do {
pendingCopy = false;
fs.cpSync(srcDir, destDir, { recursive: true });
oneSec = timers.setTimeout(1000);
await oneSec;
} while (pendingCopy);

oneSec = null;
}

// The existing approach of using `npm / pnpm link` commands doesn't play well
// with webpack that will start to resolve other modules relative to the imports
// from compass-web inevitably causing some modules to resolve from the compass
// monorepo instead of mms one. To work around that we are just watching for any
// file changes in the dist folder and copying them as-is to whatever place
// compass-web was installed in mms node_modules
distWatcher = fs.watch(srcDir, () => void copyDist());

webpackWatchProcess = child_process.spawn('npm', ['run', 'watch'], {
stdio: 'inherit',
});
32 changes: 32 additions & 0 deletions scripts/start.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Start Script Help

Usage: start.mts [-h/--help] [targets... [targetOptions...]]

## Options

- `-h, --help` Show this help message

## Targets

- `desktop` Start MongoDB Compass Desktop (default if no targets specified)
- `sandbox` Start MongoDB Compass Web Sandbox, useful for UI-only changes
- `sync` Start Cloud Sync, in combination with redirector/redwood can be used to test data explorer changes
- `--no-mms` can be passed to the sync subcommand to not run MMS's dev server.

**Note:** `sandbox` must be run alone and cannot be combined with other targets.

## Port Configuration

The `desktop` and `sandbox` targets both use the same webpack dev server port (4242) by design. When running both targets simultaneously, the sandbox will automatically detect that the desktop target's webpack dev server is already running and will skip starting its own, sharing the same webpack dev server instance.

Since `sync` must run alone, there are no port conflicts with other targets.

### Well-Known Ports

| Port | Service | Target | Description |
| ---- | ---------------------- | -------------------- | ----------------------------------------------- |
| 4242 | Webpack Dev Server | `desktop`, `sandbox` | Serves compiled frontend assets with hot reload |
| 7777 | HTTP Proxy Server | `sandbox` | Express proxy server for Atlas API requests |
| 1337 | WebSocket Proxy Server | `sandbox` | WebSocket proxy for MongoDB connections |
| 8080 | Atlas Local Backend | `sync` | Local MMS backend server (when MMS_HOME is set) |
| 8081 | MMS Dev Server | `sync` | Local MMS development server |
Loading
Loading