Skip to content

sf webapp dev command @W-20242483@#6

Merged
nrkruk merged 36 commits intomainfrom
sf-webapp-dev-command
Jan 30, 2026
Merged

sf webapp dev command @W-20242483@#6
nrkruk merged 36 commits intomainfrom
sf-webapp-dev-command

Conversation

@deepu-mungamuri94
Copy link
Copy Markdown
Collaborator

@deepu-mungamuri94 deepu-mungamuri94 commented Nov 20, 2025

Work Item: @W-20242483@

This PR implements the sf webapp dev command, a local development proxy server that enables seamless webapp development with Salesforce authentication and dev server integration.

🎯 What This Does

Provides developers with a local proxy server that:

  • Handles Authentication: Automatically injects Salesforce CLI tokens into requests
  • Routes Traffic: Intelligently routes requests between Salesforce APIs and local dev servers
  • Manages Dev Servers: Spawns, monitors, and manages local dev server lifecycle (Vite, CRA, Next.js)
  • Provides Great UX: Shows beautiful error pages, auto-detects server URLs, and handles failures gracefully

✨ Key Features

For Developers:

  • Single command to start authenticated development: sf webapp dev --name myapp --target-org myorg --open
  • Automatic browser launch with working authentication
  • Support for multiple dev server types (Vite, Create React App, Next.js, custom servers)
  • Beautiful HTML error pages when dev server is down (auto-refresh every 5s)
  • Real-time configuration updates when webapp.json changes

For Webapp Architecture:

  • WebSocket support for Hot Module Replacement (HMR)
  • Code Builder environment detection and special URI handling
  • Configurable proxy port with conflict detection
  • Graceful shutdown and cleanup on Ctrl+C

🏗️ Architecture

Built with modular components:

  • ProxyServer: Core HTTP proxy with auth injection and health monitoring
  • DevServerManager: Spawns and monitors dev servers with automatic URL detection
  • AuthManager: Manages Salesforce authentication and token refresh
  • RequestRouter: Intelligent routing between Salesforce and dev server
  • ManifestWatcher: Loads and watches webapp.json for changes
  • ErrorHandler: Standardized, user-friendly error messages

📝 Usage Example

# Start proxy with automatic dev server management
sf webapp dev --name myapp --target-org myorg --open

# Or use existing dev server
sf webapp dev --name myapp --target-org myorg --url http://localhost:5173 --open

🔍 What to Review

  1. Core proxy functionality (src/proxy/ProxyServer.ts, src/proxy/RequestRouter.ts)
  2. Dev server management (src/server/DevServerManager.ts) - especially URL detection with ANSI stripping
  3. Error handling and UX (src/error/ErrorHandler.ts, src/templates/error-page.html)
  4. Configuration management (src/config/ManifestWatcher.ts)
  5. Command integration (src/commands/webapp/dev.ts)

🚀 Technical Highlights

  • ANSI Color Code Stripping: Robust URL detection even when dev servers output colored text
  • Line-by-Line Processing: Handles chunked stdout from spawned processes
  • Organized URL Patterns: Easy to add support for new dev server types
  • Unicode Support: Properly detects Vite's Unicode arrow character (➜)
  • HTML Error Pages: Beautiful, informative error pages with auto-refresh
  • Health Monitoring: Periodic checks with automatic status updates

@salesforce-cla
Copy link
Copy Markdown

Thanks for the contribution! Unfortunately we can't verify the commit author(s): dmungamuri <d***@s***.com>. One possible solution is to add that email to your GitHub account. Alternatively you can change your commits to another email and force push the change. After getting your commits associated with your GitHub account, refresh the status of this Pull Request.

@deepu-mungamuri94 deepu-mungamuri94 changed the title W-20274055 sf-webapp-dev-command Foundation, messages & type definitions @W-20242483 @salesforce/plugin-webapp - sf webapp dev command Nov 24, 2025
@deepu-mungamuri94 deepu-mungamuri94 changed the title @W-20242483 @salesforce/plugin-webapp - sf webapp dev command W-20242483 @salesforce/plugin-webapp - sf webapp dev command Nov 24, 2025
Implement a local proxy server for webapp development that handles
authentication, request routing, and dev server lifecycle management.

FEATURES:

Core Proxy Server:
- HTTP proxy server with configurable port (default: 4545)
- Automatic authentication injection using CLI tokens
- Request routing between Salesforce APIs and local dev server
- WebSocket support for Hot Module Replacement (HMR)
- Code Builder environment detection and URI handling
- Graceful shutdown on SIGINT/SIGTERM
- Port conflict detection with user-friendly error messages

Dev Server Management:
- Automatic dev server lifecycle management (spawn, monitor, restart)
- URL detection for Vite, Create React App, Next.js, and custom servers
- ANSI color code stripping for robust URL detection
- Configurable startup timeout (default: 30s)
- Process cleanup on exit
- Support for explicit URL mode (no spawning)

Configuration Management:
- webapp.json manifest loading and validation with JSON schema
- File watching with automatic reload on configuration changes
- Debounced change detection
- Comprehensive validation error messages

Error Handling & User Experience:
- Beautiful HTML error page when dev server is unreachable
- Auto-refresh every 5 seconds until dev server is detected
- Periodic health checks (every 5s) with status updates
- Standardized error messages with actionable suggestions
- Sensitive data sanitization in error logs

MODULES IMPLEMENTED:

- AuthManager: Salesforce authentication and token management
- RequestRouter: Intelligent request routing (Salesforce vs dev server)
- ProxyServer: Core HTTP proxy with health checks and error handling
- DevServerManager: Dev server lifecycle management with URL detection
- ManifestWatcher: webapp.json loading, validation, and change detection
- ErrorHandler: Standardized error messages with actionable suggestions
- Logger: Simple logging utility with debug mode

COMMAND:

sf webapp dev --name <name> --target-org <org> [--url <url>] [--port <port>] [--open]
- Orchestrates all modules
- Supports multiple dev server types (Vite, CRA, Next.js)
- Opens browser automatically with --open flag
- Graceful shutdown and cleanup

TESTING:

- Comprehensive unit tests for all modules
- NUT tests for command error scenarios
- Manual validation scripts in docs/manual-tests/

TECHNICAL HIGHLIGHTS:

- ANSI color code stripping for robust URL detection
- Line-by-line output processing for chunked stdout
- Organized URL patterns by server type for maintainability
- Unicode support for Vite arrow character (➜)
- HTML error page template with auto-refresh
- JSON schema validation for configuration

Work Item: W-20242483
deepu-mungamuri94 and others added 11 commits November 24, 2025 16:54
- Add SF_WEBAPP_DEV_DOCUMENTATION.md: Complete guide with architecture,
  usage, troubleshooting, and developer guide
- Add QUICK_REFERENCE.md: Quick reference with one-liners and common fixes
- Add docs/README.md: Documentation index and navigation
- Update main README.md: Add plugin overview, features, and quick start
- Update .gitignore: Allow markdown documentation files

Documentation covers:
- Installation and quick start
- Command usage and all flags
- Configuration (webapp.json)
- Architecture and how it works
- Troubleshooting guide
- Developer guide for extending
- API reference
- Best practices

Work Item: W-20242483
- Remove 'Available Documentation' section
- Remove 'Finding Information' section (By Topic, By Role)
- Remove 'Troubleshooting' section
- Remove 'Contributing' section
- Remove 'Links' section
- Keep only essential: Getting Started and Command Overview

Work Item: W-20242483
- Remove 'Known Limitations' section from main docs
- Remove 'Support' section with work item reference
- Remove 'Changelog' section (first version)
- Remove 'Last Updated' and work item footer
- Remove intro header from docs/README.md
- Remove work item reference from Getting Help in QUICK_REFERENCE.md

Documentation is now cleaner and focuses on user-facing content only.

Work Item: W-20242483
The compile command was reverted when yarn install ran sf-install.
This restores the build script to properly copy:
- src/schemas/*.json to lib/schemas/
- src/templates/*.html to lib/templates/

These files are required at runtime for:
- webapp.json validation (webapp-manifest.json)
- HTML error page rendering (error-page.html)

Work Item: W-20242483
- Implement global error capture with stack trace extraction
  - Add GlobalErrorCapture for uncaught exceptions and unhandled rejections
  - Add StackTraceFormatter for parsing and formatting V8 stack traces
  - Filter intentional exits (SIGINT, oclif EEXIT) from error capture

- Add beautiful runtime error UI
  - Create runtime-error-page.html with modern white card design
  - Display error type, message, and scrollable stack trace (limited to 10 lines)
  - Show diagnostics (Node version, platform, PID, memory usage)
  - Provide actionable suggestions for fixing errors
  - Auto-refresh every 3 seconds to detect when error is resolved
  - Auto-recovery: return to app when dev server becomes healthy

- Fix webapp dev command exit behavior
  - Add SIGINT to expected exit signals in DevServerManager
  - Track and destroy active connections for immediate proxy shutdown
  - Add 2-second force-close timeout to prevent hanging
  - Command now exits cleanly on Ctrl+C without restart loops

- Improve terminal output logging
  - Move verbose startup logs to debug mode
  - Keep only essential logs in normal mode
  - Standardize log messages across components

- Add error handling utilities
  - Add runtime error codes (RUNTIME_ERROR, UNCAUGHT_EXCEPTION, UNHANDLED_REJECTION)
  - Add context-aware error suggestions
  - Integrate with existing ErrorHandler system

- Comprehensive test coverage
  - Add 287 passing tests including error capture, stack trace formatting, and exit handling
  - Test intentional exit filtering (SIGINT, SIGTERM, oclif exits)
  - Test error page rendering and XSS prevention

All changes follow strict ESLint rules with no inline disables.
- Normalize backslashes to forward slashes in path assertions
- Fixes test failure on Windows CI where paths use backslashes
- Test now passes on both Unix and Windows platforms
- Add ProxyServer.updateDevServerUrl() method to dynamically update dev server target
- Enhance manifest change handler to detect and apply dev.url changes without restart
- Add warning message when dev.command changes (requires restart)
- Update manifest reference to reflect all changes
- Add unit tests for dynamic configuration updates

When webapp.json is modified:
- dev.url changes: proxy updates immediately with health check
- dev.command changes: user warned to restart command
- All other fields: manifest reference updated

Tests: 289 passing (all existing + 2 new tests)
- Remove old documentation files (QUICK_REFERENCE.md, README.md, SF_WEBAPP_DEV_DOCUMENTATION.md)
- Add SF_WEBAPP_DEV_GUIDE.md - comprehensive guide covering:
  - Overview and architecture
  - Getting started and building
  - Command usage and options
  - File structure and components
  - VSCode integration details
  - Advanced features (hot reload, error capture, etc.)
  - Troubleshooting and FAQ

Single source of truth for all webapp dev command documentation.
- Move SF_WEBAPP_DEV_GUIDE.md from docs/ to root for easier access
- Update README documentation section with single comprehensive guide reference
- Remove outdated references to deleted documentation files
- Simplify documentation structure (single source of truth)
- Consolidate error templates into single unified error-page.html
- Add DevServerErrorParser for parsing and categorizing dev server stderr
- Improve error page design with narrower diagnostics panel (308px)
- Add emergency exit commands with copy button for hung processes
- Remove redundant fallback HTML methods (use plain text as last resort)
- Refactor ErrorPageRenderer to separate HTML from TypeScript logic
- Add comprehensive tests for DevServerErrorParser
- Update error handling to display parsed dev server errors in browser

All error pages now use consistent UI with:
- Top header with status badge
- Two-column layout (main content + diagnostics)
- Emergency commands section with SVG copy buttons
- Auto-refresh for recoverable errors
package.json Outdated
"files": [
"src/**/*.ts",
"src/**/*.json",
"src/**/*.html",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why do we need this?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is outdated after review comments. We are now only copying error html from templates folder.

Why lib/templates/ is needed

TypeScript compiler (tsc) only converts .ts.js files. It does NOT copy .html, .json, or other non-TS files.

The Problem

At runtime, ErrorPageRenderer.js loads the template relative to itself:

const currentDir = dirname(fileURLToPath(import.meta.url));
const templatePath = join(currentDir, 'error-page.html');Since the compiled code runs from lib/templates/, it looks for templates in lib/templates/, not src/templates/.

Additionally

The src/ folder is not published to npm (only lib/ is in the files array). When users install the plugin, they only get lib/. Templates must be there for the plugin to work.

Solution

The copy-resources wireit task copies src/templates/*.htmllib/templates/ during build.

@@ -3,8 +3,8 @@
"alias": [],
"command": "webapp:dev",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not able to run this locally with sf plugins link . How are you testing this?

Copy link
Copy Markdown
Collaborator Author

@deepu-mungamuri94 deepu-mungamuri94 Dec 4, 2025

Choose a reason for hiding this comment

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

I have used the vibe-coding-starter & my workspace orgfarm org in vscode for validation

Build & Link Plugin

Build

cd /path/to/plugin-webapp
yarn build

Link to SF CLI

sf plugins link .

Verify

sf plugins

Setup webapp.json

Create webapp.json in your project(vibe-coding-starter) root:

{
  "name": "myWebApp",
  "label": "My Web App",
  "version": "1.0.0",
  "outputDir": "dist",
  "dev": {
    "url": "http://localhost:5173"
  }
}

Or with a command to start dev server:

{
  "name": "myWebApp",
  "label": "My Web App", 
  "version": "1.0.0",
  "outputDir": "dist",
  "dev": {
    "command": "cd static-app && npm run dev"
  }
}

Usage

cd /path/to/vibe-coding-starter
sf webapp dev --name myWebApp --open

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

}
},
"additionalProperties": false
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not confident this will be the final schema so I don't know that we need to be validating the webapp.json at this point.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

the schema file is no longer here. I'm using the similar approach as that of the packages/webapps.

@@ -0,0 +1,755 @@
<!DOCTYPE html>
<html lang="en">
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

On proxy server startup, this error occurs:

[ErrorPageRenderer] Failed to load template from /Users/nkruk/git/cli/plugin-webapp/lib/templates/error-page.html: Error: ENOENT: no such file or directory, open '/Users/nkruk/git/cli/plugin-webapp/lib/templates/error-page.html'
at readFileSync (node:fs:441:20)
at new ErrorPageRenderer (file:///Users/nkruk/git/cli/plugin-webapp/lib/templates/ErrorPageRenderer.js:32:29)
at new ProxyServer (file:///Users/nkruk/git/cli/plugin-webapp/lib/proxy/ProxyServer.js:51:34)
at WebappDev.run (file:///Users/nkruk/git/cli/plugin-webapp/lib/commands/webapp/dev.js:207:32)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
at async WebappDev._run (/Users/nkruk/git/cli/plugin-webapp/node_modules/@oclif/core/lib/command.js:182:22)
at async Config.runCommand (/Users/nkruk/.local/share/sf/client/2.108.6-399bc9e/node_modules/@oclif/core/lib/config/config.js:456:25)
at async run (/Users/nkruk/.local/share/sf/client/2.108.6-399bc9e/node_modules/@oclif/core/lib/main.js:97:16)
at async file:///Users/nkruk/.local/share/sf/client/2.108.6-399bc9e/bin/run.js:15:1 {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '/Users/nkruk/git/cli/plugin-webapp/lib/templates/error-page.html'
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This issue was fixed, pls retry. The copy-resources task is now included in the build dependencies, which copies src/templates/error-page.html to lib/templates/.
npm run build

Running only npm run compile won't copy the HTML templates - use npm run build for a complete build.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Might still need more work when runnning from installed CLI vs linked locally. We can address in followup PR

<!DOCTYPE html>
<html lang="en">

<head>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is it possible this error page could just be part of @salesforce/webapp proxy package?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

not a high priority though, we can look at in follow up PR

import { execCmd, TestSession } from '@salesforce/cli-plugins-testkit';
import { expect } from 'chai';

describe('webapp dev NUTs', () => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

TODO: need more thorough nut tests.

  • Need to have an SFDX test project with multiple webapps
  • Some of those are invalid configs
  • 1 -> launches the devserver
  • 2 -> specify the URL
  • 3 -> dev server fails to launch
  • 4 -> dev server specified with URL isnt running
  • 5 -> webapp.json is misconfigured missing
  • 6 -> Launching command from different directories (root vs webapp folder)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Also a test to verify we properly forward the auth header and that matches the SFCLI token for the org

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Any other tests to check manifest changes -> change manifest and verify it works with proxy

- Remove maxRestarts option from DevServerOptions and DevServerConfig
- Remove restartCount tracking and auto-restart behavior
- Simplify handleProcessExit to just emit events and cleanup
- Remove obsolete Process Restart test block
- CLI will exit cleanly on crash, users restart manually
- Remove try-catch wrapper around proxyHandler (package handles errors internally)
- Remove unused handleRequestError method
- Package already returns proper HTTP error responses for all cases
- Remove single-function file (only used in one place)
- Inline Authorization header directly in WebSocket handler
- Delete empty auth/ directory
private async initLogger(): Promise<void> {
if (!this.logger) {
// Logger respects SF_LOG_LEVEL environment variable
this.logger = await Logger.child('DevServerManager');
Copy link
Copy Markdown
Collaborator

@nrkruk nrkruk Jan 29, 2026

Choose a reason for hiding this comment

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

Can we use Logger.childFromRoot() (which is not an async function) and initialize the logger in constructor?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done

…ation

- DevServerManager: initialize logger in constructor, remove initLogger() method
- ProxyServer: initialize logger in constructor, remove async logger init from start()
- DevServerManager.start() is now synchronous (returns void instead of Promise<void>)
- Remove optional chaining on logger calls since it's always initialized
- Update callers to not await DevServerManager.start()
super();
this.config = config;
this.errorPageRenderer = new ErrorPageRenderer();
this.workspaceScript = ProxyServer.detectWorkspaceScript();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is this still used at all?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, It is used.
How to validate ? When the preview opens.. try to stop the dev server.

errorPageRenderer renders error pages when the dev server fails or isn't running, and workspaceScript is displayed in those error pages to help users run the correct command.

package.json Outdated
"/oclif.lock",
"/oclif.manifest.json",
"/schemas"
"/oclif.manifest.json"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We should remove this

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We don't need the schemas and oclif.lock files. but we still need the oclif.manifest.json
`Without it, the CLI has to dynamically discover commands at runtime:

  • Scan the commands/ directory

  • Load each TypeScript/JavaScript file

  • Parse the command class to extract flags, args, descriptions

  • Build the command tree

With the manifest, all that metadata is pre-computed:

  • Read one JSON file

  • Instantly know all available commands and their structure`

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Removed /npm-shrinkwrap.json and /oclif.lock from the files array

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Took reference from here and hence those were added : https://github.com/salesforcecli/plugin-lightning-dev/blob/main/package.json#L53

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ok thats fine, I just want to be sure we are consistent with other plugins. We can leave these in

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Added back. Turns out this was just an issue with our dependencies being out of date

deepu-mungamuri94 and others added 11 commits January 30, 2026 15:00
Remove /npm-shrinkwrap.json and /oclif.lock from the files array
as they are not standard for SF CLI plugins and not required.
The generate command has been moved to a different repo.
This removes all related files including tests, schemas, and documentation.
The error-page.html template wasn't being copied to lib/templates
during build, causing runtime errors.
Signal handling for graceful shutdown is already done in dev.ts
using prependOnceListener to work correctly with oclif.
The duplicate handlers in ProxyServer were causing conflicts.
Ensures error-page.html template is copied to lib/templates during build.
sf-install from @salesforce/dev-scripts keeps removing copy-resources
from build dependencies. Using postbuild script instead which runs
after build and is not modified by sf-install.
postbuild script now handles copying HTML templates,
so the copy-resources wireit task is no longer needed.
The Unix commands (mkdir -p, cp) don't work on Windows.
Using Node.js fs module instead which works on all platforms.
- Create scripts/copy-templates.cjs for copying HTML templates
- Uses Node.js built-in fs module (works on all platforms)
- sf-install won't modify dedicated script files
- Provides clear console output during build
"volta": {
"node": "20.20.0",
"yarn": "1.22.22"
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Added volta config to ensure we are building locally with consistent tooling

"@salesforce/core": "^8.25.0",
"@salesforce/kit": "^3.2.4",
"@salesforce/sf-plugins-core": "^12"
"@salesforce/sf-plugins-core": "^12.2.6",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Our build issues we were hitting were due to out of date dependencies / hence the issues with oclif manifest not linking properly with latest CLI. These updates resolve the issue.

Copy link
Copy Markdown
Collaborator

@nrkruk nrkruk left a comment

Choose a reason for hiding this comment

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

Should be good enough for M2. We can address any code complexity / nut tests / other bugs that arise as part of M3

@nrkruk nrkruk changed the title W-20242483 @salesforce/plugin-webapp - sf webapp dev command sf webapp dev command @W-20242483@ Jan 30, 2026
@nrkruk nrkruk merged commit bca47ba into main Jan 30, 2026
15 checks passed
@nrkruk nrkruk deleted the sf-webapp-dev-command branch January 30, 2026 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants