Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
a8579b1
refactor(dev): improve webapp discovery and console output @W-21193297@
deepu-mungamuri94 Feb 6, 2026
eadfb26
Merge branch 'main' into refactor-webapp-discovery
deepu-mungamuri94 Feb 9, 2026
9b9b08e
feat: W-21111977 consume error-page template from @salesforce/webapp-…
ankitsinghkuntal09 Feb 11, 2026
2e80525
fix: address PR feedback and merge conflicts
deepu-mungamuri94 Feb 13, 2026
d9be3cb
revert: drop merge-fix changes, keep only PR feedback
deepu-mungamuri94 Feb 13, 2026
cbba20b
Merge remote-tracking branch 'origin/main' into refactor-webapp-disco…
deepu-mungamuri94 Feb 13, 2026
593993c
fix: address PR review comments
deepu-mungamuri94 Feb 13, 2026
49c4034
chore: remove unused error-page-template.ts
deepu-mungamuri94 Feb 13, 2026
96be9de
Merge branch 'main' into refactor-webapp-discovery
deepu-mungamuri94 Feb 17, 2026
ea13492
@W-21111861@ feat: Skip standalone proxy when Vite WebApp proxy is ac…
deepu-mungamuri94 Feb 18, 2026
da39c4c
Address review comments: URL mismatch tests, multi-meta warning, 60s …
deepu-mungamuri94 Feb 18, 2026
1e88b07
refactor: discover webapps from all package directories via SfProject
deepu-mungamuri94 Feb 19, 2026
1cbf5f3
Update SF_WEBAPP_DEV_GUIDE.md
deepu-mungamuri94 Feb 20, 2026
50c9844
Update SF_WEBAPP_DEV_GUIDE.md
deepu-mungamuri94 Feb 20, 2026
aba3b9c
Update SF_WEBAPP_DEV_GUIDE.md
deepu-mungamuri94 Feb 20, 2026
7c50c3b
SF_WEBAPP_DEV_GUIDE: document only dev.command and dev.url for dev co…
deepu-mungamuri94 Feb 20, 2026
1603232
Revert README to main branch
deepu-mungamuri94 Feb 20, 2026
c505620
Align README with main branch (plugin-app-dev template)
deepu-mungamuri94 Feb 20, 2026
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: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# CLI message templates - preserve formatting
messages/*.md
262 changes: 160 additions & 102 deletions SF_WEBAPP_DEV_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,17 @@ The `sf webapp dev` command enables local development of modern web applications

## Quick Start

### 1. Create your webapp in the `webapplications/` folder
### 1. Create your webapp in the SFDX project structure

```
my-project/
└── webapplications/
└── my-app/ # Your webapp folder
my-sfdx-project/
├── sfdx-project.json
└── force-app/main/default/webapplications/
└── my-app/
├── my-app.webapplication-meta.xml
├── package.json
├── src/
└── webapplication.json # Optional!
└── webapplication.json
```

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

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

> **Note**: `webapplication.json` is optional! If not present, the command uses:
> **Note**:
>
> - **Name**: Folder name (e.g., "my-app")
> - **Dev command**: `npm run dev`
> - **Manifest watching**: Disabled
> - `{name}.webapplication-meta.xml` is **required** to identify a valid webapp
> - `webapplication.json` is optional for dev configuration. If not present, defaults to:
> - **Name**: From meta.xml filename or folder name
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is not a property in webapplication.json

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.

Right.. Updated doc.

> - **Dev command**: `npm run dev`
> - **Manifest watching**: Disabled

---

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

## Webapp Discovery

The command automatically discovers webapps in the `webapplications/` folder. Each subfolder is treated as a webapp, with `webapplication.json` being optional.
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.

### How Discovery Works

```mermaid
flowchart TD
Start["sf webapp dev"] --> FindFolder["Find webapplications/ folder"]
FindFolder --> Found{"Found?"}
Found -->|No| ErrorNone["Error: No webapplications folder found"]
Found -->|Yes| HasName{"--name provided?"}
Start["sf webapp dev"] --> CheckInside{"Inside webapplications/<br/>webapp folder?"}

HasName -->|Yes| SearchByName["Find webapp by name"]
HasName -->|No| InsideWebapp{"Running from inside a webapp?"}
CheckInside -->|Yes| HasNameInside{"--name provided?"}
HasNameInside -->|Yes, different| ErrorConflict["Error: --name conflicts<br/>with current directory"]
HasNameInside -->|No or same| AutoSelect["Auto-select current webapp"]

InsideWebapp -->|Yes| AutoSelect["Auto-select current webapp"]
InsideWebapp -->|No| Count{"How many webapps?"}
CheckInside -->|No| CheckSFDX{"In SFDX project?<br/>(sfdx-project.json)"}

Count -->|1| AutoSelectSingle["Auto-select single webapp"]
Count -->|Multiple| Prompt["Interactive selection prompt"]
CheckSFDX -->|Yes| CheckPath["Check force-app/main/<br/>default/webapplications/"]
CheckPath --> HasName{"--name provided?"}

CheckSFDX -->|No| CheckMetaXml{"Current dir has<br/>.webapplication-meta.xml?"}
CheckMetaXml -->|Yes| UseStandalone["Use current dir as webapp"]
CheckMetaXml -->|No| ErrorNone["Error: No webapp found"]

HasName -->|Yes| SearchByName["Find webapp by name"]
HasName -->|No| Prompt["Interactive selection prompt<br/>(always, even if 1 webapp)"]

SearchByName --> UseWebapp["Use webapp"]
AutoSelect --> UseWebapp
AutoSelectSingle --> UseWebapp
UseStandalone --> UseWebapp
Prompt --> UseWebapp

UseWebapp --> HasManifest{"Has webapplication.json?"}
HasManifest -->|Yes| UseManifest["Use manifest config"]
HasManifest -->|No| UseDefaults["Use defaults (npm run dev)"]

UseManifest --> StartDev["Start dev server and proxy"]
UseDefaults --> StartDev
UseWebapp --> StartDev["Start dev server and proxy"]
```

### Discovery Behavior

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

### Folder Structure
### Folder Structure (SFDX Project)

```
my-project/
└── webapplications/ # Required folder (case-insensitive)
├── app-one/ # Webapp 1 (with manifest)
│ ├── webapplication.json
│ ├── package.json
│ └── src/
├── app-two/ # Webapp 2 (no manifest - uses defaults)
│ ├── package.json
│ └── src/
└── app-three/ # Webapp 3 (partial manifest)
├── webapplication.json # Only has dev.command
└── src/
my-sfdx-project/
├── sfdx-project.json # SFDX project marker
└── force-app/main/default/
└── webapplications/ # Standard SFDX location
├── app-one/ # Webapp 1 (with dev config)
│ ├── app-one.webapplication-meta.xml # Required: identifies as webapp
│ ├── webapplication.json # Optional: dev configuration
│ ├── package.json
│ └── src/
└── app-two/ # Webapp 2 (no dev config)
├── app-two.webapplication-meta.xml # Required
├── package.json
└── src/
```

### Search Scope

The command searches for the `webapplications/` folder:
### Discovery Strategy

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

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

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

### Interactive Selection

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

Format:

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

---

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

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

#### All Fields (All Optional)

```json
{
"name": "myApp",
"label": "My Application",
"version": "1.0.0",
"outputDir": "dist",
"dev": {
"command": "npm run dev"
}
}
```
#### Dev Configuration

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

#### Dev Configuration
All fields are optional. Only specify what you need to override.

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

Expand All @@ -278,8 +265,6 @@ If no `webapplication.json` exists:
}
```

Only specify what you need to override.

**Option C: Explicit URL (dev server already running)**

```json
Expand Down Expand Up @@ -335,15 +320,10 @@ Warning: No webapplication.json found for webapp "my-dashboard"
Press Ctrl+C to stop
```

### Example: Full Configuration
### Example: Dev + Routing

```json
{
"name": "salesDashboard",
"label": "Sales Dashboard",
"description": "Real-time sales analytics dashboard",
"version": "2.1.0",
"outputDir": "dist",
"dev": {
"command": "npm run dev"
},
Expand Down Expand Up @@ -393,27 +373,105 @@ Automatically detects Salesforce Code Builder environment and binds to `0.0.0.0`

---

## The `--url` Flag

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.

### Behavior

| Scenario | What Happens |
| ------------------------ | ----------------------------------------------------------------- |
| `--url` is reachable | **Proxy-only mode**: Skips starting dev server, only starts proxy |
| `--url` is NOT reachable | Starts dev server, warns if actual URL differs from `--url` |
| No `--url` provided | Starts dev server automatically, detects URL |

### Use Case 1: Connect to Existing Dev Server (Proxy-Only Mode)

If you prefer to manage your dev server separately:

```bash
# Terminal 1: Start your dev server manually
cd my-webapp
npm run dev
# Output: Local: http://localhost:5173/

# Terminal 2: Connect proxy to your running server
sf webapp dev --url http://localhost:5173 --target-org myOrg
```

**Output:**

```
✅ URL http://localhost:5173 is already available, skipping dev server startup (proxy-only mode)
✅ Ready for development!
→ Proxy: http://localhost:4545
→ Dev server: http://localhost:5173
```

### Use Case 2: URL Mismatch Warning

If you specify a `--url` that doesn't match where the dev server actually starts:

```bash
# No dev server running, specify wrong port
sf webapp dev --url http://localhost:9999 --target-org myOrg
```

**Output:**

```
Warning: ⚠️ The --url flag (http://localhost:9999) does not match the actual dev server URL (http://localhost:5173/).
The proxy will use the actual dev server URL.
```

The command continues working with the actual dev server URL.

### Important Notes

- The `--url` flag checks **only** the URL you specify, not other ports
- If you have a dev server on port 5173 but specify `--url http://localhost:9999`:
- Command checks 9999 → not available
- Starts a NEW dev server → may get port 5174 (if 5173 is taken)
- Warns about mismatch (9999 ≠ 5174)
- To use an existing dev server, specify its **exact** URL with `--url`

---

## Troubleshooting

### "No webapplications folder found"
### "No webapp found" or "No valid webapps"

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

```
my-project/
└── webapplications/
└── my-app/
└── package.json
force-app/main/default/webapplications/
└── my-app/
├── my-app.webapplication-meta.xml # Required!
├── package.json
└── webapplication.json # Optional (for dev config)
```

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

### "No webapp found with name X"
### "You are inside webapp X but specified --name Y"

The `--name` flag matches either:
This error occurs when you're inside one webapp folder but try to run a different webapp:

```bash
# You're in FirstWebApp folder but trying to run SecondWebApp
cd webapplications/FirstWebApp
sf webapp dev --name SecondWebApp --target-org myOrg # Error!
```

**Solutions:**

- Remove `--name` to use the current webapp
- Navigate to the project root and use `--name`
- Navigate to the correct webapp folder

### "No webapp found with name X"

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

```bash
# This looks for webapp named "myApp"
Expand Down Expand Up @@ -586,7 +644,7 @@ plugin-app-dev/
| Component | Purpose |
| ---------------------- | ------------------------------------------------ |
| `dev.ts` | Command orchestration and lifecycle |
| `webappDiscovery.ts` | Recursive webapplication.json discovery |
| `webappDiscovery.ts` | SFDX project detection and webapp discovery |
| `org.ts` | Salesforce authentication token management |
| `ProxyServer.ts` | HTTP proxy with WebSocket support |
| `handler.ts` | Request routing to dev server or Salesforce |
Expand Down
Loading