You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Offload Oxide scanning to separate process (#1471)
Loading Oxide's .node file into the language server process / VSCode
extension host on Windows marks that .node file as in use. When this
happens you cannot completely delete node_modules (for example by
running `npm ci`).
To work around this we'll fork a new process that can load Oxide, run
its scan(s), and then exit. During initial project discovery we use a
temporarily long lived process that persists for the duration of project
discovery — which may include multiple Oxide scans across projects (and
even versions). Once the project discovery has completed the process
will exit. For any subsequent content scanning (e.g. when CSS changes)
we'll spawn a new, temporary process for that individual scan.
This will ensure that, once the process has exited, the `.node` file is
no longer considered to be in-use and commands like `npm ci` will run
properly.
## Commentary
### Why not use `require.cache`?
Unfortunately, deleting entries from `require.cache` does not unload the
Oxide binary from the process address space.
### Why not worker threads
So, this might work but also might not. You'd still be loading it into
the processes address space so there's a chance that as long as the
process is open — whether the thread has exited or not — the `.node`
file would still be marked as in use.
Additionally, we have some flags set when building Oxide that basically
prevent it from unloading in worker threads due to some bugs in the Rust
standard library. This applies to Linux only iirc so it shouldn't
actually be a problem there but I'd rather keep the mechanism working
consistently across operating systems.
### Communication between processes
The main process and helper communicate using a JSON-RPC protocol —
similar to the one used by language servers/clients — but without any
initialization setup. This is an internal tool and the message format is
not considered stable and may change in any future version.
Communication happens over an IPC channel provided by
`child_process.fork(…)`. As far as I am aware, this uses private file
descriptors shared between processes. No other process should be capable
of "tricking" the helper into loading other `.node` files into its
address space. Only the ones we discover during NPM package resolution
should ever be loaded. Even though the temporary helper isn't active for
very long this was still a concern I had while developing this.
## Test Plan
There are automated tests that verify existing functionality still works
but testing this specific scenario on Windows in an automated fashion
with the current test setup would be a bit annoying so I did some
additional manual testing:
- Set up a small Tailwind CSS v4 project on Windows
- Opened VS Code with the current version of the extension
- Ran `npm ci` in the terminal and watched it fail b/c of the Oxide
`.node` file
- Loaded the new extension through the extension development host
- Repeated the steps
- Ran `npm ci` multiple times to make sure it worked
0 commit comments