Skip to content

Instantly share code, notes, and snippets.

@patricklx
Last active April 29, 2022 11:03
Show Gist options
  • Save patricklx/7ddd056d76c5a7a9b68c480f73ac1eef to your computer and use it in GitHub Desktop.
Save patricklx/7ddd056d76c5a7a9b68c480f73ac1eef to your computer and use it in GitHub Desktop.
in memory filesystem for broccolijs
if (true) {
try {
const fs = require('fs');
const nodePath = require('path');
const memfs = require('memfs');
const mem = memfs.fs;
// fs is missing vol.rmSync...
mem.rmSync = memfs.vol.rmSync.bind(memfs.vol);
const os = require('os');
mem.mkdirpSync(os.tmpdir());
const nodefs = Object.assign({}, fs);
nodefs.promises = Object.assign({}, fs.promises);
const tmpDir = os.tmpdir().replace(/\\/g, '/').replace('C:', '') + '/broccoli-';
let cwdInTemp = false;
const isTmpDir = (path) => {
if (typeof path === 'string') {
if (path.includes(tmpDir) || path.startsWith('mem-idx-')) return true;
if (cwdInTemp) {
if (path.startsWith('.')) return true;
if (path.startsWith('/')) return false;
if (path.startsWith('c:/')) return false;
if (path.startsWith('C:/')) return false;
return true;
}
}
return false;
};
const fixPath = (path) => {
path = path.replace('\\\\?\\', '');
path = path.replace(/\\/g, '/');
if (path.endsWith('/')) {
path = path.slice(0, -1);
}
if (path.endsWith('/.')) {
path = path.slice(0, -2);
}
if (cwdInTemp && !path.startsWith('mem-idx-')) {
if (path.startsWith('.')) return cwdInTemp + path;
if (path.startsWith('/')) return path;
if (path.startsWith('c:/')) return path;
if (path.startsWith('C:/')) return path;
return cwdInTemp + path;
}
return path;
};
const resolvePathSymlinks = (path, skipLast) => {
if (path.startsWith('mem-idx-')) {
return Number(path.replace('mem-idx-', ''));
}
let steps = path.split('/');
let last = '';
if (skipLast) {
last = steps.slice(-1)[0];
steps.splice(-1, 1);
}
let fullPathParts = [];
let tmpDir = os.tmpdir().replace(/\\/g, '/');
if (tmpDir.endsWith('/')) {
tmpDir = tmpDir.slice(0, -1);
}
if (path.startsWith(tmpDir)) {
fullPathParts.push(tmpDir);
steps = path.replace(tmpDir + '/', '').split('/');
if (skipLast) {
steps.splice(-1, 1);
}
}
while (steps.length) {
const step = steps.shift(0);
fullPathParts.push(step);
const fullPath = fullPathParts.join('/');
const f = isTmpDir(fullPath) ? mem : null;
if (f && f.lstatSync(fullPath, { throwIfNoEntry: false })?.isSymbolicLink()) {
let lnk = f.readlinkSync(fullPath);
if (lnk.startsWith('/')) {
lnk = 'C:' + lnk;
}
steps.splice(0, 0, ...lnk.replace(tmpDir + '/', '').split('/'));
fullPathParts = [];
if (lnk.startsWith(tmpDir)) {
fullPathParts.push(tmpDir);
}
}
}
if (last) {
fullPathParts.push(last);
}
return fullPathParts.join('/');
};
const own = {
symlink(path, ...args) {
const cb = args.slice(-1)[0];
try {
const r = this.symlinkSync(path, ...args);
cb(null, r);
} catch (e) {
cb(e);
}
},
symlinkSync(target, path, opts) {
path = path.replace('\\\\?\\', '');
path = path.replace(/\\/g, '/');
if (path.endsWith('/')) {
path = path.slice(0, -1);
}
if (isTmpDir(path)) {
mem.symlinkSync(target, path, opts);
return;
}
return nodefs.symlinkSync(target, path, opts);
},
readlinkSync(path, opts) {
if (isTmpDir(path)) {
let lnk = mem.readlinkSync(path, opts);
if (lnk.startsWith('/')) {
lnk = 'C:' + lnk;
}
return lnk;
}
return nodefs.readlinkSync(path, opts);
},
unlinkSync(path) {
if (isTmpDir(path)) {
return mem.unlinkSync(path);
}
if (!path.startsWith(__dirname.replace(/\\/g, '/')+ '/dist') && !path.startsWith(os.tmpdir().replace(/\\/g, '/'))) {
throw new Error('should not delete outside temp or dist ' + path);
}
return nodefs.unlinkSync(path);
},
readdirSync(path, opts) {
if (isTmpDir(path)) {
if (mem.lstatSync(path).isSymbolicLink()) {
const lnk = this.readlinkSync(path);
return this.readdirSync(lnk, opts);
}
return mem.readdirSync(path, opts);
}
return nodefs.readdirSync(path, opts);
},
realpath(path, ...args) {
const cb = args.slice(-1)[0];
try {
const r = this.realpathSync(path, ...args);
cb(null, r);
} catch (e) {
cb(e);
}
},
realpathSync(path, opts) {
if (isTmpDir(path)) {
if (mem.lstatSync(path).isSymbolicLink()) {
const lnk = this.readlinkSync(path);
return this.realpathSync(lnk, opts);
}
let resP = mem.realpathSync(path, opts);
if (resP.startsWith('/')) {
resP = 'C:' + resP;
}
return resP;
}
return nodefs.realpathSync(path, opts);
},
stat(path, ...args) {
const cb = args.slice(-1)[0];
try {
const r = this.statSync(path, ...args);
cb(null, r);
} catch (e) {
cb(e);
}
},
existsSync(path) {
try {
path = this.readlinkSync(path);
} catch (e) {
// pass
}
if (isTmpDir(path)) {
return mem.existsSync(path);
}
return nodefs.existsSync(path);
},
statSync(path, ...args) {
if (isTmpDir(path)) {
if (mem.lstatSync(path, ...args)?.isSymbolicLink()) {
const lnk = this.readlinkSync(path);
return this.statSync(lnk, ...args);
}
return mem.statSync(path, ...args);
}
return nodefs.statSync(path, ...args);
},
openSync(path, ...args) {
if (isTmpDir(path)) {
return 'mem-idx-' + mem.openSync(path, ...args);
}
return nodefs.openSync(path, ...args);
}
};
// fix for rollup
const chdir = process.chdir;
process.chdir = function(path) {
path = fixPath(path);
if (isTmpDir(path)) {
if (!path.endsWith('/')) {
path += '/';
}
cwdInTemp = path;
} else {
cwdInTemp = false;
return chdir(path);
}
};
const skip = ['ReadStream', 'WriteStream', 'Stats'];
Object.keys(fs).forEach((k) => {
if (skip.includes(k)) return;
if (k.includes('_')) return;
const x = (path, ...args) => {
if (typeof path === 'string') {
path = fixPath(path);
}
const wasTmp = isTmpDir(path);
if (wasTmp) {
path = resolvePathSymlinks(path, k.startsWith('lstat') || k.startsWith('rm') || k.startsWith('unlink') || k.startsWith('readlink'));
}
if (own[k]) {
return own[k](path, ...args);
}
if (isTmpDir(path) || (wasTmp && Number.isInteger(path))) {
return mem[k](path, ...args);
}
return nodefs[k](path, ...args);
};
console.log('patch fs', k);
fs[k] = x;
});
// fix for rollup
fs.promises.lstat = (...args) => new Promise(resolve => resolve(fs.lstatSync(...args)));
fs.promises.stat = (...args) => new Promise(resolve => resolve(fs.statSync(...args)));
fs.promises.readdir = (...args) => new Promise(resolve => resolve(fs.readdirSync(...args)));
fs.promises.realpath = (...args) => new Promise(resolve => resolve(fs.realpathSync(...args)));
fs.promises.readFile = (...args) => new Promise(resolve => resolve(fs.readFileSync(...args)));
fs.promises.writeFile = (...args) => new Promise(resolve => resolve(fs.writeFileSync(...args)));
fs.promises.mkdir = (...args) => new Promise(resolve => resolve(fs.mkdirSync(...args)));
const resolve = nodePath.resolve;
nodePath.resolve = function(...paths) {
const path = paths.join('/');
if (cwdInTemp && !path.startsWith('/') && !path.startsWith('C:')) {
return resolve(cwdInTemp, ...paths);
}
return resolve(...paths);
};
} catch (e) {
console.log(e);
throw e;
}
}
require.cache = new Proxy(require.cache, {
deleteProperty: (target, prop) => console.log(new Error('trying to delete cache for ' + prop).stack)
});
require('ember-cli/bin/ember');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment