# Bundle Size Optimization Plan ## Goal Reduce `@actions/languageservice` package size from **7.9 MB** to **~1.5 MB** (80% reduction). ## Summary | Phase | Change | Savings | Effort | |-------|--------|---------|--------| | 1a | Minify all JSON | 60% | Low | | 1b | Strip unused fields | 10% | Low | | 1c | Drop unused events | 19% | Low | | 2 | Lazy-load webhooks.json (optional) | Faster initial load | Medium | ## Phase 1: Optimize JSON files ### What each JSON file is used for | File | Package | Purpose | |------|---------|---------| | `webhooks.json` | languageservice | Autocomplete/validation for `github.event.*` expressions. Contains event payload schemas from GitHub's REST API. | | `objects.json` | languageservice | Deduplicated parameter definitions shared across webhooks (reduces duplication in webhooks.json). | | `workflow-v1.0.json` | workflow-parser | Workflow schema defining valid YAML structure (`jobs`, `steps`, `runs-on`, event triggers, etc.). | | `descriptions.json` | languageservice | Hover descriptions for contexts (`github`, `env`, `secrets`) and built-in functions (`format`, `contains`, etc.). | | `schedule.json` | languageservice | Sample `github.event` payload for `on: schedule` trigger (not a real webhook, manually authored). | | `workflow_call.json` | languageservice | Sample `github.event` payload for `on: workflow_call` trigger (not a real webhook, manually authored). | ### Impact table | File | Original | Strip | Drop | Minify | Gzip | All (no Gzip) | All (w/ Gzip) | |------|----------|-------|------|--------|------|---------------|---------------| | `webhooks.json` | 6.2 MB | 5.6 MB | 5.0 MB | 2.4 MB | 188 KB | **1.0 MB** | **50 KB** | | `objects.json` | 948 KB | N/A | 770 KB | 460 KB | 36 KB | **180 KB** | **18 KB** | | `workflow-v1.0.json` | 112 KB | N/A | N/A | 70 KB | 13 KB | **70 KB** | **12 KB** | | `descriptions.json` | 18 KB | N/A | N/A | 17 KB | 3 KB | **17 KB** | **3 KB** | | `schedule.json` | 5.7 KB | N/A | N/A | 5.1 KB | 1 KB | **5.1 KB** | **1 KB** | | `workflow_call.json` | 7.3 KB | N/A | N/A | 6.5 KB | 1 KB | **6.5 KB** | **1 KB** | | **Total** | **7.3 MB** | | | | **~240 KB** | **~1.3 MB** | **~85 KB** | - **Strip** = Remove unused fields (`summary`, `availability`, `category`, `action`) - **Drop** = Remove 31 non-trigger events (`installation`, `star`, `team`, etc.) - **Minify** = Remove whitespace (`JSON.stringify(data)` instead of `JSON.stringify(data, null, 2)`) - **Gzip** = Network transfer size (free - handled automatically by browser/server) ### 1a. Minify all JSON files **Generated files** (`webhooks.json`, `objects.json`): - Update `languageservice/script/webhooks/index.ts` - These are generated via `npm run update-webhooks` from GitHub's REST API spec - Use `JSON.stringify(data)` instead of `JSON.stringify(data, null, 2)` **Hand-authored files** (`workflow-v1.0.json`, `descriptions.json`, `schedule.json`, `workflow_call.json`): - Add minification step to build scripts ### 1b. Strip unused fields from webhooks.json Remove before writing: - `summary` - `availability` - `category` - `action` ### 1c. Drop non-trigger events from webhooks.json Keep only events that can trigger workflows ([docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows)). Drop 31 events: ``` code_scanning_alert, commit_comment, dependabot_alert, deploy_key, github_app_authorization, installation, installation_repositories, installation_target, marketplace_purchase, member, membership, meta, org_block, organization, package, ping, projects_v2, projects_v2_item, pull_request_review_thread, repository, repository_import, repository_vulnerability_alert, secret_scanning_alert, secret_scanning_alert_location, security_advisory, security_and_analysis, sponsorship, star, team, team_add, workflow_job ``` **Expected result:** Total JSON 7.3 MB → ~1.3 MB (82% reduction) --- ## Phase 2: Lazy loading (optional) Refactor `eventPayloads.ts` to load JSON on first use: ```typescript let webhooksData: Webhooks | null = null; async function getWebhooks() { if (!webhooksData) { const { default: data } = await import("./webhooks.json"); webhooksData = hydrate(data); } return webhooksData; } ``` **Benefit:** Faster initial load when `github.event.*` isn't used. --- ## Current github-ui architecture github-ui lazy-loads the language service via dynamic import: ```typescript // workflow-editor-next.ts let languageServicePromise: Promise | null = null async function getLanguageServiceModule() { if (!languageServicePromise) { languageServicePromise = import('./workflow-editor-language-service') } return languageServicePromise } ``` **What this means:** - The language service is only loaded when the workflow editor needs autocomplete/hover/validation - Webpack code-splits it into a separate chunk - The ~7.9 MB package is NOT loaded on initial page load **Why Phase 1 is the priority:** - When the language service chunk IS loaded, it still loads all 7.3 MB of JSON - Reducing JSON to ~1.3 MB directly reduces this chunk size - No changes needed in github-ui - the benefit is automatic --- ## Not doing - **Tree-shaking / `sideEffects`** - github-ui imports `complete`, `hover`, and `validate` together, and all three depend on the same webhook JSON. Tree-shaking can't eliminate any of it. - **Replacing dependencies** - `yaml` and `cronstrue` are appropriately sized - **Multi-pass validation API** - Too complex for the benefit - **Further deduplication** - Current object deduplication is sufficient --- ## Future considerations - **`workflow_call.json` may be incorrect** - For `on: workflow_call`, `github.event` is inherited from the calling workflow (could be push, pull_request, etc.). The current file shows generic properties which may be misleading for autocomplete. Consider returning `Null` for all modes or removing the file entirely. --- ## Success metrics | Metric | Before | After | |--------|--------|-------| | `webhooks.json` | 6.2 MB | ~1.2 MB | | `objects.json` | 948 KB | ~225 KB | | Total package (disk) | 7.9 MB | ~1.5 MB | | npm tarball (gzipped) | 368 KB | ~80 KB |