feat: added support for specifying the HMR port for CLI start command#2947
Conversation
🦋 Changeset detectedLatest commit: 0018e84 The changes in this PR will be included in the next version bump. This PR includes changesets to release 22 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📝 WalkthroughWalkthroughThis PR adds explicit HMR WebSocket port handling: a new ChangesHMR Port Configuration
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI
participant Handler
participant resolveHmrPort
participant Vite
User->>CLI: run start --hmr-port / omit
CLI->>Handler: pass hmrPort (or undefined)
Handler->>resolveHmrPort: resolve(hmrPort, hmrEnabled)
resolveHmrPort-->>Handler: resolvedHmrPort
Handler->>Vite: createServer({ server.hmr.port: resolvedHmrPort? })
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/likec4/src/vite/vite-dev.ts (1)
31-31: ⚡ Quick winPrefer
Number.parseIntover globalparseInt.Modern JavaScript best practice is to use
Number.parseIntfor better clarity and to avoid potential global namespace pollution.♻️ Proposed fix
- const port = explicitPort ?? (env['HMR_PORT'] ? parseInt(env['HMR_PORT'], 10) : undefined) + const port = explicitPort ?? (env['HMR_PORT'] ? Number.parseInt(env['HMR_PORT'], 10) : undefined)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/likec4/src/vite/vite-dev.ts` at line 31, Replace the use of the global parseInt when deriving port so it uses Number.parseInt for clarity and to avoid global namespace usage; specifically update the expression assigning port (which checks explicitPort and env['HMR_PORT']) to call Number.parseInt(env['HMR_PORT'], 10) instead of parseInt, keeping the same fallback/undefined behavior for explicitPort and missing HMR_PORT.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/likec4/src/vite/vite-dev.ts`:
- Line 31: Replace the use of the global parseInt when deriving port so it uses
Number.parseInt for clarity and to avoid global namespace usage; specifically
update the expression assigning port (which checks explicitPort and
env['HMR_PORT']) to call Number.parseInt(env['HMR_PORT'], 10) instead of
parseInt, keeping the same fallback/undefined behavior for explicitPort and
missing HMR_PORT.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 8c697e6c-a5df-406f-bda5-63e6b2129e24
📒 Files selected for processing (9)
.changeset/specify-hmr-port.mdapps/docs/src/content/docs/tooling/cli.mdxapps/docs/src/content/docs/tooling/docker.mdxpackages/likec4/src/cli/options.tspackages/likec4/src/cli/serve/index.tspackages/likec4/src/cli/serve/serve.spec.tspackages/likec4/src/cli/serve/serve.tspackages/likec4/src/vite/vite-dev.spec.tspackages/likec4/src/vite/vite-dev.ts
There was a problem hiding this comment.
Pull request overview
Adds a new CLI option to pin the Vite HMR WebSocket port for likec4 start (and its serve/dev aliases), improving reliability in containerized/networked setups where port publishing must be explicit.
Changes:
- Added
--hmr-portCLI option (andHMR_PORTenv support) and wired it through thestarthandler into Vite dev server creation. - Refactored HMR port selection into a testable helper with unit tests.
- Updated CLI and Docker documentation plus added a changeset entry.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/likec4/src/vite/vite-dev.ts | Introduces resolveHmrPort and uses it to configure Vite HMR port. |
| packages/likec4/src/vite/vite-dev.spec.ts | Adds unit tests for HMR port resolution behavior. |
| packages/likec4/src/cli/serve/serve.ts | Extends handler params to accept hmrPort and passes it to viteDev. |
| packages/likec4/src/cli/serve/serve.spec.ts | Adds unit tests verifying hmrPort wiring from handler to viteDev. |
| packages/likec4/src/cli/serve/index.ts | Adds --hmr-port yargs option for start/serve/dev. |
| packages/likec4/src/cli/options.ts | Defines the hmrPort yargs option metadata/description. |
| apps/docs/src/content/docs/tooling/docker.mdx | Documents HMR port pinning and docker port publishing guidance. |
| apps/docs/src/content/docs/tooling/cli.mdx | Documents --hmr-port alongside other dev server options. |
| .changeset/specify-hmr-port.md | Adds a patch changeset describing the new option and env var behavior. |
| const port = explicitPort ?? (env['HMR_PORT'] ? parseInt(env['HMR_PORT'], 10) : undefined) | ||
| if (port) { | ||
| return port | ||
| } | ||
| return getPort({ port: portNumbers(24678, 24690) }) |
| await handler({ | ||
| path: '/tmp/test', | ||
| useDotBin: false, | ||
| webcomponentPrefix: 'likec4', | ||
| title: undefined, | ||
| useHashHistory: undefined, | ||
| enableHMR: false, // disable HMR to simplify – we only care about propagation | ||
| enableWebcomponent: false, | ||
| hmrPort: 24700, | ||
| }) |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
packages/likec4/src/vite/vite-dev.ts (3)
31-31: 💤 Low valueConsider using
Number.parseIntfor clarity.While
parseIntworks correctly here, usingNumber.parseIntmakes it explicit that you're calling the method from the globalNumberobject, which is preferred in modern TypeScript codebases.♻️ Optional refactor
- const port = explicitPort ?? (env['HMR_PORT'] ? parseInt(env['HMR_PORT'], 10) : undefined) + const port = explicitPort ?? (env['HMR_PORT'] ? Number.parseInt(env['HMR_PORT'], 10) : undefined)As per coding guidelines, use oxlint for linting (which flagged this via SonarCloud).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/likec4/src/vite/vite-dev.ts` at line 31, The variable declaration for port uses the global parseInt; update it to use Number.parseInt for clarity and consistency: change the parseInt call in the expression assigning port (referencing variables explicitPort and env['HMR_PORT']) to Number.parseInt(env['HMR_PORT'], 10) so the codebase explicitly uses the Number namespace as per linting guidelines.
90-95: ⚡ Quick winEnhance logging to distinguish port sources.
The current logging shows
(explicit)only whenhmrPortargument is provided, but doesn't distinguish between environment variable and auto-discovered ports. Users who setHMR_PORTenvironment variable won't see any source label, which could be confusing.📊 Suggested logging enhancement
const resolvedHmrPort = await resolveHmrPort(hmrPort, hmr) if (hmr && resolvedHmrPort !== undefined) { - const source = hmrPort ? ' (explicit)' : '' + let source = '' + if (hmrPort !== undefined) { + source = ' (via --hmr-port)' + } else if (env['HMR_PORT']) { + source = ' (via HMR_PORT env)' + } else { + source = ' (auto-discovered)' + } logger.info(`Enabling HMR: localhost:${resolvedHmrPort}${source}`) if (isInsideContainer()) { logger.info(k.yellow(`ensure port ${resolvedHmrPort} is published from container`)) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/likec4/src/vite/vite-dev.ts` around lines 90 - 95, The log for HMR port should indicate whether the port came from an explicit hmrPort argument, from the HMR_PORT environment variable, or was auto-discovered; update the message around the hmr/resolvedHmrPort logic so the source string is set based on hmrPort (explicit), process.env.HMR_PORT (env), or default/auto (auto-discovered) and include that source label in the logger.info call that currently references logger.info(`Enabling HMR: localhost:${resolvedHmrPort}${source}`); keep the isInsideContainer() check and its message unchanged.
24-36: ⚡ Quick winConsider validating port numbers are in valid range.
The function accepts
explicitPortwithout validating it's a valid port number (1–65535). Similarly, when parsingHMR_PORTfrom the environment, the result isn't range-checked. While invalid ports will cause Vite server startup to fail with an error, early validation would provide clearer feedback to users.🛡️ Suggested validation
export async function resolveHmrPort( explicitPort: number | undefined, hmrEnabled: boolean, ): Promise<number | undefined> { if (!hmrEnabled) { return undefined } const port = explicitPort ?? (env['HMR_PORT'] ? Number.parseInt(env['HMR_PORT'], 10) : undefined) - if (port) { + if (port !== undefined && !Number.isNaN(port)) { + if (port < 1 || port > 65535) { + throw new Error(`Invalid HMR port: ${port}. Must be between 1 and 65535.`) + } return port } return getPort({ port: portNumbers(24678, 24690) }) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/likec4/src/vite/vite-dev.ts` around lines 24 - 36, The resolveHmrPort function currently accepts explicitPort and parses env['HMR_PORT'] without range checks; validate both explicitPort and the parsed envPort are integers between 1 and 65535 before returning them, and if either is out of range throw a clear Error indicating the invalid value and whether it came from explicitPort or HMR_PORT; keep the fallback to getPort({ port: portNumbers(24678, 24690) }) unchanged and only call it when no valid explicit/env port is provided.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/likec4/src/vite/vite-dev.ts`:
- Line 31: The variable declaration for port uses the global parseInt; update it
to use Number.parseInt for clarity and consistency: change the parseInt call in
the expression assigning port (referencing variables explicitPort and
env['HMR_PORT']) to Number.parseInt(env['HMR_PORT'], 10) so the codebase
explicitly uses the Number namespace as per linting guidelines.
- Around line 90-95: The log for HMR port should indicate whether the port came
from an explicit hmrPort argument, from the HMR_PORT environment variable, or
was auto-discovered; update the message around the hmr/resolvedHmrPort logic so
the source string is set based on hmrPort (explicit), process.env.HMR_PORT
(env), or default/auto (auto-discovered) and include that source label in the
logger.info call that currently references logger.info(`Enabling HMR:
localhost:${resolvedHmrPort}${source}`); keep the isInsideContainer() check and
its message unchanged.
- Around line 24-36: The resolveHmrPort function currently accepts explicitPort
and parses env['HMR_PORT'] without range checks; validate both explicitPort and
the parsed envPort are integers between 1 and 65535 before returning them, and
if either is out of range throw a clear Error indicating the invalid value and
whether it came from explicitPort or HMR_PORT; keep the fallback to getPort({
port: portNumbers(24678, 24690) }) unchanged and only call it when no valid
explicit/env port is provided.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a86df92f-c78c-462a-b9b3-614f41d57902
📒 Files selected for processing (1)
packages/likec4/src/vite/vite-dev.ts
…urce label in log
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/likec4/src/vite/vite-dev.ts (1)
100-100: ⚡ Quick winExtract nested ternary for improved readability.
The nested ternary operator reduces readability. As per the static analysis hint, consider extracting this logic into a separate variable or using if-else statements.
♻️ Proposed refactor
const resolvedHmrPort = await resolveHmrPort(hmrPort, hmr) if (hmr && resolvedHmrPort !== undefined) { - const source = hmrPort ? ' (explicit)' : env['HMR_PORT'] ? ' (env)' : ' (auto-discovered)' + let source = ' (auto-discovered)' + if (hmrPort) { + source = ' (explicit)' + } else if (env['HMR_PORT']) { + source = ' (env)' + } logger.info(`Enabling HMR: localhost:${resolvedHmrPort}${source}`)As per coding guidelines, this aligns with the preference for clearer control flow over compact but harder-to-read expressions.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/likec4/src/vite/vite-dev.ts` at line 100, The nested ternary assigning source is hard to read; replace it by computing a descriptive string with a small if/else or switch before the assignment so source is set from a clear variable — use the existing identifiers hmrPort and env['HMR_PORT'] to decide between " (explicit)", " (env)", and " (auto-discovered)" and then assign that value to source (keep the symbol name source so downstream code is unchanged).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/likec4/src/vite/vite-dev.ts`:
- Around line 32-34: Extract the duplicated port validation into a single helper
(e.g., validatePort) and call it from both the explicitPort branch and the
environment-variable branch instead of repeating the Number.isInteger/1-65535
checks; update the code that currently uses explicitPort and the code that reads
the HMR port from env (the duplicated checks around explicitPort and the
corresponding env path) to invoke validatePort(port, 'explicitPort'|'env') which
throws the same Error message on invalid input so validation behavior remains
consistent and maintainable.
---
Nitpick comments:
In `@packages/likec4/src/vite/vite-dev.ts`:
- Line 100: The nested ternary assigning source is hard to read; replace it by
computing a descriptive string with a small if/else or switch before the
assignment so source is set from a clear variable — use the existing identifiers
hmrPort and env['HMR_PORT'] to decide between " (explicit)", " (env)", and "
(auto-discovered)" and then assign that value to source (keep the symbol name
source so downstream code is unchanged).
🪄 Autofix (Beta)
✅ Autofix completed
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1e591671-c807-4cf9-b804-25b86c67babf
📒 Files selected for processing (1)
packages/likec4/src/vite/vite-dev.ts
|
Note Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it. Fixes Applied SuccessfullyFixed 1 file(s) based on 1 unresolved review comment. Files modified:
Commit: The changes have been pushed to the Time taken: |
Fixed 1 file(s) based on 1 unresolved review comment. Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/likec4/src/vite/vite-dev.ts (1)
107-116: ⚡ Quick winConsider extracting the nested ternary for clarity.
The nested ternary at line 109 correctly determines the source of the HMR port, but could be made more readable by extracting it into separate
ifstatements or a small helper. Additionally, usinghmrPort !== undefinedinstead of the truthiness check would be more consistent with the logic inresolveHmrPortand more robust.♻️ Proposed refactor
const resolvedHmrPort = await resolveHmrPort(hmrPort, hmr) if (hmr && resolvedHmrPort !== undefined) { - const source = hmrPort ? ' (explicit)' : env['HMR_PORT'] ? ' (env)' : ' (auto-discovered)' + let source: string + if (hmrPort !== undefined) { + source = ' (explicit)' + } else if (env['HMR_PORT']) { + source = ' (env)' + } else { + source = ' (auto-discovered)' + } logger.info(`Enabling HMR: localhost:${resolvedHmrPort}${source}`)As per coding guidelines, the repository prefers clarity in TypeScript code; extracting the nested ternary aligns with best practices for maintainable code.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/likec4/src/vite/vite-dev.ts` around lines 107 - 116, The nested ternary that builds the source label for HMR is hard to read and uses a truthiness check on hmrPort; replace that expression by computing the source string in a small local block (e.g., before the logger.info) using explicit if/else or a helper so you can check hmrPort !== undefined, then fall back to env['HMR_PORT'] and then to 'auto-discovered'; update the logger.info call that uses resolvedHmrPort and source to use the new variable and keep the existing isInsideContainer() branch as-is (references: resolvedHmrPort, hmrPort, env['HMR_PORT'], logger.info, isInsideContainer()).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/likec4/src/vite/vite-dev.ts`:
- Around line 107-116: The nested ternary that builds the source label for HMR
is hard to read and uses a truthiness check on hmrPort; replace that expression
by computing the source string in a small local block (e.g., before the
logger.info) using explicit if/else or a helper so you can check hmrPort !==
undefined, then fall back to env['HMR_PORT'] and then to 'auto-discovered';
update the logger.info call that uses resolvedHmrPort and source to use the new
variable and keep the existing isInsideContainer() branch as-is (references:
resolvedHmrPort, hmrPort, env['HMR_PORT'], logger.info, isInsideContainer()).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 99e4968a-0c43-48a6-be7e-56a2bb2bb7ee
📒 Files selected for processing (1)
packages/likec4/src/vite/vite-dev.ts
Checklist
mainbefore creating this PR.pnpm typecheckandpnpm test./changeset-generatorSKILL).