add indexeddb

github
bunsenstraat 3 years ago
parent f021dd5f21
commit 9de52d7d27
  1. 24
      apps/remix-ide/src/app/files/dgitProvider.js
  2. 5
      apps/remix-ide/src/app/files/fileManager.js
  3. 197
      apps/remix-ide/src/app/files/fileProvider.js
  4. 29
      apps/remix-ide/src/assets/js/fileSystem.js
  5. 1
      apps/remix-ide/src/assets/js/lightning-fs.min.js
  6. 43
      apps/remix-ide/src/index.html

@ -51,8 +51,8 @@ class DGitProvider extends Plugin {
async getGitConfig () { async getGitConfig () {
const workspace = await this.call('filePanel', 'getCurrentWorkspace') const workspace = await this.call('filePanel', 'getCurrentWorkspace')
return { return {
fs: window.remixFileSystem, fs: window.remixFileSystemCallback,
dir: workspace.absolutePath dir: addSlash(workspace.absolutePath) + '/'
} }
} }
@ -82,10 +82,11 @@ class DGitProvider extends Plugin {
...await this.getGitConfig(), ...await this.getGitConfig(),
...cmd ...cmd
}) })
console.log("STATUS", status, await this.getGitConfig())
return status return status
} }
async add (cmd) { async add(cmd) {
await git.add({ await git.add({
...await this.getGitConfig(), ...await this.getGitConfig(),
...cmd ...cmd
@ -299,7 +300,7 @@ class DGitProvider extends Plugin {
const files = await this.getDirectory('/') const files = await this.getDirectory('/')
this.filesToSend = [] this.filesToSend = []
for (const file of files) { for (const file of files) {
const c = window.remixFileSystem.readFileSync(`${workspace.absolutePath}/${file}`) const c = await window.remixFileSystem.readFile(addSlash(`${workspace.absolutePath}/${file}`))
const ob = { const ob = {
path: file, path: file,
content: c content: c
@ -320,7 +321,7 @@ class DGitProvider extends Plugin {
const data = new FormData() const data = new FormData()
files.forEach(async (file) => { files.forEach(async (file) => {
const c = window.remixFileSystem.readFileSync(`${workspace.absolutePath}/${file}`) const c = window.remixFileSystem.readFileSync(addSlash(`${workspace.absolutePath}/${file}`))
data.append('file', new Blob([c]), `base/${file}`) data.append('file', new Blob([c]), `base/${file}`)
}) })
// get last commit data // get last commit data
@ -431,7 +432,7 @@ class DGitProvider extends Plugin {
this.createDirectories(`${workspace.absolutePath}/${dir}`) this.createDirectories(`${workspace.absolutePath}/${dir}`)
} catch (e) { throw new Error(e) } } catch (e) { throw new Error(e) }
try { try {
window.remixFileSystem.writeFileSync(`${workspace.absolutePath}/${file.path}`, Buffer.concat(content) || new Uint8Array()) await window.remixFileSystem.writeFile(addSlash(`${workspace.absolutePath}/${file.path}`, Buffer.concat(content) || new Uint8Array()))
} catch (e) { throw new Error(e) } } catch (e) { throw new Error(e) }
} }
} catch (e) { } catch (e) {
@ -495,7 +496,7 @@ class DGitProvider extends Plugin {
const files = await this.getDirectory('/') const files = await this.getDirectory('/')
this.filesToSend = [] this.filesToSend = []
for (const file of files) { for (const file of files) {
const c = window.remixFileSystem.readFileSync(`${workspace.absolutePath}/${file}`) const c = await window.remixFileSystem.readFile(addSlash(`${workspace.absolutePath}/${file}`))
zip.file(file, c) zip.file(file, c)
} }
await zip.generateAsync({ await zip.generateAsync({
@ -515,8 +516,8 @@ class DGitProvider extends Plugin {
if (i > 0) previouspath = '/' + directories.slice(0, i).join('/') if (i > 0) previouspath = '/' + directories.slice(0, i).join('/')
const finalPath = previouspath + '/' + directories[i] const finalPath = previouspath + '/' + directories[i]
try { try {
if (!window.remixFileSystem.existsSync(finalPath)) { if (!await window.remixFileSystem.exists(addSlash(finalPath))) {
window.remixFileSystem.mkdirSync(finalPath) await window.remixFileSystem.mkdir(addSlash(finalPath))
} }
} catch (e) { } catch (e) {
console.log(e) console.log(e)
@ -547,6 +548,11 @@ class DGitProvider extends Plugin {
} }
} }
const addSlash = (file) => {
if (!file.startsWith('/'))file = '/' + file
return file
}
const normalize = (filesList) => { const normalize = (filesList) => {
const folders = [] const folders = []
const files = [] const files = []

@ -148,10 +148,9 @@ class FileManager extends Plugin {
* @param {string} path path of the directory or file * @param {string} path path of the directory or file
* @returns {boolean} true if path is a file. * @returns {boolean} true if path is a file.
*/ */
isFile (path) { async isFile (path) {
const provider = this.fileProviderOf(path) const provider = this.fileProviderOf(path)
const result = provider.isFile(path) const result = await provider.isFile(path)
return result return result
} }

@ -72,48 +72,50 @@ class FileProvider {
async exists (path) { async exists (path) {
// todo check the type (directory/file) as well #2386 // todo check the type (directory/file) as well #2386
// currently it is not possible to have a file and folder with same path // currently it is not possible to have a file and folder with same path
const ret = this._exists(path) const ret = await this._exists(path)
return ret return ret
} }
_exists (path) { async _exists (path) {
path = this.getPathFromUrl(path) || path // ensure we actually use the normalized path from here path = this.getPathFromUrl(path) || path // ensure we actually use the normalized path from here
var unprefixedpath = this.removePrefix(path) var unprefixedpath = this.removePrefix(path)
return path === this.type ? true : window.remixFileSystem.existsSync(unprefixedpath) console.log(unprefixedpath)
return path === this.type ? true : await window.remixFileSystem.exists(this.addSlash(unprefixedpath))
} }
init (cb) { init (cb) {
cb() cb()
} }
get (path, cb) { async get (path, cb) {
cb = cb || function () {}
path = this.getPathFromUrl(path) || path // ensure we actually use the normalized path from here path = this.getPathFromUrl(path) || path // ensure we actually use the normalized path from here
var unprefixedpath = this.removePrefix(path) var unprefixedpath = this.removePrefix(path)
var exists = window.remixFileSystem.existsSync(unprefixedpath) console.log('getting ', unprefixedpath, await window.remixFileSystem.readFile(this.addSlash(unprefixedpath), 'utf8'))
if (!exists) return cb(null, null) try {
window.remixFileSystem.readFile(unprefixedpath, 'utf8', (err, content) => { const content = await window.remixFileSystem.readFile(this.addSlash(unprefixedpath), 'utf8')
cb(err, content) if (cb) cb(null, content)
}) return content
} catch (err) {
if (cb) cb(err, null)
throw new Error(err)
}
} }
set (path, content, cb) { async set (path, content, cb) {
cb = cb || function () {}
var unprefixedpath = this.removePrefix(path) var unprefixedpath = this.removePrefix(path)
var exists = window.remixFileSystem.existsSync(unprefixedpath) const exists = await window.remixFileSystem.exists(unprefixedpath)
if (exists && window.remixFileSystem.readFileSync(unprefixedpath, 'utf8') === content) { if (exists && await window.remixFileSystem.readFile(unprefixedpath, 'utf8') === content) {
cb() if (cb) cb()
return true return null
}
if (!exists && unprefixedpath.indexOf('/') !== -1) {
// the last element is the filename and we should remove it
this.createDir(path.substr(0, path.lastIndexOf('/')))
} }
await this.createDir(path.substr(0, path.lastIndexOf('/')))
console.log('set file', path, unprefixedpath)
try { try {
window.remixFileSystem.writeFileSync(unprefixedpath, content) await window.remixFileSystem.writeFile(this.addSlash(unprefixedpath), content, 'utf8')
} catch (e) { } catch (e) {
cb(e) if (cb) cb(e)
return false return false
} }
if (!exists) { if (!exists) {
@ -121,22 +123,26 @@ class FileProvider {
} else { } else {
this.event.emit('fileChanged', this._normalizePath(unprefixedpath)) this.event.emit('fileChanged', this._normalizePath(unprefixedpath))
} }
cb() if (cb) cb()
return true return true
} }
createDir (path, cb) { async createDir (path, cb) {
const unprefixedpath = this.removePrefix(path) const unprefixedpath = this.removePrefix(path)
const paths = unprefixedpath.split('/') const paths = unprefixedpath.split('/')
if (paths.length && paths[0] === '') paths.shift() if (paths.length && paths[0] === '') paths.shift()
let currentCheck = '' let currentCheck = ''
paths.forEach((value) => { for (const value of paths) {
currentCheck = currentCheck + '/' + value currentCheck = currentCheck + '/' + value
if (!window.remixFileSystem.existsSync(currentCheck)) { if (!await window.remixFileSystem.exists(currentCheck)) {
window.remixFileSystem.mkdirSync(currentCheck) try {
this.event.emit('folderAdded', this._normalizePath(currentCheck)) await window.remixFileSystem.mkdir(currentCheck)
this.event.emit('folderAdded', this._normalizePath(currentCheck))
} catch (error) {
}
} }
}) }
if (cb) cb() if (cb) cb()
} }
@ -150,55 +156,51 @@ class FileProvider {
return false return false
} }
isDirectory (path) { async isDirectory (path) {
const unprefixedpath = this.removePrefix(path) const unprefixedpath = this.removePrefix(path)
return path === this.type ? true : (await window.remixFileSystem.stat(this.addSlash(unprefixedpath))).isDirectory()
return path === this.type ? true : window.remixFileSystem.statSync(unprefixedpath).isDirectory()
} }
isFile (path) { async isFile (path) {
path = this.getPathFromUrl(path) || path // ensure we actually use the normalized path from here path = this.getPathFromUrl(path) || path // ensure we actually use the normalized path from here
path = this.removePrefix(path) path = this.removePrefix(path)
return window.remixFileSystem.statSync(path).isFile() return (await window.remixFileSystem.stat(this.addSlash(path))).isFile()
} }
/** /**
* Removes the folder recursively * Removes the folder recursively
* @param {*} path is the folder to be removed * @param {*} path is the folder to be removed
*/ */
remove (path) { async remove (path) {
return new Promise((resolve, reject) => { path = this.removePrefix(path)
path = this.removePrefix(path) if (await window.remixFileSystem.exists(path)) {
if (window.remixFileSystem.existsSync(path)) { const stat = await window.remixFileSystem.stat(path)
const stat = window.remixFileSystem.statSync(path) try {
try { if (!stat.isDirectory()) {
if (!stat.isDirectory()) { return (this.removeFile(path))
resolve(this.removeFile(path)) } else {
} else { const items = await window.remixFileSystem.readdir(path)
const items = window.remixFileSystem.readdirSync(path) if (items.length !== 0) {
if (items.length !== 0) { for (const item of items) {
items.forEach((item, index) => { const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}`
const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}` if ((await window.remixFileSystem.stat(curPath)).isDirectory()) { // delete folder
if (window.remixFileSystem.statSync(curPath).isDirectory()) { // delete folder this.remove(curPath)
this.remove(curPath) } else { // delete file
} else { // delete file this.removeFile(curPath)
this.removeFile(curPath) }
}
})
if (window.remixFileSystem.readdirSync(path).length === 0) window.remixFileSystem.rmdirSync(path, console.log)
} else {
// folder is empty
window.remixFileSystem.rmdirSync(path, console.log)
} }
this.event.emit('fileRemoved', this._normalizePath(path)) if (await window.remixFileSystem.readdir(path).length === 0) await window.remixFileSystem.rmdir(path)
} else {
// folder is empty
await window.remixFileSystem.rmdirSync(path)
} }
} catch (e) { this.event.emit('fileRemoved', this._normalizePath(path))
console.log(e)
return resolve(false)
} }
} catch (e) {
console.log(e)
return false
} }
return resolve(true) }
})
} }
/** /**
@ -207,28 +209,28 @@ class FileProvider {
* @param {Function} visitFile is a function called for each visited files * @param {Function} visitFile is a function called for each visited files
* @param {Function} visitFolder is a function called for each visited folders * @param {Function} visitFolder is a function called for each visited folders
*/ */
_copyFolderToJsonInternal (path, visitFile, visitFolder) { async _copyFolderToJsonInternal (path, visitFile, visitFolder) {
visitFile = visitFile || (() => {}) /* visitFile = visitFile || (() => { })
visitFolder = visitFolder || (() => {}) visitFolder = visitFolder || (() => { })
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const json = {} const json = {}
path = this.removePrefix(path) path = this.removePrefix(path)
if (window.remixFileSystem.existsSync(path)) { if (window.remixFileSystem.exists(path)) {
try { try {
const items = window.remixFileSystem.readdirSync(path) const items = await window.remixFileSystem.readdir(path)
visitFolder({ path }) visitFolder({ path })
if (items.length !== 0) { if (items.length !== 0) {
items.forEach(async (item, index) => { for (const item of items) {
const file = {} const file = {}
const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}` const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}`
if (window.remixFileSystem.statSync(curPath).isDirectory()) { if (await window.remixFileSystem.stat(curPath).isDirectory()) {
file.children = await this._copyFolderToJsonInternal(curPath, visitFile, visitFolder) file.children = await this._copyFolderToJsonInternal(curPath, visitFile, visitFolder)
} else { } else {
file.content = window.remixFileSystem.readFileSync(curPath, 'utf8') file.content = window.remixFileSystem.readFileSync(curPath, 'utf8')
visitFile({ path: curPath, content: file.content }) visitFile({ path: curPath, content: file.content })
} }
json[curPath] = file json[curPath] = file
}) }
} }
} catch (e) { } catch (e) {
console.log(e) console.log(e)
@ -236,7 +238,7 @@ class FileProvider {
} }
} }
return resolve(json) return resolve(json)
}) }) */
} }
/** /**
@ -245,26 +247,26 @@ class FileProvider {
* @param {Function} visitFile is a function called for each visited files * @param {Function} visitFile is a function called for each visited files
* @param {Function} visitFolder is a function called for each visited folders * @param {Function} visitFolder is a function called for each visited folders
*/ */
copyFolderToJson (path, visitFile, visitFolder) { async copyFolderToJson (path, visitFile, visitFolder) {
visitFile = visitFile || (() => {}) visitFile = visitFile || (() => { })
visitFolder = visitFolder || (() => {}) visitFolder = visitFolder || (() => { })
return this._copyFolderToJsonInternal(path, visitFile, visitFolder) return await this._copyFolderToJsonInternal(path, visitFile, visitFolder)
} }
removeFile (path) { async removeFile (path) {
path = this.removePrefix(path) path = this.removePrefix(path)
if (window.remixFileSystem.existsSync(path) && !window.remixFileSystem.statSync(path).isDirectory()) { if (await window.remixFileSystem.exists(path) && !(await window.remixFileSystem.stat(path)).isDirectory()) {
window.remixFileSystem.unlinkSync(path, console.log) await window.remixFileSystem.unlink(path)
this.event.emit('fileRemoved', this._normalizePath(path)) this.event.emit('fileRemoved', this._normalizePath(path))
return true return true
} else return false } else return false
} }
rename (oldPath, newPath, isFolder) { async rename (oldPath, newPath, isFolder) {
var unprefixedoldPath = this.removePrefix(oldPath) var unprefixedoldPath = this.removePrefix(oldPath)
var unprefixednewPath = this.removePrefix(newPath) var unprefixednewPath = this.removePrefix(newPath)
if (this._exists(unprefixedoldPath)) { if (await this._exists(unprefixedoldPath)) {
window.remixFileSystem.renameSync(unprefixedoldPath, unprefixednewPath) await window.remixFileSystem.rename(unprefixedoldPath, unprefixednewPath)
this.event.emit('fileRenamed', this.event.emit('fileRenamed',
this._normalizePath(unprefixedoldPath), this._normalizePath(unprefixedoldPath),
this._normalizePath(unprefixednewPath), this._normalizePath(unprefixednewPath),
@ -275,24 +277,37 @@ class FileProvider {
return false return false
} }
resolveDirectory (path, callback) { async resolveDirectory (path, cb) {
path = this.removePrefix(path) path = this.removePrefix(path)
console.log('resolve', path)
if (path.indexOf('/') !== 0) path = '/' + path if (path.indexOf('/') !== 0) path = '/' + path
try {
window.remixFileSystem.readdir(path, (error, files) => { const files = await window.remixFileSystem.readdir(path)
var ret = {} const ret = {}
if (files) { if (files) {
files.forEach(element => { for (let element of files) {
console.log(path, element)
path = path.replace(/^\/|\/$/g, '') // remove first and last slash path = path.replace(/^\/|\/$/g, '') // remove first and last slash
element = element.replace(/^\/|\/$/g, '') // remove first and last slash element = element.replace(/^\/|\/$/g, '') // remove first and last slash
const absPath = (path === '/' ? '' : path) + '/' + element const absPath = (path === '/' ? '' : path) + '/' + element
ret[absPath.indexOf('/') === 0 ? absPath.substr(1, absPath.length) : absPath] = { isDirectory: window.remixFileSystem.statSync(absPath).isDirectory() } ret[absPath.indexOf('/') === 0 ? absPath.substr(1, absPath.length) : absPath] = { isDirectory: (await window.remixFileSystem.stat(this.addSlash(absPath))).isDirectory() }
// ^ ret does not accept path starting with '/' // ^ ret does not accept path starting with '/'
}) console.log(ret)
}
} }
callback(error, ret) console.log('FILES', ret, files)
}) if (cb) cb(null, ret)
return ret
} catch (error) {
if (cb) cb(error, null)
throw new Error(error)
}
}
addSlash (file) {
if (!file.startsWith('/'))file = '/' + file
return file
} }
removePrefix (path) { removePrefix (path) {

@ -0,0 +1,29 @@
class remixFileSystem extends LightningFS {
constructor(...t) {
super(...t)
this.promises = {
...this.promises,
exists: async (path) => {
return new Promise((resolve, reject) => {
this.promises.stat(path).then(() => resolve(true)).catch(() => resolve(false))
})
},
statSync: async (path) => {
return new Promise((resolve, reject) => {
console.log("stat", path)
this.promises.stat(path).then((stat) => {
console.log("stat", stat)
resolve({
isDirectory: () => {
return stat.type === 'dir'
},
isFile: () => {
return stat.type === 'file'
},
})
}).catch(() => reject(false))
})
}
}
}
}

File diff suppressed because one or more lines are too long

@ -28,6 +28,7 @@
<link rel="icon" type="x-icon" href="assets/img/icon.png"> <link rel="icon" type="x-icon" href="assets/img/icon.png">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/intro.js/4.1.0/introjs.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/intro.js/4.1.0/introjs.min.css">
<script src="assets/js/browserfs.min.js"></script> <script src="assets/js/browserfs.min.js"></script>
<script src="assets/js/lightning-fs.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<!-- Matomo --> <!-- Matomo -->
<script type="text/javascript"> <script type="text/javascript">
@ -98,7 +99,7 @@
document.head.appendChild(app) document.head.appendChild(app)
} }
window.onload = () => { window.onload = () => {
BrowserFS.install(window) /* BrowserFS.install(window)
BrowserFS.configure({ BrowserFS.configure({
fs: "LocalStorage" fs: "LocalStorage"
}, function(e) { }, function(e) {
@ -107,7 +108,45 @@
app.setAttribute('src', versions[versionToLoad]) app.setAttribute('src', versions[versionToLoad])
document.body.appendChild(app) document.body.appendChild(app)
window.remixFileSystem = require('fs') window.remixFileSystem = require('fs')
}) }) */
class remixFileSystem extends LightningFS {
constructor(...t) {
super(...t)
this.superStat = this.promises.stat
this.promises = {
...this.promises,
exists: async (path) => {
return new Promise((resolve, reject) => {
this.superStat(path).then(() => resolve(true)).catch(() => resolve(false))
})
},
stat: async (path) => {
return new Promise((resolve, reject) => {
console.log("stat", path, this)
//return
this.superStat(path).then((stat) => {
console.log("stat", stat)
resolve({
isDirectory: () => {
return stat.type === 'dir'
},
isFile: () => {
return stat.type === 'file'
},
})
}).catch(() => reject(false))
})
}
}
}
}
window.remixFileSystemCallback = new remixFileSystem('RemixFileSystem')
window.remixFileSystem = window.remixFileSystemCallback.promises
console.log(window.remixFileSystem)
let app = document.createElement('script')
app.setAttribute('src', versions[versionToLoad])
document.body.appendChild(app)
} }
</script> </script>
<script src="runtime.js" type="module"></script> <script src="runtime.js" type="module"></script>

Loading…
Cancel
Save