npm: Use fs API to read package.json, avoid LSP notification spam#292380
Draft
npm: Use fs API to read package.json, avoid LSP notification spam#292380
Conversation
- Use workspace.fs.readFile instead of workspace.openTextDocument to read package.json - This prevents textDocument/didOpen and didClose notifications to LSP servers - Add mtime-based caching to avoid unnecessary file reads - Create minimal TextDocument stub for parsing scripts without triggering LSP Co-authored-by: connor4312 <2230985+connor4312@users.noreply.github.com>
- Avoid potential out-of-bounds array access when checking for line endings - Use safer approach to detect Windows line endings (\r\n) Co-authored-by: connor4312 <2230985+connor4312@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Fix json documentSelector to prevent spamming LSP requests
npm: Use fs API to read package.json, avoid LSP notification spam
Feb 2, 2026
connor4312
requested changes
Feb 2, 2026
extensions/npm/src/tasks.ts
Outdated
| } | ||
|
|
||
| // Helper to create a minimal TextDocument-like object from file content | ||
| function createTextDocumentStub(uri: Uri, content: string): TextDocument { |
Member
There was a problem hiding this comment.
Don't try to fake a text document. Instead, make readScripts's first document be an IPositionOffsetTransformer, which matches the minimal types of the document but otherwise has an implementation that can be used for strings.
Contributor
Author
There was a problem hiding this comment.
Done in commit 849be58. Created IPositionOffsetTransformer interface and refactored readScripts to accept it instead of TextDocument. The PositionOffsetTransformer class now properly implements this interface without faking VS Code APIs.
- Create IPositionOffsetTransformer interface in readScripts.ts - Change readScripts to accept IPositionOffsetTransformer instead of TextDocument - Replace createTextDocumentStub with PositionOffsetTransformer class - TextDocument already implements IPositionOffsetTransformer, so existing callers work unchanged - Cleaner architecture that doesn't fake VS Code APIs Co-authored-by: connor4312 <2230985+connor4312@users.noreply.github.com>
connor4312
requested changes
Feb 2, 2026
extensions/npm/src/tasks.ts
Outdated
| } | ||
|
|
||
| // Cache for package.json file reading to avoid unnecessary reads | ||
| const packageJsonCache = new Map<string, IPackageJsonCache>(); |
Member
There was a problem hiding this comment.
Getting there. Now a few more things:
- Use an LRU cache for package.json entries (set to 128 items)
- Persist the LRU cache in the workspace storage on the
vscode.ExtensionContext(passed to the activate() method)
Contributor
Author
There was a problem hiding this comment.
Done in commit 2a2cf9a. Implemented PackageJsonLRUCache class with:
- 128 entry limit with LRU eviction
- Loads from workspace storage on initialization
- Persists to workspace storage asynchronously on changes
- Initialized in
activate()with theExtensionContext
- Implement PackageJsonLRUCache with 128 entry limit - Load cache from workspace storage on activation - Persist cache changes to workspace storage asynchronously - Initialize cache in activate() with ExtensionContext - LRU eviction: oldest accessed entries removed when cache exceeds limit Co-authored-by: connor4312 <2230985+connor4312@users.noreply.github.com>
connor4312
reviewed
Feb 2, 2026
extensions/npm/src/tasks.ts
Outdated
| // Use workspace.fs.readFile instead of openTextDocument to avoid triggering | ||
| // textDocument/didOpen notifications to language servers | ||
| const content = await workspace.fs.readFile(packageJsonUri); | ||
| const text = Buffer.from(content).toString('utf8'); |
- Replace Buffer.from().toString('utf8') with TextDecoder
- Improves cross-platform compatibility
Co-authored-by: connor4312 <2230985+connor4312@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The npm extension was opening every package.json as a text document during task discovery, triggering
textDocument/didOpen|didCloseto all LSP servers with JSON documentSelector. Large projects with 100+ package.json files would generate 200+ notifications.Changes
Replace
workspace.openTextDocument()withworkspace.fs.readFile()ingetScripts()TextDecoderfor cross-platform portabilityIPositionOffsetTransformerinterface for position/offset transformationPositionOffsetTransformerclass that provides only the minimal required functionality (uri,positionAt())Add LRU cache with workspace storage persistence for parsed package.json scripts
invalidateTasksCache()Implementation
The
IPositionOffsetTransformerinterface defines the minimal contract needed for position/offset conversion. ThePositionOffsetTransformerclass implements this interface, handling both Unix and Windows line endings, using binary search forpositionAt()performance. ThereadScripts()function is refactored to acceptIPositionOffsetTransformerinstead ofTextDocument, maintaining backward compatibility sinceTextDocumentalready implements the required interface.The
PackageJsonLRUCacheclass implements an LRU eviction policy with workspace storage persistence, ensuring the cache survives VS Code restarts while preventing unbounded memory growth in large projects.File content decoding uses the standard
TextDecoderAPI for better portability across Node.js and browser environments.Original prompt
This section details on the original issue you should resolve
<issue_title>documentSelector
jsonfor LSP-Connection will spamtextDocument/didOpen|didCloserequest for allpackage.json</issue_title><issue_description>
Type: Bug
coming from: oxc-project/oxc#17615 (comment)
Possible duplicate, but the server is just waiting for
textDocument/formatting: #238935my extension want to use the following code:
I tried with a new
documentSelectoronclientOptions. With{language: "json"}too, same bug.When the server is started, VS Code will send, for every package.json in the project, a
textDocument/didOpenandtextDocument/didClosenotification to the server. Even if you have only a TypeScript file open.With a big project, this can send over 100 requests to the server.
Without
json, the server is just waiting, after dynamic registrations, for the formatting request.Try it yourself with the oxc git pull request and these settings.
You need to rebase, execute these commands and restart the editor after it:
These are my export from the output channel: Oxc (Fmt).log, after some minutes these will happend again (without doing something): Oxc (Fmt)2.log
VS Code version: Code 1.107.1 (994fd12, 2025-12-17T14:15:14.850Z)
OS version: Windows_NT x64 10.0.26200
Modes:
Remote OS version: Linux x64 6.6.87.2-microsoft-standard-WSL2
System Info
direct_rendering_display_compositor: disabled_off_ok
gpu_compositing: enabled
multiple_raster_threads: enabled_on
opengl: enabled_on
rasterization: enabled
raw_draw: disabled_off_ok
skia_graphite: disabled_off
trees_in_viz: disabled_off
video_decode: enabled
video_encode: enabled
webgl: enabled
webgl2: enabled
webgpu: enabled
webnn: disabled_off
Extensions (39)
A/B Experiments