Skip to content

Commit e743427

Browse files
deepu-mungamuri94ankitsinghkuntal09Ankit Singhcursoragentbpbuch
authored
refactor: improve webapp discovery with SFDX project detection @W-21193297@ (#18)
* refactor(dev): improve webapp discovery and console output @W-21193297@ - Add SFDX project detection for webapp discovery - Simplify webapplications folder detection logic - Condense warning messages, remove verbose JSON examples - Add help tip for configuration options * feat: W-21111977 consume error-page template from @salesforce/webapp-experimental/proxy (#22) * feat: consume error-page template from @salesforce/webapp-experimental/proxy @W-21111977@ Update plugin-webapp to import the error page HTML template from the @salesforce/webapp-experimental proxy package instead of bundling it locally. Migration changes: - ErrorPageRenderer now uses getErrorPageTemplate() from @salesforce/webapp-experimental/proxy - Removed local error-page.html template (now lives in webapps package) - Removed scripts/copy-templates.cjs (no longer needed) - Removed postbuild script from package.json - Bumped @salesforce/webapp-experimental dependency to ^1.17.0 Bug fixes included: - DevServerManager: emit DevServerError directly instead of wrapping in SfError, so the proxy can display the "Dev Server Error" page when dev server crashes - ProxyServer: add socket error handler to prevent ECONNRESET from crashing the proxy when dev server dies mid-connection Depends on: salesforce-experience-platform-emu/webapps PR for @W-21111977@ Co-authored-by: Cursor <cursoragent@cursor.com> * feat: W-21111977 consume error-page template from @salesforce/webapp-experimental/proxy Update plugin-webapp to import the error page HTML template from the @salesforce/webapp-experimental proxy package (v1.23.0) instead of bundling it locally. Migration changes: - ErrorPageRenderer now uses getErrorPageTemplate() from @salesforce/webapp-experimental/proxy (direct import, no workarounds) - Removed local error-page.html template (now lives in webapps package) - Removed scripts/copy-templates.cjs (no longer needed) - Removed postbuild script from package.json - Bumped @salesforce/webapp-experimental dependency to ^1.23.0 Bug fixes included: - DevServerManager: emit DevServerError directly instead of wrapping in SfError, so the proxy can display the "Dev Server Error" page when dev server crashes - ProxyServer: add socket error handler to prevent ECONNRESET from crashing the proxy when dev server dies mid-connection - ProxyServer: remove label/version from fallback manifest (not in type) Depends on: salesforce-experience-platform-emu/webapps#86 (merged) Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Ankit Singh <singhankit@singhank-ltmg4lv.internal.salesforce.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix: address PR feedback and merge conflicts - messages: specify 'proxy server' in Ctrl+C stop message - ProxyServer: add label and version to fallback WebAppManifest - ErrorPageRenderer: use local template (getErrorPageTemplate not in package) - tests: add label and version to WebAppManifest fixtures Co-authored-by: Cursor <cursoragent@cursor.com> * revert: drop merge-fix changes, keep only PR feedback Keep only the messages change: 'Press Ctrl+C to stop the proxy server'. Revert ErrorPageRenderer, ProxyServer, and test changes - will address separately. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: address PR review comments - no-manifest: log applied defaults (dev command, proxy port) at info level - ready-for-development: show only URL to open in browser (proxy URL) - flags.url: add dev server URL precedence to help text Co-authored-by: Cursor <cursoragent@cursor.com> * chore: remove unused error-page-template.ts Co-authored-by: Cursor <cursoragent@cursor.com> * @W-21111861@ feat: Skip standalone proxy when Vite WebApp proxy is active (#23) * feat: add Vite proxy detection and TTY-aware messaging - Detect Vite WebApp proxy via health check, skip standalone proxy when active - Use getErrorPageTemplate from @salesforce/webapp-experimental/proxy - TTY-aware stop message (Ctrl+C in terminal vs VS Code command palette) - Add info.ready-for-development-vite for Vite proxy case - Simplify ready-for-development to show only URL to open - Resolve PR review comments on messaging Co-authored-by: Cursor <cursoragent@cursor.com> * fix: remove label/version to match @salesforce/webapp-experimental 1.x type - Run yarn install to resolve 1.23.0+ (was 0.2.0 from stale lock) - ProxyServer: use minimal fallback { name, outputDir } per PR #22 - Tests: remove label/version from manifest fixtures - ManifestWatcher tests: fix assertions for simplified type Co-authored-by: Cursor <cursoragent@cursor.com> * fix: group ready-for-development messages, simplify Vite message format Co-authored-by: Cursor <cursoragent@cursor.com> * fix: update server-running message - remove bold, add quotes Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> * Address review comments: URL mismatch tests, multi-meta warning, 60s timeout - Add comprehensive tests for devServerUrl vs actualDevServerUrl mismatch combinations (explicit URL match/mismatch, manifest, skipDevServer cases) - Warn when multiple .webapplication-meta.xml files found in directory; use first match for backward compatibility - Increase dev server startup timeout from 30 to 60 seconds to align with VS Code extension and support slower dev server startups Co-authored-by: Cursor <cursoragent@cursor.com> * refactor: discover webapps from all package directories via SfProject Co-authored-by: Cursor <cursoragent@cursor.com> * Update SF_WEBAPP_DEV_GUIDE.md Co-authored-by: Brian Buchanan <5377888+bpbuch@users.noreply.github.com> * Update SF_WEBAPP_DEV_GUIDE.md Co-authored-by: Brian Buchanan <5377888+bpbuch@users.noreply.github.com> * Update SF_WEBAPP_DEV_GUIDE.md Co-authored-by: Brian Buchanan <5377888+bpbuch@users.noreply.github.com> * SF_WEBAPP_DEV_GUIDE: document only dev.command and dev.url for dev command - Remove name, label from --name option (matches folder name) - Simplify picker format, remove label references - Remove Full Configuration example (name, label, version, outputDir) - Add Dev + Routing example with only dev and routing - Update troubleshooting: --name matches folder name only Co-authored-by: Cursor <cursoragent@cursor.com> * Revert README to main branch Co-authored-by: Cursor <cursoragent@cursor.com> * Align README with main branch (plugin-app-dev template) Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Ankit Singh <singhankit@salesforce.com> Co-authored-by: Ankit Singh <singhankit@singhank-ltmg4lv.internal.salesforce.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Brian Buchanan <5377888+bpbuch@users.noreply.github.com>
1 parent caed512 commit e743427

13 files changed

Lines changed: 1218 additions & 484 deletions

File tree

.prettierignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# CLI message templates - preserve formatting
2+
messages/*.md

SF_WEBAPP_DEV_GUIDE.md

Lines changed: 160 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,17 @@ The `sf webapp dev` command enables local development of modern web applications
2424

2525
## Quick Start
2626

27-
### 1. Create your webapp in the `webapplications/` folder
27+
### 1. Create your webapp in the SFDX project structure
2828

2929
```
30-
my-project/
31-
└── webapplications/
32-
└── my-app/ # Your webapp folder
30+
my-sfdx-project/
31+
├── sfdx-project.json
32+
└── force-app/main/default/webapplications/
33+
└── my-app/
34+
├── my-app.webapplication-meta.xml
3335
├── package.json
3436
├── src/
35-
└── webapplication.json # Optional!
37+
└── webapplication.json
3638
```
3739

3840
### 2. Run the command
@@ -45,11 +47,13 @@ sf webapp dev --target-org myOrg --open
4547

4648
Browser opens to `http://localhost:4545` with your app running and Salesforce authentication ready.
4749

48-
> **Note**: `webapplication.json` is optional! If not present, the command uses:
50+
> **Note**:
4951
>
50-
> - **Name**: Folder name (e.g., "my-app")
51-
> - **Dev command**: `npm run dev`
52-
> - **Manifest watching**: Disabled
52+
> - `{name}.webapplication-meta.xml` is **required** to identify a valid webapp
53+
> - `webapplication.json` is optional for dev configuration. If not present, defaults to:
54+
> - **Name**: From meta.xml filename or folder name
55+
> - **Dev command**: `npm run dev`
56+
> - **Manifest watching**: Disabled
5357
5458
---
5559

@@ -64,7 +68,7 @@ sf webapp dev [OPTIONS]
6468
| Option | Short | Description | Default |
6569
| -------------- | ----- | ----------------------------------------------- | ------------- |
6670
| `--target-org` | `-o` | Salesforce org alias or username | Required |
67-
| `--name` | `-n` | Web application name (from webapplication.json) | Auto-discover |
71+
| `--name` | `-n` | Web application name (folder name) | Auto-discover |
6872
| `--url` | `-u` | Explicit dev server URL | Auto-detect |
6973
| `--port` | `-p` | Proxy server port | 4545 |
7074
| `--open` | `-b` | Open browser automatically | false |
@@ -95,78 +99,76 @@ SF_LOG_LEVEL=debug sf webapp dev --target-org myOrg
9599

96100
## Webapp Discovery
97101

98-
The command automatically discovers webapps in the `webapplications/` folder. Each subfolder is treated as a webapp, with `webapplication.json` being optional.
102+
The command discovers webapps using a simplified, deterministic algorithm. Webapps are identified by the presence of a `{name}.webapplication-meta.xml` file (SFDX metadata format). The optional `webapplication.json` file provides dev configuration.
99103

100104
### How Discovery Works
101105

102106
```mermaid
103107
flowchart TD
104-
Start["sf webapp dev"] --> FindFolder["Find webapplications/ folder"]
105-
FindFolder --> Found{"Found?"}
106-
Found -->|No| ErrorNone["Error: No webapplications folder found"]
107-
Found -->|Yes| HasName{"--name provided?"}
108+
Start["sf webapp dev"] --> CheckInside{"Inside webapplications/<br/>webapp folder?"}
108109
109-
HasName -->|Yes| SearchByName["Find webapp by name"]
110-
HasName -->|No| InsideWebapp{"Running from inside a webapp?"}
110+
CheckInside -->|Yes| HasNameInside{"--name provided?"}
111+
HasNameInside -->|Yes, different| ErrorConflict["Error: --name conflicts<br/>with current directory"]
112+
HasNameInside -->|No or same| AutoSelect["Auto-select current webapp"]
111113
112-
InsideWebapp -->|Yes| AutoSelect["Auto-select current webapp"]
113-
InsideWebapp -->|No| Count{"How many webapps?"}
114+
CheckInside -->|No| CheckSFDX{"In SFDX project?<br/>(sfdx-project.json)"}
114115
115-
Count -->|1| AutoSelectSingle["Auto-select single webapp"]
116-
Count -->|Multiple| Prompt["Interactive selection prompt"]
116+
CheckSFDX -->|Yes| CheckPath["Check force-app/main/<br/>default/webapplications/"]
117+
CheckPath --> HasName{"--name provided?"}
118+
119+
CheckSFDX -->|No| CheckMetaXml{"Current dir has<br/>.webapplication-meta.xml?"}
120+
CheckMetaXml -->|Yes| UseStandalone["Use current dir as webapp"]
121+
CheckMetaXml -->|No| ErrorNone["Error: No webapp found"]
122+
123+
HasName -->|Yes| SearchByName["Find webapp by name"]
124+
HasName -->|No| Prompt["Interactive selection prompt<br/>(always, even if 1 webapp)"]
117125
118126
SearchByName --> UseWebapp["Use webapp"]
119127
AutoSelect --> UseWebapp
120-
AutoSelectSingle --> UseWebapp
128+
UseStandalone --> UseWebapp
121129
Prompt --> UseWebapp
122130
123-
UseWebapp --> HasManifest{"Has webapplication.json?"}
124-
HasManifest -->|Yes| UseManifest["Use manifest config"]
125-
HasManifest -->|No| UseDefaults["Use defaults (npm run dev)"]
126-
127-
UseManifest --> StartDev["Start dev server and proxy"]
128-
UseDefaults --> StartDev
131+
UseWebapp --> StartDev["Start dev server and proxy"]
129132
```
130133

131134
### Discovery Behavior
132135

133-
| Scenario | Behavior |
134-
| --------------------------------- | ---------------------------------------------- |
135-
| `--name myApp` provided | Finds webapp by name (manifest name or folder) |
136-
| Running from inside webapp folder | Auto-selects that webapp |
137-
| Single webapp found | Auto-selects it |
138-
| Multiple webapps found | Shows interactive selection with arrow keys |
139-
| No webapplications folder | Shows error with helpful message |
136+
| Scenario | Behavior |
137+
| ----------------------------------- | --------------------------------------------------------- |
138+
| `--name myApp` provided | Finds webapp by name, starts dev server |
139+
| Running from inside webapp folder | Auto-selects that webapp |
140+
| `--name` conflicts with current dir | Error: must match current webapp or run from project root |
141+
| At SFDX project root | **Always prompts** for webapp selection |
142+
| Outside SFDX project with meta.xml | Uses current directory as standalone webapp |
143+
| No webapp found | Shows error with helpful message |
140144

141-
### Folder Structure
145+
### Folder Structure (SFDX Project)
142146

143147
```
144-
my-project/
145-
└── webapplications/ # Required folder (case-insensitive)
146-
├── app-one/ # Webapp 1 (with manifest)
147-
│ ├── webapplication.json
148-
│ ├── package.json
149-
│ └── src/
150-
├── app-two/ # Webapp 2 (no manifest - uses defaults)
151-
│ ├── package.json
152-
│ └── src/
153-
└── app-three/ # Webapp 3 (partial manifest)
154-
├── webapplication.json # Only has dev.command
155-
└── src/
148+
my-sfdx-project/
149+
├── sfdx-project.json # SFDX project marker
150+
└── force-app/main/default/
151+
└── webapplications/ # Standard SFDX location
152+
├── app-one/ # Webapp 1 (with dev config)
153+
│ ├── app-one.webapplication-meta.xml # Required: identifies as webapp
154+
│ ├── webapplication.json # Optional: dev configuration
155+
│ ├── package.json
156+
│ └── src/
157+
└── app-two/ # Webapp 2 (no dev config)
158+
├── app-two.webapplication-meta.xml # Required
159+
├── package.json
160+
└── src/
156161
```
157162

158-
### Search Scope
159-
160-
The command searches for the `webapplications/` folder:
163+
### Discovery Strategy
161164

162-
1. **Upward**: First checks if you're inside a webapplications folder
163-
2. **Downward**: Then searches child directories recursively
165+
The command uses a simplified, deterministic approach:
164166

165-
Excluded directories:
167+
1. **Inside webapp folder**: If running from `webapplications/<webapp>/` or deeper, auto-selects that webapp
168+
2. **SFDX project root**: Uses fixed path `force-app/main/default/webapplications/`
169+
3. **Standalone**: If current directory has a `.webapplication-meta.xml` file, uses it directly
166170

167-
- `node_modules`, `.git`, `dist`, `build`, `out`, `coverage`
168-
- `.next`, `.nuxt`, `.output`
169-
- Hidden directories (starting with `.`)
171+
**Important**: Only directories containing a `{name}.webapplication-meta.xml` file are recognized as valid webapps.
170172

171173
### Interactive Selection
172174

@@ -175,16 +177,15 @@ When multiple webapps are found, you'll see an interactive prompt:
175177
```
176178
Found 3 webapps in project
177179
? Select the webapp to run: (Use arrow keys)
178-
MyApp - My Application (webapplications/app-one)
180+
app-one (webapplications/app-one)
179181
app-two (webapplications/app-two) [no manifest]
180-
CustomName (webapplications/app-three)
182+
app-three (webapplications/app-three)
181183
```
182184

183185
Format:
184186

185-
- **With manifest + label**: `Name - Label (path)`
186-
- **With manifest, no label**: `Name (path)`
187-
- **No manifest**: `name (path) [no manifest]`
187+
- **With manifest**: `folder-name (path)`
188+
- **No manifest**: `folder-name (path) [no manifest]`
188189

189190
---
190191

@@ -237,28 +238,14 @@ Browser → Proxy → [Auth Headers Injected] → Salesforce → Response
237238

238239
The `webapplication.json` file is **optional**. All fields are also optional - missing fields use defaults.
239240

240-
#### All Fields (All Optional)
241-
242-
```json
243-
{
244-
"name": "myApp",
245-
"label": "My Application",
246-
"version": "1.0.0",
247-
"outputDir": "dist",
248-
"dev": {
249-
"command": "npm run dev"
250-
}
251-
}
252-
```
241+
#### Dev Configuration
253242

254-
| Field | Type | Description | Default |
255-
| ----------- | ------ | ----------------------------------------- | ------------------ |
256-
| `name` | string | Unique identifier (used with --name flag) | Folder name |
257-
| `label` | string | Human-readable display name | None |
258-
| `version` | string | Semantic version (e.g., "1.0.0") | None |
259-
| `outputDir` | string | Build output directory | None (deploy only) |
243+
| Field | Type | Description | Default |
244+
| ------------- | ------ | ------------------------------------- | ------------------------- |
245+
| `dev.command` | string | Command to start dev server | `npm run dev` |
246+
| `dev.url` | string | Dev server URL (when already running) | `http://localhost:5173` |
260247

261-
#### Dev Configuration
248+
All fields are optional. Only specify what you need to override.
262249

263250
**Option A: No manifest (uses defaults)**
264251

@@ -278,8 +265,6 @@ If no `webapplication.json` exists:
278265
}
279266
```
280267

281-
Only specify what you need to override.
282-
283268
**Option C: Explicit URL (dev server already running)**
284269

285270
```json
@@ -335,15 +320,10 @@ Warning: No webapplication.json found for webapp "my-dashboard"
335320
Press Ctrl+C to stop
336321
```
337322

338-
### Example: Full Configuration
323+
### Example: Dev + Routing
339324

340325
```json
341326
{
342-
"name": "salesDashboard",
343-
"label": "Sales Dashboard",
344-
"description": "Real-time sales analytics dashboard",
345-
"version": "2.1.0",
346-
"outputDir": "dist",
347327
"dev": {
348328
"command": "npm run dev"
349329
},
@@ -393,27 +373,105 @@ Automatically detects Salesforce Code Builder environment and binds to `0.0.0.0`
393373

394374
---
395375

376+
## The `--url` Flag
377+
378+
The `--url` flag provides control over which dev server URL the proxy uses. It has smart behavior depending on whether the URL is already available.
379+
380+
### Behavior
381+
382+
| Scenario | What Happens |
383+
| ------------------------ | ----------------------------------------------------------------- |
384+
| `--url` is reachable | **Proxy-only mode**: Skips starting dev server, only starts proxy |
385+
| `--url` is NOT reachable | Starts dev server, warns if actual URL differs from `--url` |
386+
| No `--url` provided | Starts dev server automatically, detects URL |
387+
388+
### Use Case 1: Connect to Existing Dev Server (Proxy-Only Mode)
389+
390+
If you prefer to manage your dev server separately:
391+
392+
```bash
393+
# Terminal 1: Start your dev server manually
394+
cd my-webapp
395+
npm run dev
396+
# Output: Local: http://localhost:5173/
397+
398+
# Terminal 2: Connect proxy to your running server
399+
sf webapp dev --url http://localhost:5173 --target-org myOrg
400+
```
401+
402+
**Output:**
403+
404+
```
405+
✅ URL http://localhost:5173 is already available, skipping dev server startup (proxy-only mode)
406+
✅ Ready for development!
407+
→ Proxy: http://localhost:4545
408+
→ Dev server: http://localhost:5173
409+
```
410+
411+
### Use Case 2: URL Mismatch Warning
412+
413+
If you specify a `--url` that doesn't match where the dev server actually starts:
414+
415+
```bash
416+
# No dev server running, specify wrong port
417+
sf webapp dev --url http://localhost:9999 --target-org myOrg
418+
```
419+
420+
**Output:**
421+
422+
```
423+
Warning: ⚠️ The --url flag (http://localhost:9999) does not match the actual dev server URL (http://localhost:5173/).
424+
The proxy will use the actual dev server URL.
425+
```
426+
427+
The command continues working with the actual dev server URL.
428+
429+
### Important Notes
430+
431+
- The `--url` flag checks **only** the URL you specify, not other ports
432+
- If you have a dev server on port 5173 but specify `--url http://localhost:9999`:
433+
- Command checks 9999 → not available
434+
- Starts a NEW dev server → may get port 5174 (if 5173 is taken)
435+
- Warns about mismatch (9999 ≠ 5174)
436+
- To use an existing dev server, specify its **exact** URL with `--url`
437+
438+
---
439+
396440
## Troubleshooting
397441

398-
### "No webapplications folder found"
442+
### "No webapp found" or "No valid webapps"
399443

400-
Create a `webapplications/` folder with at least one webapp subfolder:
444+
Ensure your webapp has the required `.webapplication-meta.xml` file:
401445

402446
```
403-
my-project/
404-
└── webapplications/
405-
└── my-app/
406-
└── package.json
447+
force-app/main/default/webapplications/
448+
└── my-app/
449+
├── my-app.webapplication-meta.xml # Required!
450+
├── package.json
451+
└── webapplication.json # Optional (for dev config)
407452
```
408453

409-
Note: `webapplication.json` is optional!
454+
The `.webapplication-meta.xml` file identifies a valid SFDX webapp. Without it, the directory is ignored.
410455

411-
### "No webapp found with name X"
456+
### "You are inside webapp X but specified --name Y"
412457

413-
The `--name` flag matches either:
458+
This error occurs when you're inside one webapp folder but try to run a different webapp:
459+
460+
```bash
461+
# You're in FirstWebApp folder but trying to run SecondWebApp
462+
cd webapplications/FirstWebApp
463+
sf webapp dev --name SecondWebApp --target-org myOrg # Error!
464+
```
465+
466+
**Solutions:**
467+
468+
- Remove `--name` to use the current webapp
469+
- Navigate to the project root and use `--name`
470+
- Navigate to the correct webapp folder
471+
472+
### "No webapp found with name X"
414473

415-
1. The `name` field in `webapplication.json`
416-
2. The folder name (if no manifest or no name in manifest)
474+
The `--name` flag matches the folder name of the webapp.
417475

418476
```bash
419477
# This looks for webapp named "myApp"
@@ -586,7 +644,7 @@ plugin-app-dev/
586644
| Component | Purpose |
587645
| ---------------------- | ------------------------------------------------ |
588646
| `dev.ts` | Command orchestration and lifecycle |
589-
| `webappDiscovery.ts` | Recursive webapplication.json discovery |
647+
| `webappDiscovery.ts` | SFDX project detection and webapp discovery |
590648
| `org.ts` | Salesforce authentication token management |
591649
| `ProxyServer.ts` | HTTP proxy with WebSocket support |
592650
| `handler.ts` | Request routing to dev server or Salesforce |

0 commit comments

Comments
 (0)