Skip to content

LWC extension rewrites jsconfig.json unconditionally and watches directory creations, which causes sustained file-watcher CPU load #7966

Description

Summary

The LWC extension causes a continuous file-watcher load in large SFDX projects. Two defects combine.

  1. writeSfdxJsconfig() writes jsconfig.json on every call. The function does not compare the new content with the old content.
  2. The LWC client watches **/ with ignoreCreateEvents: false. The Apex and Lightning extensions use ignoreCreateEvents: true for the same purpose.

The extension writes into the workspace. The file watcher sees the write. The watcher event starts a new index operation. The index operation regenerates the LWC typings. The regeneration creates more watcher events.

All CPU figures below use the percent-of-one-core convention. The top command uses the same convention. My machine has 14 logical CPUs, thus the maximum is 1400%. The Windows Task Manager uses a different convention. It divides by the total capacity. Thus it shows a smaller number for the same load.

Defect 1: unconditional write

packages/salesforcedx-lightning-lsp-common/src/baseContext.ts

383:  const existingConfigContent = await this.fileSystemAccessor.getFileContent(jsconfigPath);
      // ...merge...
426:  jsconfigContent = JSON.stringify(mergedConfig, null, 4);
452:  await this.fileSystemAccessor.updateFileContent(jsconfigPath, jsconfigContent);

Line 383 reads the old content. Line 452 writes the new content. The code does not compare the two values.

updateForceIgnoreFile() in the same file uses the correct pattern. writeSfdxJsconfig() calls this function at line 466.

115:  const originalContent = forceignoreContent;
      // ...append patterns...
136:  if (forceignoreContent === originalContent) {
137:    return;
138:  }

Defect 2: watcher flag

Three packages create the same watcher. The LWC package is different from the other two.

// packages/salesforcedx-vscode-apex/src/languageServer.ts:234
createFileSystemWatcher('**/', true, true, false),   // only events for folder deletions

// packages/salesforcedx-vscode-lightning/src/index.ts:156
createFileSystemWatcher('**/', true, true, false),

// packages/salesforcedx-vscode-lwc/src/languageClient/clientOptions.ts:33
// need to watch for directory deletions as no events are created for contents or deleted directories
createFileSystemWatcher('**/', false, true, false)   // ignoreCreateEvents = false

The signature is (pattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents). The comment says that the watcher observes deletions. The code also observes the creation of every directory in the workspace. https://code.visualstudio.com/api/references/vscode-api#workspace

Steps To Reproduce:

  1. Open a large SFDX project in VS Code. My project has 191 LWC bundles and 496 Apex classes.

  2. Make sure that files.watcherExclude does not exclude .sfdx. This is the default condition.

  3. Do not use the editor for some minutes.

  4. Run the PowerShell script below. The script samples the CPU time for 8 seconds. It reports both conventions.

    $cores = [Environment]::ProcessorCount
    $ids = (Get-CimInstance Win32_Process -Filter "Name='Code.exe'").ProcessId
    $t1 = @{}
    foreach ($i in $ids) {
      $p = Get-CimInstance Win32_Process -Filter "ProcessId=$i" -EA SilentlyContinue
      if ($p) { $t1[$i] = ($p.KernelModeTime + $p.UserModeTime) / 1e7 }
    }
    $sw = [Diagnostics.Stopwatch]::StartNew(); Start-Sleep -Seconds 8; $sw.Stop()
    $el = $sw.Elapsed.TotalSeconds
    foreach ($i in $ids) {
      $p = Get-CimInstance Win32_Process -Filter "ProcessId=$i" -EA SilentlyContinue
      if ($p -and $t1.ContainsKey($i)) {
        $d = (($p.KernelModeTime + $p.UserModeTime) / 1e7) - $t1[$i]
        if ($d -gt 0.05) {
          [pscustomobject]@{
            PID        = $i
            CPUsec     = [math]::Round($d, 2)
            PctOneCore = [math]::Round($d / $el * 100, 1)
            PctMachine = [math]::Round($d / ($el * $cores) * 100, 1)
          }
        }
      }
    }
  5. Find the process with the highest CPUsec value.

  6. Run (Get-CimInstance Win32_Process -Filter "ProcessId=<PID>").CommandLine for that process. The command line contains --utility-sub-type=node.mojom.NodeService. This process runs the file watcher.

  7. Look at the modification times of force-app/main/default/lwc/jsconfig.json.

  8. Look at the modification times of the files in .sfdx/typings/lwc/.

Expected result

The file watcher uses almost no CPU while the editor is idle.

The extension writes jsconfig.json only when the content changes.

Actual result

The file-watcher process keeps one core to two cores busy while the editor is idle. The process accumulated 4.9 hours of CPU time in one session.

Two measurements from the same session follow. The editor was idle for both.

Sample Interval CPU time used Percent of one core Percent of machine
1 6.00 s 7.6 s 126.6% 9.0%
2 8.01 s 16.75 s 209.2% 14.9%

The machine has 14 logical CPUs. The process uses more than one thread, thus it uses more than one core.

The extension regenerates the 5,166 .d.ts files in .sfdx/typings/lwc/. These files and jsconfig.json get new modification times again and again. The content of jsconfig.json does not change.

Image

The extension host becomes unstable under this load. My renderer.log file has 915 entries that report an unresponsive extension host. The same log shows the exits of 14 different extension-host process IDs.

The extension host starts git.exe child processes. When the extension host exits, the child processes become orphans. I found 10 orphaned git.exe processes. They stayed blocked for two days. Because of these processes, the built-in Git view and the diff view do not work correctly. A git status command in a terminal stays fast at 265 ms to 421 ms.

Additional information

Suggested fix for defect 1

Compare the content before the write in writeSfdxJsconfig():

if (jsconfigContent === existingConfigContent) {
  continue;
}
await this.fileSystemAccessor.updateFileContent(jsconfigPath, jsconfigContent);

Suggested fix for defect 2

Set ignoreCreateEvents to true in clientOptions.ts at line 33. This value agrees with the comment. It also agrees with the Apex and Lightning packages.

createFileSystemWatcher('**/', true, true, false)

Workaround

Add files.watcherExclude for **/.sfdx/** to the workspace settings. This setting reduces the load, but it does not remove the cause.

Note on .forceignore

writeSfdxJsconfig() also fore appends two patterns to .forceignore. The patterns are **/jsconfig.json and **/.eslintrc.json. This write has the correct guard. I report it because the extension changes a file that is under source control. Maybe, the extension should be able to distinguish between developer tooling actual sources, so it does not tries to parse and deploy local configs, even if these are not even in a proper source folder or have associated *-meta.xml and related *.ts, *.js, or any other LWC specific source files with them. Check: jsconfig.json

Project Info:

Project Info

Generated: 2026-08-07T05:55:07.771Z

Metadata

sourceApiVersion: 67.0
packageDirectories: 1
namespace:

Types

Type Components Files Size (KB)
ApexClass 496 1488 6364
ApexComponent 12 36 28
ApexEmailNotifications 1 1 0
ApexPage 39 117 169
ApexTrigger 35 105 30
ApprovalProcess 2 2 5
AssignmentRules 2 2 318
Audience 32 32 51
AuraDefinitionBundle 29 180 255
AutoResponseRules 2 2 2
Bot 3 9 56
CampaignInfluenceModel 1 1 0
Certificate 6 18 22
CleanDataService 3 3 57
Community 1 1 0
ConnectedApp 6 6 10
ContentAsset 5031 15093 292845
CspTrustedSite 26 26 18
CustomApplication 28 28 1440
CustomFeedFilter 3 3 1
CustomField 1 1 1
CustomHelpMenuSection 1 1 1
CustomIndex 3 3 0
CustomLabels 1 1 343
CustomMetadata 1811 1811 2084
CustomNotificationType 1 1 0
CustomObject 411 6748 8118
CustomObjectTranslation 1806 18988 11850
CustomPermission 16 16 4
CustomSite 9 9 17
CustomTab 32 32 7
Dashboard 2 2 7
DashboardFolder 1 1 1
DataCategoryGroup 3 3 6
DigitalExperience 17 51 23
DigitalExperienceBundle 1 1 1
DigitalExperienceConfig 1 1 0
Document 23 69 3117
DocumentFolder 1 1 0
DuplicateRule 11 11 20
EmailFolder 5 5 13
EmailServicesFunction 2 2 2
EmailTemplate 122 366 745
EmbeddedServiceConfig 1 1 3
EscalationRules 1 1 0
ExperienceBundle 4 449 813
ExternalClientApplication 2 2 1
ExternalCredential 2 2 3
ExternalDataSource 1 1 1
ExtlClntAppConfigurablePolicies 2 2 1
ExtlClntAppGlobalOauthSettings 2 2 3
ExtlClntAppOauthConfigurablePolicies 2 2 2
ExtlClntAppOauthSecuritySettings 2 2 1
ExtlClntAppOauthSettings 2 2 1
FlexiPage 75 75 1632
Flow 151 151 2682
FlowDefinition 93 93 16
ForecastingType 20 20 9
GenAiFunction 10 40 34
GenAiPlannerBundle 2 4 3
GenAiPlugin 3 3 12
GenAiPromptTemplate 1 1 18
GlobalValueSet 32 32 288
GlobalValueSetTranslation 142 142 1443
Group 282 282 52
HomePageLayout 1 1 1
IframeWhiteListUrlSettings 1 1 0
InstalledPackage 14 14 4
Layout 402 402 1847
LeadConvertSettings 1 1 5
Letterhead 1 1 1
LightningComponentBundle 191 899 2973
LightningMessageChannel 3 3 1
ListView 1 1 1
ManagedContentType 3 3 5
ManagedTopics 5 5 1
MatchingRules 5 5 9
MessagingChannel 1 1 4
MilestoneType 8 8 2
NamedCredential 6 6 4
NavigationMenu 15 15 24
Network 4 4 13
NetworkBranding 5 15 54
NotificationTypeConfig 1 1 22
PathAssistant 11 11 11
PermissionSet 86 86 1585
PermissionSetGroup 3 3 2
PresenceUserConfig 1 1 1
Profile 27 27 23939
ProfilePasswordPolicy 17 17 11
ProfileSessionSetting 16 16 7
Prompt 6 6 11
Queue 198 198 132
QueueRoutingConfig 3 3 1
QuickAction 120 120 151
RemoteSiteSetting 21 21 6
Report 22 22 53
ReportFolder 6 6 3
ReportType 90 90 2050
Role 2383 2383 1153
SamlSsoConfig 3 3 8
Settings 125 125 1739
SharingRules 189 189 49
SharingSet 1 1 1
SiteDotCom 4 12 4706
StandardValueSet 38 38 99
StandardValueSetTranslation 172 172 858
StaticResource 45 2627 51491
Territory2 127 127 68
Territory2Model 2 2 0
Territory2Rule 83 83 45
Territory2Type 6 6 1
TimeSheetTemplate 1 1 1
TopicsForObjects 146 146 32
Translations 21 21 17879
UserProvisioningConfig 1 1 0
WebStoreTemplate 4 4 5
Workflow 7 7 9

Org

Key Value
Org type production
Source tracking false
SourceMember count N/A

Settings

Setting Value
salesforcedx-vscode-metadata.showSuccessNotification true
salesforcedx-vscode-metadata.sourceTracking.pollingIntervalSeconds 0
salesforcedx-vscode-core.push-or-deploy-on-save.enabled false
salesforcedx-vscode-core.push-or-deploy-on-save.ignoreConflictsOnPush false
salesforcedx-vscode-core.detectConflictsForDeployAndRetrieve false
salesforcedx-vscode-core.clearOutputTab false
salesforcedx-vscode-core.show-cli-success-msg true
salesforcedx-vscode-core.telemetry.enabled false
salesforcedx-vscode-core.enable-sobject-refresh-on-startup true
salesforcedx-vscode-core.telemetry-tag
salesforcedx-vscode-salesforcedx.enableLocalTraces false
salesforcedx-vscode-salesforcedx.enableConsoleTraces false
salesforcedx-vscode-salesforcedx.enableFileTraces false
salesforcedx-vscode-apex.java.home C:\Program Files\Java\jdk-25.0.2

Environment

Key Value
Salesforce CLI @salesforce/cli/2.142.7 win32-x64 node-v24.14.1
Java java 25.0.2 2026-01-20 LTS
Editor Visual Studio Code
VS Code 1.132.0
Node v24.18.0
OS Windows_NT 10.0.26200

Extensions (Salesforce)

Extension Version
salesforce.apex-language-server-extension 0.8.0
salesforce.mule-dx-api-component 4.21.0
salesforce.mule-dx-apikit-component 1.4.0
salesforce.mule-dx-data-weave-client 2.13.0
salesforce.mule-dx-dependencies 1.7.1
salesforce.mule-dx-extension-pack 1.24.1
salesforce.mule-dx-mule-dev-component 3.31.4
salesforce.mule-dx-munit-component 1.3.13
salesforce.mule-dx-runtime 1.2.0
salesforce.mule-dx-vscode 5.18.3
salesforce.salesforce-vscode-slds 2.0.12
salesforce.salesforcedx-metadata-visualizer-vscode 1.2.0
salesforce.salesforcedx-vscode 67.7.1
salesforce.salesforcedx-vscode-apex 67.7.1
salesforce.salesforcedx-vscode-apex-log 67.7.1
salesforce.salesforcedx-vscode-apex-replay-debugger 67.7.1
salesforce.salesforcedx-vscode-apex-testing 67.7.1
salesforce.salesforcedx-vscode-core 67.7.1
salesforce.salesforcedx-vscode-expanded 67.7.1
salesforce.salesforcedx-vscode-lightning 67.7.1
salesforce.salesforcedx-vscode-lwc 67.7.1
salesforce.salesforcedx-vscode-metadata 67.7.1
salesforce.salesforcedx-vscode-org 67.7.1
salesforce.salesforcedx-vscode-org-browser 67.7.1
salesforce.salesforcedx-vscode-services 67.7.1
salesforce.salesforcedx-vscode-soql 67.7.1
salesforce.salesforcedx-vscode-ui-preview 1.4.0
salesforce.salesforcedx-vscode-visualforce 67.7.1
salesforce.sfdx-code-analyzer-vscode 1.21.0

Most recent version of the extensions where this was working:

Not sure, had these issues for a while, but now the frustration accumulated so bad, I had to see what's under the hood. I think salesforcedx-lightning-lsp-common was shipped with this issue, as packages/salesforcedx-lightning-lsp-common/src/baseContext.ts always worked as described, from first commit: 3352a85, and packages/salesforcedx-vscode-lwc/src/languageClient/clientOptions.ts had the same issue from it's first commit too 54f6656

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions