Last active
April 29, 2022 11:03
-
-
Save patricklx/7ddd056d76c5a7a9b68c480f73ac1eef to your computer and use it in GitHub Desktop.
in memory filesystem for broccolijs
This file contains 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
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