Skip to content

Commit 5a35bad

Browse files
feat: make webapplication.json optional for sf webapp dev command (#16)
This change makes the webapplication.json manifest file optional when running the `sf webapp dev` command. Webapps are now discovered by folder structure within the `webapplications/` directory. Key changes: - Webapp discovery now uses folder-based detection within webapplications/ folder - If webapplication.json exists, use manifest values; otherwise use defaults - Default dev command changed from 'npm run build' to 'npm run dev' - Auto-select webapp when running from inside a webapp folder - ManifestWatcher only enabled when webapplication.json is present - Improved error messages for missing dependencies (command not found) Code cleanup: - Remove unused ErrorHandler factory methods and inline single-use utilities - Remove dead code from ProxyServer (unused stats tracking) - Remove unused exported functions from webappDiscovery Testing & docs: - Add comprehensive unit tests for webappDiscovery (39 tests) - Update developer guide for optional manifest feature
1 parent e10f785 commit 5a35bad

18 files changed

Lines changed: 961 additions & 1690 deletions

SF_WEBAPP_DEV_GUIDE.md

Lines changed: 185 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ The `sf webapp dev` command enables local development of modern web applications
1010

1111
### Key Features
1212

13-
- **Auto-Discovery**: Automatically finds `webapplication.json` files in your project
13+
- **Auto-Discovery**: Automatically finds webapps in `webapplications/` folder
14+
- **Optional Manifest**: `webapplication.json` is optional - uses sensible defaults
15+
- **Auto-Selection**: Automatically selects webapp when running from inside its folder
1416
- **Interactive Selection**: Prompts with arrow-key navigation when multiple webapps exist
1517
- **Authentication Injection**: Automatically adds Salesforce auth headers to API calls
1618
- **Intelligent Routing**: Routes requests to dev server or Salesforce based on URL patterns
@@ -22,18 +24,15 @@ The `sf webapp dev` command enables local development of modern web applications
2224

2325
## Quick Start
2426

25-
### 1. Create `webapplication.json` in your project
27+
### 1. Create your webapp in the `webapplications/` folder
2628

27-
```json
28-
{
29-
"name": "myApp",
30-
"label": "My Application",
31-
"version": "1.0.0",
32-
"outputDir": "dist",
33-
"dev": {
34-
"command": "npm run dev"
35-
}
36-
}
29+
```
30+
my-project/
31+
└── webapplications/
32+
└── my-app/ # Your webapp folder
33+
├── package.json
34+
├── src/
35+
└── webapplication.json # Optional!
3736
```
3837

3938
### 2. Run the command
@@ -46,6 +45,12 @@ sf webapp dev --target-org myOrg --open
4645

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

48+
> **Note**: `webapplication.json` is optional! If not present, the command uses:
49+
>
50+
> - **Name**: Folder name (e.g., "my-app")
51+
> - **Dev command**: `npm run dev`
52+
> - **Manifest watching**: Disabled
53+
4954
---
5055

5156
## Command Syntax
@@ -90,62 +95,96 @@ SF_LOG_LEVEL=debug sf webapp dev --target-org myOrg
9095

9196
## Webapp Discovery
9297

93-
The command automatically discovers `webapplication.json` files in your project, making the `--name` flag optional in most cases.
98+
The command automatically discovers webapps in the `webapplications/` folder. Each subfolder is treated as a webapp, with `webapplication.json` being optional.
9499

95100
### How Discovery Works
96101

97102
```mermaid
98103
flowchart TD
99-
Start["sf webapp dev"] --> HasName{"--name provided?"}
100-
HasName -->|Yes| SearchByName["Search for webapplication.json with matching name field"]
101-
HasName -->|No| SearchAll["Search all webapplication.json files in project"]
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?"}
102108
103-
SearchByName --> FoundMatch{"Found?"}
104-
FoundMatch -->|Yes| UseManifest["Use matched webapplication.json"]
105-
FoundMatch -->|No| ErrorNotFound["Error: No webapp found with name X"]
109+
HasName -->|Yes| SearchByName["Find webapp by name"]
110+
HasName -->|No| InsideWebapp{"Running from inside a webapp?"}
106111
107-
SearchAll --> Count{"How many found?"}
108-
Count -->|0| ErrorNone["Error: No webapplication.json found"]
109-
Count -->|1| UseManifest
112+
InsideWebapp -->|Yes| AutoSelect["Auto-select current webapp"]
113+
InsideWebapp -->|No| Count{"How many webapps?"}
114+
115+
Count -->|1| AutoSelectSingle["Auto-select single webapp"]
110116
Count -->|Multiple| Prompt["Interactive selection prompt"]
111-
Prompt --> UseManifest
117+
118+
SearchByName --> UseWebapp["Use webapp"]
119+
AutoSelect --> UseWebapp
120+
AutoSelectSingle --> UseWebapp
121+
Prompt --> UseWebapp
122+
123+
UseWebapp --> HasManifest{"Has webapplication.json?"}
124+
HasManifest -->|Yes| UseManifest["Use manifest config"]
125+
HasManifest -->|No| UseDefaults["Use defaults (npm run dev)"]
112126
113127
UseManifest --> StartDev["Start dev server and proxy"]
128+
UseDefaults --> StartDev
114129
```
115130

116131
### Discovery Behavior
117132

118-
| Scenario | Behavior |
119-
| -------------------------------- | ----------------------------------------------------------- |
120-
| `--name myApp` provided | Finds webapplication.json where `name` field equals "myApp" |
121-
| No `--name`, single webapp found | Auto-selects the webapp |
122-
| No `--name`, multiple found | Shows interactive selection with arrow keys |
123-
| No `--name`, none found | Shows error with helpful message |
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 |
140+
141+
### Folder Structure
142+
143+
```
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/
156+
```
124157

125158
### Search Scope
126159

127-
The command searches the current directory and all subdirectories, excluding:
160+
The command searches for the `webapplications/` folder:
161+
162+
1. **Upward**: First checks if you're inside a webapplications folder
163+
2. **Downward**: Then searches child directories recursively
128164

129-
- `node_modules`
130-
- `.git`
131-
- `dist`, `build`, `out`
132-
- `coverage`
165+
Excluded directories:
166+
167+
- `node_modules`, `.git`, `dist`, `build`, `out`, `coverage`
133168
- `.next`, `.nuxt`, `.output`
134169
- Hidden directories (starting with `.`)
135170

136171
### Interactive Selection
137172

138-
When multiple `webapplication.json` files are found, you'll see an interactive prompt:
173+
When multiple webapps are found, you'll see an interactive prompt:
139174

140175
```
141-
Found 3 webapplication.json files in project
176+
Found 3 webapps in project
142177
? Select the webapp to run: (Use arrow keys)
143-
myApp - My Application (webapplication.json)
144-
adminPortal - Admin Portal (apps/admin/webapplication.json)
145-
dashboard - Dashboard App (packages/dashboard/webapplication.json)
178+
MyApp - My Application (webapplications/app-one)
179+
app-two (webapplications/app-two) [no manifest]
180+
CustomName (webapplications/app-three)
146181
```
147182

148-
Use arrow keys to navigate and Enter to select.
183+
Format:
184+
185+
- **With manifest + label**: `Name - Label (path)`
186+
- **With manifest, no label**: `Name (path)`
187+
- **No manifest**: `name (path) [no manifest]`
149188

150189
---
151190

@@ -196,39 +235,52 @@ Browser → Proxy → [Auth Headers Injected] → Salesforce → Response
196235

197236
### webapplication.json Schema
198237

199-
#### Required Fields
238+
The `webapplication.json` file is **optional**. All fields are also optional - missing fields use defaults.
239+
240+
#### All Fields (All Optional)
200241

201242
```json
202243
{
203244
"name": "myApp",
204245
"label": "My Application",
205246
"version": "1.0.0",
206-
"outputDir": "dist"
247+
"outputDir": "dist",
248+
"dev": {
249+
"command": "npm run dev"
250+
}
207251
}
208252
```
209253

210-
| Field | Type | Description |
211-
| ----------- | ------ | ----------------------------------------- |
212-
| `name` | string | Unique identifier (used with --name flag) |
213-
| `label` | string | Human-readable display name |
214-
| `version` | string | Semantic version (e.g., "1.0.0") |
215-
| `outputDir` | string | Build output directory |
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) |
216260

217261
#### Dev Configuration
218262

219-
**Option A: Auto-spawn dev server**
263+
**Option A: No manifest (uses defaults)**
264+
265+
If no `webapplication.json` exists:
266+
267+
- Dev command: `npm run dev`
268+
- Name: folder name
269+
- Manifest watching: disabled
270+
271+
**Option B: Minimal manifest**
220272

221273
```json
222274
{
223275
"dev": {
224-
"command": "npm run dev"
276+
"command": "npm start"
225277
}
226278
}
227279
```
228280

229-
The command will spawn your dev server and automatically detect its URL.
281+
Only specify what you need to override.
230282

231-
**Option B: Explicit URL (dev server already running)**
283+
**Option C: Explicit URL (dev server already running)**
232284

233285
```json
234286
{
@@ -253,7 +305,37 @@ Use this when you want to start the dev server yourself.
253305
}
254306
```
255307

256-
### Complete Example
308+
### Example: Minimal (No Manifest)
309+
310+
```
311+
webapplications/
312+
└── my-dashboard/
313+
├── package.json # Has "scripts": { "dev": "vite" }
314+
└── src/
315+
```
316+
317+
Run: `sf webapp dev --target-org myOrg`
318+
319+
Console output:
320+
321+
```
322+
Warning: No webapplication.json found for webapp "my-dashboard"
323+
Location: my-dashboard
324+
Using defaults:
325+
→ Name: "my-dashboard" (derived from folder)
326+
→ Command: "npm run dev"
327+
→ Manifest watching: disabled
328+
💡 To customize, create a webapplication.json file in your webapp directory.
329+
330+
✅ Using webapp: my-dashboard (webapplications/my-dashboard)
331+
332+
✅ Ready for development!
333+
→ Proxy: http://localhost:4545 (open this in your browser)
334+
→ Dev server: http://localhost:5173
335+
Press Ctrl+C to stop
336+
```
337+
338+
### Example: Full Configuration
257339

258340
```json
259341
{
@@ -287,6 +369,8 @@ Manifest changed detected
287369
Dev server URL updated to: http://localhost:5174
288370
```
289371

372+
> **Note**: Manifest watching is only enabled when `webapplication.json` exists. Webapps without manifests don't have this feature.
373+
290374
### Health Monitoring
291375

292376
The proxy continuously monitors dev server availability:
@@ -311,29 +395,39 @@ Automatically detects Salesforce Code Builder environment and binds to `0.0.0.0`
311395

312396
## Troubleshooting
313397

314-
### "No webapplication.json found"
398+
### "No webapplications folder found"
315399

316-
Ensure you have a `webapplication.json` file with required fields:
400+
Create a `webapplications/` folder with at least one webapp subfolder:
317401

318-
```json
319-
{
320-
"name": "myApp",
321-
"label": "My Application",
322-
"version": "1.0.0",
323-
"outputDir": "dist"
324-
}
402+
```
403+
my-project/
404+
└── webapplications/
405+
└── my-app/
406+
└── package.json
325407
```
326408

409+
Note: `webapplication.json` is optional!
410+
327411
### "No webapp found with name X"
328412

329-
The `--name` flag matches the `name` field inside `webapplication.json`, not the file path:
413+
The `--name` flag matches either:
414+
415+
1. The `name` field in `webapplication.json`
416+
2. The folder name (if no manifest or no name in manifest)
330417

331418
```bash
332-
# This looks for webapplication.json where name="myApp"
419+
# This looks for webapp named "myApp"
333420
sf webapp dev --name myApp --target-org myOrg
334421
```
335422

336-
Check your `webapplication.json` content to verify the name.
423+
### "Dependencies Not Installed" / "command not found"
424+
425+
Install dependencies in your webapp folder:
426+
427+
```bash
428+
cd webapplications/my-app
429+
npm install
430+
```
337431

338432
### "No Dev Server Detected"
339433

@@ -362,12 +456,37 @@ sf org login web --alias myOrg
362456

363457
### Debug Mode
364458

365-
Enable detailed logging:
459+
Enable detailed logging by setting `SF_LOG_LEVEL=debug`. Debug logs are written to the SF CLI log file (not stdout).
460+
461+
**Step 1: Start log tail in Terminal 1**
462+
463+
```bash
464+
# Tail today's log file, filtering for webapp messages
465+
tail -f ~/.sf/sf-$(date +%Y-%m-%d).log | grep --line-buffered WebappDev
466+
467+
# Or for cleaner output (requires jq):
468+
tail -f ~/.sf/sf-$(date +%Y-%m-%d).log | grep --line-buffered WebappDev | jq -r '.msg'
469+
```
470+
471+
**Step 2: Run command in Terminal 2**
366472

367473
```bash
368474
SF_LOG_LEVEL=debug sf webapp dev --target-org myOrg
369475
```
370476

477+
**Example debug output:**
478+
479+
```
480+
Discovering webapplication.json manifest(s)...
481+
Using webapp: myApp at webapplications/my-app
482+
Manifest loaded: myApp
483+
Starting dev server with command: npm run dev
484+
Dev server ready at: http://localhost:5173/
485+
Using authentication for org: user@example.com
486+
Starting proxy server on port 4545...
487+
Proxy server running on http://localhost:4545
488+
```
489+
371490
---
372491

373492
## VSCode Integration

0 commit comments

Comments
 (0)