@fkn/vite-plugin resolves Node’s fs, net, dgram and http to the matching @fkn/lib entries, so code written for Node keeps its imports and runs in the browser on FKN. This page covers what the plugin wires, the shims it supplies, the options that switch an alias off, and what to use without it.
The plugin is a build-time dependency and the library is a run-time one:
Terminal window
npminstall@fkn/lib
npminstall--save-dev@fkn/vite-plugin
@fkn/vite-plugin 0.3.1 needs @fkn/lib 0.5.1 or newer and Vite 5 or newer as peers. It is one entry in your Vite plugins:
For detailed information, see the documentation of the asynchronous version of
this API:
exists
.
fs.exists() is deprecated, but fs.existsSync() is not. The callback parameter to fs.exists() accepts parameters that are inconsistent with other
Node.js callbacks. fs.existsSync() does not use a callback.
import { existsSync } from'node:fs';
if (existsSync('/etc/passwd'))
console.log('The path exists.');
@since ― v0.1.21
existsSync('library/catalog.json') // true, this tab wrote it
Sends data on the socket. The second parameter specifies the encoding in the
case of a string. It defaults to UTF8 encoding.
Returns true if the entire data was flushed successfully to the kernel
buffer. Returns false if all or part of the data was queued in user memory.'drain' will be emitted when the buffer is again free.
The optional callback parameter will be executed when the data is finally
written out, which may not be immediately.
See Writable stream write() method for more
information.
Adds the listener function to the end of the listeners array for the
event named eventName. No checks are made to see if the listener has
already been added. Multiple calls passing the same combination of eventName
and listener will result in the listener being added, and called, multiple
times.
server.on('connection', (stream) => {
console.log('someone connected!');
});
Returns a reference to the EventEmitter, so that calls can be chained.
By default, event listeners are invoked in the order they are added. The
emitter.prependListener() method can be used as an alternative to add the
event listener to the beginning of the listeners array.
Adds the listener function to the end of the listeners array for the
event named eventName. No checks are made to see if the listener has
already been added. Multiple calls passing the same combination of eventName
and listener will result in the listener being added, and called, multiple
times.
server.on('connection', (stream) => {
console.log('someone connected!');
});
Returns a reference to the EventEmitter, so that calls can be chained.
By default, event listeners are invoked in the order they are added. The
emitter.prependListener() method can be used as an alternative to add the
event listener to the beginning of the listeners array.
Decodes buf to a string according to the specified character encoding inencoding. start and end may be passed to decode only a subset of buf.
If encoding is 'utf8' and a byte sequence in the input is not valid UTF-8,
then each invalid byte is replaced with the replacement character U+FFFD.
The maximum length of a string instance (in UTF-16 code units) is available
as
constants.MAX_STRING_LENGTH
.
import { Buffer } from'node:buffer';
constbuf1= Buffer.allocUnsafe(26);
for (let i =0; i <26; i++) {
// 97 is the decimal ASCII value for 'a'.
buf1[i] = i +97;
}
console.log(buf1.toString('utf8'));
// Prints: abcdefghijklmnopqrstuvwxyz
console.log(buf1.toString('utf8', 0, 5));
// Prints: abcde
constbuf2= Buffer.from('tést');
console.log(buf2.toString('hex'));
// Prints: 74c3a97374
console.log(buf2.toString('utf8', 0, 3));
// Prints: té
console.log(buf2.toString(undefined, 0, 3));
// Prints: té
@since ― v0.1.90
@param ― encoding The character encoding to use.
@param ― start The byte offset to start decoding at.
@param ― end The byte offset to stop decoding at (not inclusive).
toString('utf8')) // a Buffer per read, in arrival order
node:fs is @fkn/lib/fs at run time and node:net is @fkn/lib/net. The types stay Node’s, from whatever @types/node your app has, and they declare more than the library implements. What node:fs covers is on Node fs polyfill, and the mount() call a synchronous read needs is on mount() before a synchronous read. Where a write goes after the in-memory layer is on storage, and what net and dgram carry is on TCP and UDP sockets.
Each FKN alias is an exact match on the specifier, in its bare and node: forms. Every other builtin that node-stdlib-browser covers falls through to that package:
the builtins node-stdlib-browser covers, with their node: forms
that package’s entry
The FKN entries come first because the first match wins, and an exact match is what leaves fs-extra alone. The fallback covers that package’s map and nothing more: worker_threads, perf_hooks and fs/promises have no entry there.
dns and https have no FKN alias. dns falls through to an empty module, so import dns from 'node:dns' compiles and fails at the first call. Import @fkn/lib/dns by name instead, see HTTP and DNS.
https falls through to https-browserify, which requires http, so the http alias sends that require to @fkn/lib/http. Nothing in the library speaks TLS, so treat https as absent. Fetch an HTTPS resource with cloud.fetch, see HTTP and DNS.
The plugin applies the same overrides inside Vite’s dependency optimizer, which resolves before the alias-aware resolver runs. A package in node_modules that imports net therefore lands on the same @fkn/lib as your own code, see bundle an app that uses sockets.
Under the alias an import keeps Node’s types, so Node’s overloads compile where the library’s own declarations refuse them:
Adds the listener function to the end of the listeners array for the
event named eventName. No checks are made to see if the listener has
already been added. Multiple calls passing the same combination of eventName
and listener will result in the listener being added, and called, multiple
times.
server.on('connection', (stream) => {
console.log('someone connected!');
});
Returns a reference to the EventEmitter, so that calls can be chained.
By default, event listeners are invoked in the order they are added. The
emitter.prependListener() method can be used as an alternative to add the
event listener to the beginning of the listeners array.
The numeric representation of the remote port. For example, 80 or 21. Value may be undefined if
the socket is destroyed (for example, if the client disconnected).
@since ― v0.5.10
remotePort) // 80, attached here rather than passed as a third argument
Adds the listener function to the end of the listeners array for the
event named eventName. No checks are made to see if the listener has
already been added. Multiple calls passing the same combination of eventName
and listener will result in the listener being added, and called, multiple
times.
server.on('connection', (stream) => {
console.log('someone connected!');
});
Returns a reference to the EventEmitter, so that calls can be chained.
By default, event listeners are invoked in the order they are added. The
emitter.prependListener() method can be used as an alternative to add the
event listener to the beginning of the listeners array.
connect('/run/app.sock') // throws Error('FKN WebVPN does not support IPC connections')
Three forms reach the library’s connect: the options form, port with host, and port alone. The library handles Node’s positional forms, and port alone targets localhost, see TCP and UDP sockets. The path form is the one Node overload that compiles under the alias and throws at run time, with FKN WebVPN does not support IPC connections.
A listener passed after port and host never runs, because net.connect and createConnection forward only their first two arguments. net.connect(80, 'example.org', listener) connects and drops the listener silently. Pass the options form, attach socket.on('connect', ...) as above, or call new net.Socket().connect(80, 'example.org', listener), since the method keeps all three.
The published library imports buffer, events and stream as bare specifiers: net needs events and stream, dgram needs buffer and events, http needs all three, and fs needs buffer. A browser bundle has to provide the three modules and the globals the shims read, and the plugin does both.
The modules come from the fallback half of the alias table, buffer to the buffer package, events to events and stream to stream-browserify, inside the dependency optimizer as well.
The globals matter because the stream shim reads global at module scope. fkn() returns two plugins, and both supply them. The first lists buffer and process in optimizeDeps.include and injects the node-stdlib-browser shim during dependency optimization, with global, process and Buffer defined. The second is @rollup/plugin-inject enforced post, which injects the same three names into every module it transforms.
stream-http, a client over the browser’s own requests rather than an FKN socket. See HTTP and DNS.
The empty module exports a default of null and nothing else, so import fs from 'fs' with the alias off compiles and every call on it throws. fs: false also drops the fs/promises alias, and the fallback has no entry for that name, so an app that imports fs/promises keeps fs on.
The options go to the fkn() call, one flag per alias:
Type helper to make it easier to use vite.config.ts
accepts a direct
UserConfig
object, or a function that returns it.
The function receives a
ConfigEnv
object.
defineConfig({
UserConfig.plugins?: PluginOption[] |undefined
Array of vite plugins to use.
plugins: [
functionfkn(options?: {
fs?:boolean;
net?:boolean;
dgram?:boolean;
http?:boolean;
}):Plugin<any>[]
fkn({
fs?: boolean |undefined
fs: false })], // net, dgram and http still resolve to @fkn/lib, fs does not
})
That call keeps the three socket aliases and leaves the file system to another package. fkn({ net: false, dgram: false, http: false }) is the other direction, the file system alone. The shims stay in place either way.
Without the plugin your code imports @fkn/lib/net and @fkn/lib/fs by name, with the library’s declarations instead of Node’s, and your bundle supplies the three shims itself. install shows the vite-plugin-node-polyfills configuration that does, scoped to the three bare names, and says when your own node: imports need more.
@fkn/net, @fkn/dgram and @fkn/fs wrap the same entries in Node’s own types, for code written against @types/node. Each one pins @fkn/lib to exactly 0.9.24, so an app on a different version carries two copies of the library. @fkn/fs on that route is described on Node fs polyfill.