diff options
author | Minteck <nekostarfan@gmail.com> | 2021-08-24 14:41:48 +0200 |
---|---|---|
committer | Minteck <nekostarfan@gmail.com> | 2021-08-24 14:41:48 +0200 |
commit | d25e11bee6ca5ca523884da132d18e1400e077b9 (patch) | |
tree | 8af39fde19f7ed640a60fb397c7edd647dff1c4c /node_modules/nexe/lib/fs | |
download | kartik-iridium-d25e11bee6ca5ca523884da132d18e1400e077b9.tar.gz kartik-iridium-d25e11bee6ca5ca523884da132d18e1400e077b9.tar.bz2 kartik-iridium-d25e11bee6ca5ca523884da132d18e1400e077b9.zip |
Initial commit
Diffstat (limited to 'node_modules/nexe/lib/fs')
-rw-r--r-- | node_modules/nexe/lib/fs/README.md | 40 | ||||
-rw-r--r-- | node_modules/nexe/lib/fs/bootstrap.js | 20 | ||||
-rw-r--r-- | node_modules/nexe/lib/fs/bundle.d.ts | 36 | ||||
-rw-r--r-- | node_modules/nexe/lib/fs/bundle.js | 145 | ||||
-rw-r--r-- | node_modules/nexe/lib/fs/package.json | 15 | ||||
-rw-r--r-- | node_modules/nexe/lib/fs/patch.d.ts | 18 | ||||
-rw-r--r-- | node_modules/nexe/lib/fs/patch.js | 372 |
7 files changed, 646 insertions, 0 deletions
diff --git a/node_modules/nexe/lib/fs/README.md b/node_modules/nexe/lib/fs/README.md new file mode 100644 index 0000000..181c364 --- /dev/null +++ b/node_modules/nexe/lib/fs/README.md @@ -0,0 +1,40 @@ +# nexe-fs + +### Getting Started: + +This module contains a set of patches used to create (and use) the Nexe Virtual FileSystem + +In order for NVFS to work with node's `require` method. A custom build of node must be used. If using the `nexe` cli this will be handled automatically + +The customization should be applied as early as possbile when node is starting up. + +i.e. The contents of `nexe-fs/bootstrap` should be put [here](https://github.com/nodejs/node/blob/0827c80920311fa9d1e6989c8a73aaaeca962eb7/lib/internal/bootstrap/node.js#L27-L28). + +### Creating a NVFS: + +To create a virtual file system use the `Bundle` Object, in your build process: + +```javascript +const { Bundle } = require('nexe-fs') + +const bundle = new Bundle() +await bundle.addResource(absoluteFileName) + +bundle.toStream().pipe(fs.createWriteStream('./myFsBlob')) +``` + +In the entrypoint of your application, the fs patch needs to be applied (executed as early as possible) +For example, In the build process, the application's entrypoint could be prepended with the following: + +```javascript +const codeToPrependToEntrypoint = fs.readFileSync(require.resolve('nexe-fs/patch'), 'utf-8') + ` +shimFs({ + blobPath: 'location/of/myFsBlob', + resources: ${JSON.stringify(bundle)}, + layout: { + resourceStart: 0 // the offset within the blob that is referenced by the bundle index + } +})` +``` + +Since a custom node build is being used. A great place for this code would be the node source file: `_third_party_main.js` diff --git a/node_modules/nexe/lib/fs/bootstrap.js b/node_modules/nexe/lib/fs/bootstrap.js new file mode 100644 index 0000000..a790b91 --- /dev/null +++ b/node_modules/nexe/lib/fs/bootstrap.js @@ -0,0 +1,20 @@ +if (true) { + const __nexe_patches = (process.nexe = { patches: {} }).patches + const slice = [].slice + const __nexe_noop_patch = function (original) { + const args = slice.call(arguments, 1) + return original.apply(this, args) + } + const __nexe_patch = function (obj, method, patch) { + const original = obj[method] + if (!original) return + __nexe_patches[method] = patch + obj[method] = function() { + const args = [original].concat(slice.call(arguments)) + return __nexe_patches[method].apply(this, args) + } + } + __nexe_patch((process).binding('fs'), 'internalModuleReadFile', __nexe_noop_patch) + __nexe_patch((process).binding('fs'), 'internalModuleReadJSON', __nexe_noop_patch) + __nexe_patch((process).binding('fs'), 'internalModuleStat', __nexe_noop_patch) +} diff --git a/node_modules/nexe/lib/fs/bundle.d.ts b/node_modules/nexe/lib/fs/bundle.d.ts new file mode 100644 index 0000000..db298ed --- /dev/null +++ b/node_modules/nexe/lib/fs/bundle.d.ts @@ -0,0 +1,36 @@ +/// <reference types="node" /> +import { Readable } from 'stream'; +import { File } from 'resolve-dependencies'; +export declare type MultiStreams = (Readable | (() => Readable))[]; +export declare function toStream(content: Buffer | string): Readable; +export declare class Bundle { + size: number; + cwd: string; + rendered: boolean; + private offset; + private index; + private files; + streams: MultiStreams; + constructor({ cwd }?: { + cwd: string; + }); + get list(): string[]; + addResource(absoluteFileName: string, content?: File | Buffer | string): Promise<number>; + /** + * De-dupe files by absolute path, partition by symlink/real + * Iterate over real, add entries + * Iterate over symlinks, add symlinks + */ + renderIndex(): { + [key: string]: [number, number]; + }; + /** + * Add a stream if needed and an entry with the required offset and size + * Ensure the calling order of this method is idempotent (eg, while iterating a sorted set) + * @param entryPath + * @param file + * @param useEntry + */ + addEntry(entryPath: string, file: File, useEntry?: string): void; + toStream(): any; +} diff --git a/node_modules/nexe/lib/fs/bundle.js b/node_modules/nexe/lib/fs/bundle.js new file mode 100644 index 0000000..74ed317 --- /dev/null +++ b/node_modules/nexe/lib/fs/bundle.js @@ -0,0 +1,145 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const fs = require("fs"); +const util_1 = require("util"); +const path_1 = require("path"); +const stream_1 = require("stream"); +const MultiStream = require("multistream"); +const lstat = util_1.promisify(fs.lstat), realpath = util_1.promisify(fs.realpath), createReadStream = fs.createReadStream, stat = util_1.promisify(fs.stat); +function makeRelative(cwd, path) { + return './' + path_1.relative(cwd, path); +} +function sortEntry(a, b) { + return a[0] > b[0] ? 1 : -1; +} +function toStream(content) { + const readable = new stream_1.Readable({ + read() { + this.push(content); + this.push(null); + }, + }); + return readable; +} +exports.toStream = toStream; +function createFile(absoluteFileName) { + return __awaiter(this, void 0, void 0, function* () { + const stats = yield lstat(absoluteFileName), file = { + size: stats.size, + contents: '', + absPath: absoluteFileName, + deps: {}, + }; + if (stats.isSymbolicLink()) { + file.size = stats.size; + const [realPath, realStat] = yield Promise.all([ + realpath(absoluteFileName), + stat(absoluteFileName), + ]); + file.realPath = realPath; + file.realSize = realStat.size; + } + return file; + }); +} +class Bundle { + constructor({ cwd } = { cwd: process.cwd() }) { + this.size = 0; + this.rendered = false; + this.offset = 0; + this.index = {}; + this.files = {}; + this.streams = []; + this.cwd = cwd; + } + get list() { + return Object.keys(this.files); + } + addResource(absoluteFileName, content) { + return __awaiter(this, void 0, void 0, function* () { + if (this.files[absoluteFileName]) { + return this.size; + } + if (typeof content === 'string' || Buffer.isBuffer(content)) { + this.files[absoluteFileName] = { + size: Buffer.byteLength(content), + contents: content, + deps: {}, + absPath: absoluteFileName, + }; + } + else if (content) { + this.files[content.absPath] = content; + } + else { + this.files[absoluteFileName] = { + absPath: absoluteFileName, + contents: '', + deps: {}, + size: 0, + }; + this.files[absoluteFileName] = yield createFile(absoluteFileName); + } + return (this.size += this.files[absoluteFileName].size); + }); + } + /** + * De-dupe files by absolute path, partition by symlink/real + * Iterate over real, add entries + * Iterate over symlinks, add symlinks + */ + renderIndex() { + if (this.rendered) { + throw new Error('Bundle index already rendered'); + } + const files = Object.entries(this.files), realFiles = [], symLinks = []; + for (const entry of files) { + if (entry[1].realPath) { + symLinks.push(entry); + } + else { + realFiles.push(entry); + } + } + realFiles.sort(sortEntry); + symLinks.sort(sortEntry); + for (const [absPath, file] of realFiles) { + this.addEntry(absPath, file); + } + for (const [absPath, file] of symLinks) { + this.addEntry(file.realPath, file); + this.addEntry(absPath, file, file.realPath); + } + this.rendered = true; + return this.index; + } + /** + * Add a stream if needed and an entry with the required offset and size + * Ensure the calling order of this method is idempotent (eg, while iterating a sorted set) + * @param entryPath + * @param file + * @param useEntry + */ + addEntry(entryPath, file, useEntry) { + var _a; + const existingName = useEntry && makeRelative(this.cwd, useEntry), name = makeRelative(this.cwd, entryPath), size = (_a = file.realSize) !== null && _a !== void 0 ? _a : file.size, existingEntry = this.index[existingName !== null && existingName !== void 0 ? existingName : name]; + this.index[name] = existingEntry || [this.offset, size]; + if (!existingEntry) { + this.streams.push(() => file.contents ? toStream(file.contents) : createReadStream(file.absPath)); + this.offset += size; + } + } + toStream() { + return new MultiStream(this.streams); + } +} +exports.Bundle = Bundle; diff --git a/node_modules/nexe/lib/fs/package.json b/node_modules/nexe/lib/fs/package.json new file mode 100644 index 0000000..c830c02 --- /dev/null +++ b/node_modules/nexe/lib/fs/package.json @@ -0,0 +1,15 @@ +{ + "name": "nexe-fs", + "version": "1.0.1", + "description": "Virtual File System used by nexe", + "main": "bundle.js", + "files": [ + "*.d.ts", + "*.js" + ], + "author": "Caleb Boyd", + "dependencies": { + "multistream": "^2.1.1" + }, + "license": "MIT" +} diff --git a/node_modules/nexe/lib/fs/patch.d.ts b/node_modules/nexe/lib/fs/patch.d.ts new file mode 100644 index 0000000..82c1570 --- /dev/null +++ b/node_modules/nexe/lib/fs/patch.d.ts @@ -0,0 +1,18 @@ +/// <reference types="node" /> +import { Stats } from 'fs'; +export interface NexeBinary { + blobPath: string; + resources: { + [key: string]: number[]; + }; + layout: { + stat: Stats; + resourceStart: number; + contentSize?: number; + contentStart?: number; + resourceSize?: number; + }; +} +declare function shimFs(binary: NexeBinary, fs?: any): true | undefined; +declare function restoreFs(): void; +export { shimFs, restoreFs }; diff --git a/node_modules/nexe/lib/fs/patch.js b/node_modules/nexe/lib/fs/patch.js new file mode 100644 index 0000000..14a5a60 --- /dev/null +++ b/node_modules/nexe/lib/fs/patch.js @@ -0,0 +1,372 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +let originalFsMethods = null; +let lazyRestoreFs = () => { }; +// optional Win32 file namespace prefix followed by drive letter and colon +const windowsFullPathRegex = /^(\\{2}\?\\)?([a-zA-Z]):/; +const upcaseDriveLetter = (s) => s.replace(windowsFullPathRegex, (_match, ns, drive) => `${ns || ''}${drive.toUpperCase()}:`); +function shimFs(binary, fs = require('fs')) { + if (originalFsMethods !== null) { + return; + } + originalFsMethods = Object.assign({}, fs); + const { blobPath, resources: manifest } = binary, { resourceStart, stat } = binary.layout, directories = {}, notAFile = '!@#$%^&*', isWin = process.platform.startsWith('win'), isString = (x) => typeof x === 'string' || x instanceof String, noop = () => { }, path = require('path'), winPath = isWin ? upcaseDriveLetter : (s) => s, baseDir = winPath(path.dirname(process.execPath)); + let log = (_) => true; + let loggedManifest = false; + if ((process.env.DEBUG || '').toLowerCase().includes('nexe:require')) { + log = (text) => { + setupManifest(); + if (!loggedManifest) { + process.stderr.write('[nexe] - MANIFEST' + JSON.stringify(manifest, null, 4) + '\n'); + process.stderr.write('[nexe] - DIRECTORIES' + JSON.stringify(directories, null, 4) + '\n'); + loggedManifest = true; + } + return process.stderr.write('[nexe] - ' + text + '\n'); + }; + } + const getKey = function getKey(filepath) { + if (Buffer.isBuffer(filepath)) { + filepath = filepath.toString(); + } + if (!isString(filepath)) { + return notAFile; + } + let key = path.resolve(baseDir, filepath); + return winPath(key); + }; + const statTime = function () { + return { + dev: 0, + ino: 0, + nlink: 0, + rdev: 0, + uid: 123, + gid: 500, + blksize: 4096, + blocks: 0, + atime: new Date(stat.atime), + atimeMs: stat.atime.getTime(), + mtime: new Date(stat.mtime), + mtimeMs: stat.mtime.getTime(), + ctime: new Date(stat.ctime), + ctimMs: stat.ctime.getTime(), + birthtime: new Date(stat.birthtime), + birthtimeMs: stat.birthtime.getTime(), + }; + }; + let BigInt; + try { + BigInt = eval('BigInt'); + } + catch (ignored) { } + const createStat = function (extensions, options) { + const stat = Object.assign(new fs.Stats(), binary.layout.stat, statTime(), extensions); + if (options && options.bigint && BigInt) { + for (const k in stat) { + if (Object.prototype.hasOwnProperty.call(stat, k) && typeof stat[k] === 'number') { + stat[k] = BigInt(stat[k]); + } + } + } + return stat; + }; + const ownStat = function (filepath, options) { + setupManifest(); + const key = getKey(filepath); + if (directories[key]) { + let mode = binary.layout.stat.mode; + mode |= fs.constants.S_IFDIR; + mode &= ~fs.constants.S_IFREG; + return createStat({ mode, size: 0 }, options); + } + if (manifest[key]) { + return createStat({ size: manifest[key][1] }, options); + } + }; + const getStat = function (fn) { + return function stat(filepath, options, callback) { + let stat; + if (typeof options === 'function') { + callback = options; + stat = ownStat(filepath, null); + } + else { + stat = ownStat(filepath, options); + } + if (stat) { + process.nextTick(() => { + callback(null, stat); + }); + } + else { + return originalFsMethods[fn].apply(fs, arguments); + } + }; + }; + function makeLong(filepath) { + return path._makeLong && path._makeLong(filepath); + } + function fileOpts(options) { + return !options ? {} : isString(options) ? { encoding: options } : options; + } + let setupManifest = () => { + Object.keys(manifest).forEach((filepath) => { + const entry = manifest[filepath]; + const absolutePath = getKey(filepath); + const longPath = makeLong(absolutePath); + const normalizedPath = winPath(path.normalize(filepath)); + if (!manifest[absolutePath]) { + manifest[absolutePath] = entry; + } + if (longPath && !manifest[longPath]) { + manifest[longPath] = entry; + } + if (!manifest[normalizedPath]) { + manifest[normalizedPath] = manifest[filepath]; + } + let currentDir = path.dirname(absolutePath); + let prevDir = absolutePath; + while (currentDir !== prevDir) { + directories[currentDir] = directories[currentDir] || {}; + directories[currentDir][path.basename(prevDir)] = true; + const longDir = makeLong(currentDir); + if (longDir && !directories[longDir]) { + directories[longDir] = directories[currentDir]; + } + prevDir = currentDir; + currentDir = path.dirname(currentDir); + } + }); + manifest[notAFile] = false; + directories[notAFile] = false; + setupManifest = noop; + }; + //naive patches intended to work for most use cases + const nfs = { + existsSync: function existsSync(filepath) { + setupManifest(); + const key = getKey(filepath); + if (manifest[key] || directories[key]) { + return true; + } + return originalFsMethods.existsSync.apply(fs, arguments); + }, + realpath: function realpath(filepath, options, cb) { + setupManifest(); + const key = getKey(filepath); + if (isString(filepath) && (manifest[filepath] || manifest[key])) { + return process.nextTick(() => cb(null, filepath)); + } + return originalFsMethods.realpath.call(fs, filepath, options, cb); + }, + realpathSync: function realpathSync(filepath, options) { + setupManifest(); + const key = getKey(filepath); + if (manifest[key]) { + return filepath; + } + return originalFsMethods.realpathSync.call(fs, filepath, options); + }, + readdir: function readdir(filepath, options, callback) { + setupManifest(); + const dir = directories[getKey(filepath)]; + if (dir) { + if ('function' === typeof options) { + callback = options; + options = { encoding: 'utf8' }; + } + process.nextTick(() => callback(null, Object.keys(dir))); + } + else { + return originalFsMethods.readdir.apply(fs, arguments); + } + }, + readdirSync: function readdirSync(filepath, options) { + setupManifest(); + const dir = directories[getKey(filepath)]; + if (dir) { + return Object.keys(dir); + } + return originalFsMethods.readdirSync.apply(fs, arguments); + }, + readFile: function readFile(filepath, options, callback) { + setupManifest(); + const entry = manifest[getKey(filepath)]; + if (!entry) { + return originalFsMethods.readFile.apply(fs, arguments); + } + const [offset, length] = entry; + const resourceOffset = resourceStart + offset; + const encoding = fileOpts(options).encoding; + callback = typeof options === 'function' ? options : callback; + originalFsMethods.open(blobPath, 'r', function (err, fd) { + if (err) + return callback(err, null); + originalFsMethods.read(fd, Buffer.alloc(length), 0, length, resourceOffset, function (error, bytesRead, result) { + if (error) { + return originalFsMethods.close(fd, function () { + callback(error, null); + }); + } + originalFsMethods.close(fd, function (err) { + if (err) { + return callback(err, result); + } + callback(err, encoding ? result.toString(encoding) : result); + }); + }); + }); + }, + createReadStream: function createReadStream(filepath, options) { + setupManifest(); + const entry = manifest[getKey(filepath)]; + if (!entry) { + return originalFsMethods.createReadStream.apply(fs, arguments); + } + const [offset, length] = entry; + const resourceOffset = resourceStart + offset; + const opts = fileOpts(options); + return originalFsMethods.createReadStream(blobPath, Object.assign({}, opts, { + start: resourceOffset, + end: resourceOffset + length - 1, + })); + }, + readFileSync: function readFileSync(filepath, options) { + setupManifest(); + const entry = manifest[getKey(filepath)]; + if (!entry) { + return originalFsMethods.readFileSync.apply(fs, arguments); + } + const [offset, length] = entry; + const resourceOffset = resourceStart + offset; + const encoding = fileOpts(options).encoding; + const fd = originalFsMethods.openSync(process.execPath, 'r'); + const result = Buffer.alloc(length); + originalFsMethods.readSync(fd, result, 0, length, resourceOffset); + originalFsMethods.closeSync(fd); + return encoding ? result.toString(encoding) : result; + }, + statSync: function statSync(filepath, options) { + const stat = ownStat(filepath, options); + if (stat) { + return stat; + } + return originalFsMethods.statSync.apply(fs, arguments); + }, + stat: getStat('stat'), + lstat: getStat('lstat'), + lstatSync: function statSync(filepath, options) { + const stat = ownStat(filepath, options); + if (stat) { + return stat; + } + return originalFsMethods.lstatSync.apply(fs, arguments); + }, + }; + if (typeof fs.exists === 'function') { + nfs.exists = function (filepath, cb) { + cb = cb || noop; + const exists = nfs.existsSync(filepath); + process.nextTick(() => cb(exists)); + }; + } + const patches = process.nexe.patches || {}; + delete process.nexe; + patches.internalModuleReadFile = function (original, ...args) { + setupManifest(); + const filepath = getKey(args[0]); + if (manifest[filepath]) { + log('read (hit) ' + filepath); + return nfs.readFileSync(filepath, 'utf-8'); + } + log('read (miss) ' + filepath); + return original.call(this, ...args); + }; + let returningArray; + patches.internalModuleReadJSON = function (original, ...args) { + if (returningArray == null) + returningArray = Array.isArray(original.call(this, '')); + const res = patches.internalModuleReadFile.call(this, original, ...args); + return returningArray && !Array.isArray(res) + ? [res, /"(main|name|type|exports|imports)"/.test(res)] + : res; + }; + patches.internalModuleStat = function (original, ...args) { + setupManifest(); + const filepath = getKey(args[0]); + if (manifest[filepath]) { + log('stat (hit) ' + filepath + ' ' + 0); + return 0; + } + if (directories[filepath]) { + log('stat dir (hit) ' + filepath + ' ' + 1); + return 1; + } + const res = original.call(this, ...args); + if (res === 0) { + log('stat (miss) ' + filepath + ' ' + res); + } + else if (res === 1) { + log('stat dir (miss) ' + filepath + ' ' + res); + } + else { + log('stat (fail) ' + filepath + ' ' + res); + } + return res; + }; + if (typeof fs.exists === 'function') { + nfs.exists = function (filepath, cb) { + cb = cb || noop; + const exists = nfs.existsSync(filepath); + if (!exists) { + return originalFsMethods.exists(filepath, cb); + } + process.nextTick(() => cb(exists)); + }; + } + if (typeof fs.copyFile === 'function') { + nfs.copyFile = function (filepath, dest, flags, callback) { + setupManifest(); + const entry = manifest[getKey(filepath)]; + if (!entry) { + return originalFsMethods.copyFile.apply(fs, arguments); + } + if (typeof flags === 'function') { + callback = flags; + flags = 0; + } + nfs.readFile(filepath, (err, buffer) => { + if (err) { + return callback(err); + } + originalFsMethods.writeFile(dest, buffer, (err) => { + if (err) { + return callback(err); + } + callback(null); + }); + }); + }; + nfs.copyFileSync = function (filepath, dest) { + setupManifest(); + const entry = manifest[getKey(filepath)]; + if (!entry) { + return originalFsMethods.copyFileSync.apply(fs, arguments); + } + return originalFsMethods.writeFileSync(dest, nfs.readFileSync(filepath)); + }; + } + Object.assign(fs, nfs); + lazyRestoreFs = () => { + Object.keys(nfs).forEach((key) => { + fs[key] = originalFsMethods[key]; + }); + lazyRestoreFs = () => { }; + }; + return true; +} +exports.shimFs = shimFs; +function restoreFs() { + lazyRestoreFs(); +} +exports.restoreFs = restoreFs; |