commit
349854342e
@ -0,0 +1,130 @@ |
||||
import { RemixApp } from '@remix-ui/app' |
||||
import React, { useEffect, useRef, useState } from 'react' |
||||
import { render } from 'react-dom' |
||||
import * as packageJson from '../../../../../package.json' |
||||
import { fileSystem, fileSystems } from '../files/fileSystem' |
||||
import { indexedDBFileSystem } from '../files/filesystems/indexedDB' |
||||
import { localStorageFS } from '../files/filesystems/localStorage' |
||||
import { fileSystemUtility, migrationTestData } from '../files/filesystems/fileSystemUtility' |
||||
import './styles/preload.css' |
||||
const _paq = window._paq = window._paq || [] |
||||
|
||||
export const Preload = () => { |
||||
|
||||
const [supported, setSupported] = useState<boolean>(true) |
||||
const [error, setError] = useState<boolean>(false) |
||||
const [showDownloader, setShowDownloader] = useState<boolean>(false) |
||||
const remixFileSystems = useRef<fileSystems>(new fileSystems()) |
||||
const remixIndexedDB = useRef<fileSystem>(new indexedDBFileSystem()) |
||||
const localStorageFileSystem = useRef<fileSystem>(new localStorageFS()) |
||||
// url parameters to e2e test the fallbacks and error warnings
|
||||
const testmigrationFallback = useRef<boolean>(window.location.hash.includes('e2e_testmigration_fallback=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') |
||||
const testmigrationResult = useRef<boolean>(window.location.hash.includes('e2e_testmigration=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') |
||||
const testBlockStorage = useRef<boolean>(window.location.hash.includes('e2e_testblock_storage=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') |
||||
|
||||
function loadAppComponent() { |
||||
import('../../app').then((AppComponent) => { |
||||
const appComponent = new AppComponent.default() |
||||
appComponent.run().then(() => { |
||||
render( |
||||
<> |
||||
<RemixApp app={appComponent} /> |
||||
</>, |
||||
document.getElementById('root') |
||||
) |
||||
}) |
||||
}).catch(err => { |
||||
_paq.push(['_trackEvent', 'Preload', 'error', err && err.message]) |
||||
console.log('Error loading Remix:', err) |
||||
setError(true) |
||||
}) |
||||
} |
||||
|
||||
const downloadBackup = async () => { |
||||
setShowDownloader(false) |
||||
const fsUtility = new fileSystemUtility() |
||||
await fsUtility.downloadBackup(remixFileSystems.current.fileSystems['localstorage']) |
||||
await migrateAndLoad() |
||||
} |
||||
|
||||
const migrateAndLoad = async () => { |
||||
setShowDownloader(false) |
||||
const fsUtility = new fileSystemUtility() |
||||
const migrationResult = await fsUtility.migrate(localStorageFileSystem.current, remixIndexedDB.current) |
||||
_paq.push(['_trackEvent', 'Migrate', 'result', migrationResult?'success' : 'fail']) |
||||
await setFileSystems() |
||||
} |
||||
|
||||
const setFileSystems = async() => { |
||||
const fsLoaded = await remixFileSystems.current.setFileSystem([(testmigrationFallback.current || testBlockStorage.current)? null: remixIndexedDB.current, testBlockStorage.current? null:localStorageFileSystem.current]) |
||||
if (fsLoaded) { |
||||
console.log(fsLoaded.name + ' activated') |
||||
_paq.push(['_trackEvent', 'Storage', 'activate', fsLoaded.name]) |
||||
loadAppComponent() |
||||
} else { |
||||
_paq.push(['_trackEvent', 'Storage', 'error', 'no supported storage']) |
||||
setSupported(false) |
||||
} |
||||
} |
||||
|
||||
const testmigration = async() => {
|
||||
if (testmigrationResult.current) { |
||||
const fsUtility = new fileSystemUtility() |
||||
fsUtility.populateWorkspace(migrationTestData, remixFileSystems.current.fileSystems['localstorage'].fs) |
||||
} |
||||
} |
||||
|
||||
useEffect(() => { |
||||
async function loadStorage() { |
||||
await remixFileSystems.current.addFileSystem(remixIndexedDB.current) || _paq.push(['_trackEvent', 'Storage', 'error', 'indexedDB not supported']) |
||||
await remixFileSystems.current.addFileSystem(localStorageFileSystem.current) || _paq.push(['_trackEvent', 'Storage', 'error', 'localstorage not supported']) |
||||
await testmigration() |
||||
remixIndexedDB.current.loaded && await remixIndexedDB.current.checkWorkspaces() |
||||
localStorageFileSystem.current.loaded && await localStorageFileSystem.current.checkWorkspaces() |
||||
remixIndexedDB.current.loaded && ( (remixIndexedDB.current.hasWorkSpaces || !localStorageFileSystem.current.hasWorkSpaces)? await setFileSystems():setShowDownloader(true)) |
||||
!remixIndexedDB.current.loaded && await setFileSystems() |
||||
} |
||||
loadStorage() |
||||
}, []) |
||||
|
||||
return <> |
||||
<div className='preload-container'> |
||||
<div className='preload-logo pb-4'> |
||||
{logo} |
||||
<div className="info-secondary splash"> |
||||
REMIX IDE |
||||
<br /> |
||||
<span className='version'> v{packageJson.version}</span> |
||||
</div> |
||||
</div> |
||||
{!supported ? |
||||
<div className='preload-info-container alert alert-warning'> |
||||
Your browser does not support any of the filesytems required by Remix. |
||||
Either change the settings in your browser or use a supported browser. |
||||
</div> : null} |
||||
{error ? |
||||
<div className='preload-info-container alert alert-danger'> |
||||
An unknown error has occured loading the application. |
||||
</div> : null} |
||||
{showDownloader ? |
||||
<div className='preload-info-container alert alert-info'> |
||||
This app will be updated now. Please download a backup of your files now to make sure you don't lose your work. |
||||
<br></br> |
||||
You don't need to do anything else, your files will be available when the app loads. |
||||
<div onClick={async () => { await downloadBackup() }} data-id='downloadbackup-btn' className='btn btn-primary mt-1'>download backup</div> |
||||
<div onClick={async () => { await migrateAndLoad() }} data-id='skipbackup-btn' className='btn btn-primary mt-1'>skip backup</div> |
||||
</div> : null} |
||||
{(supported && !error && !showDownloader) ? |
||||
<div> |
||||
<i className="fas fa-spinner fa-spin fa-2x"></i> |
||||
</div> : null} |
||||
</div> |
||||
</> |
||||
} |
||||
|
||||
|
||||
const logo = <svg id="Ebene_2" data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 105 100"> |
||||
<path d="M91.84,35a.09.09,0,0,1-.1-.07,41,41,0,0,0-79.48,0,.09.09,0,0,1-.1.07C9.45,35,1,35.35,1,42.53c0,8.56,1,16,6,20.32,2.16,1.85,5.81,2.3,9.27,2.22a44.4,44.4,0,0,0,6.45-.68.09.09,0,0,0,.06-.15A34.81,34.81,0,0,1,17,45c0-.1,0-.21,0-.31a35,35,0,0,1,70,0c0,.1,0,.21,0,.31a34.81,34.81,0,0,1-5.78,19.24.09.09,0,0,0,.06.15,44.4,44.4,0,0,0,6.45.68c3.46.08,7.11-.37,9.27-2.22,5-4.27,6-11.76,6-20.32C103,35.35,94.55,35,91.84,35Z" /> |
||||
<path d="M52,74,25.4,65.13a.1.1,0,0,0-.1.17L51.93,91.93a.1.1,0,0,0,.14,0L78.7,65.3a.1.1,0,0,0-.1-.17L52,74A.06.06,0,0,1,52,74Z" /> |
||||
<path d="M75.68,46.9,82,45a.09.09,0,0,0,.08-.09,29.91,29.91,0,0,0-.87-6.94.11.11,0,0,0-.09-.08l-6.43-.58a.1.1,0,0,1-.06-.18l4.78-4.18a.13.13,0,0,0,0-.12,30.19,30.19,0,0,0-3.65-6.07.09.09,0,0,0-.11,0l-5.91,2a.1.1,0,0,1-.12-.14L72.19,23a.11.11,0,0,0,0-.12,29.86,29.86,0,0,0-5.84-4.13.09.09,0,0,0-.11,0l-4.47,4.13a.1.1,0,0,1-.17-.07l.09-6a.1.1,0,0,0-.07-.1,30.54,30.54,0,0,0-7-1.47.1.1,0,0,0-.1.07l-2.38,5.54a.1.1,0,0,1-.18,0l-2.37-5.54a.11.11,0,0,0-.11-.06,30,30,0,0,0-7,1.48.12.12,0,0,0-.07.1l.08,6.05a.09.09,0,0,1-.16.07L37.8,18.76a.11.11,0,0,0-.12,0,29.75,29.75,0,0,0-5.83,4.13.11.11,0,0,0,0,.12l2.59,5.6a.11.11,0,0,1-.13.14l-5.9-2a.11.11,0,0,0-.12,0,30.23,30.23,0,0,0-3.62,6.08.11.11,0,0,0,0,.12l4.79,4.19a.1.1,0,0,1-.06.17L23,37.91a.1.1,0,0,0-.09.07A29.9,29.9,0,0,0,22,44.92a.1.1,0,0,0,.07.1L28.4,47a.1.1,0,0,1,0,.18l-5.84,3.26a.16.16,0,0,0,0,.11,30.17,30.17,0,0,0,2.1,6.76c.32.71.67,1.4,1,2.08a.1.1,0,0,0,.06,0L52,68.16H52l26.34-8.78a.1.1,0,0,0,.06-.05,30.48,30.48,0,0,0,3.11-8.88.1.1,0,0,0-.05-.11l-5.83-3.26A.1.1,0,0,1,75.68,46.9Z" /> |
||||
</svg> |
@ -0,0 +1,23 @@ |
||||
.preload-container { |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
align-items: center; |
||||
height: 100vh; |
||||
} |
||||
|
||||
.preload-info-container { |
||||
display: flex; |
||||
flex-direction: column; |
||||
text-align: center; |
||||
max-width: 400px; |
||||
} |
||||
|
||||
.preload-info-container .btn { |
||||
cursor: pointer; |
||||
} |
||||
|
||||
.preload-logo { |
||||
min-width: 200px; |
||||
padding-bottom: 1.5rem !important; |
||||
} |
@ -0,0 +1,72 @@ |
||||
export class fileSystem { |
||||
name: string |
||||
enabled: boolean |
||||
available: boolean |
||||
fs: any |
||||
fsCallBack: any; |
||||
hasWorkSpaces: boolean |
||||
loaded: boolean |
||||
load: () => Promise<unknown> |
||||
test: () => Promise<unknown> |
||||
|
||||
constructor() { |
||||
this.available = false |
||||
this.enabled = false |
||||
this.hasWorkSpaces = false |
||||
this.loaded = false |
||||
} |
||||
|
||||
checkWorkspaces = async () => { |
||||
try { |
||||
await this.fs.stat('.workspaces') |
||||
this.hasWorkSpaces = true |
||||
} catch (e) { |
||||
|
||||
} |
||||
} |
||||
|
||||
set = async () => { |
||||
const w = (window as any) |
||||
if (!this.loaded) return false |
||||
w.remixFileSystem = this.fs |
||||
w.remixFileSystem.name = this.name |
||||
w.remixFileSystemCallback = this.fsCallBack |
||||
return true |
||||
} |
||||
} |
||||
|
||||
export class fileSystems { |
||||
fileSystems: Record<string, fileSystem> |
||||
constructor() { |
||||
this.fileSystems = {} |
||||
} |
||||
|
||||
addFileSystem = async (fs: fileSystem): Promise<boolean> => { |
||||
try { |
||||
this.fileSystems[fs.name] = fs |
||||
await fs.test() && await fs.load() |
||||
console.log(fs.name + ' is loaded...') |
||||
return true |
||||
} catch (e) { |
||||
console.log(fs.name + ' not available...') |
||||
return false |
||||
} |
||||
} |
||||
/** |
||||
* sets filesystem using list as fallback |
||||
* @param {string[]} names |
||||
* @returns {Promise} |
||||
*/ |
||||
setFileSystem = async (filesystems?: fileSystem[]): Promise<fileSystem> => { |
||||
for (const fs of filesystems) { |
||||
if (fs && this.fileSystems[fs.name]) { |
||||
const result = await this.fileSystems[fs.name].set() |
||||
if (result) return this.fileSystems[fs.name] |
||||
} |
||||
} |
||||
return null |
||||
} |
||||
|
||||
|
||||
} |
||||
|
@ -0,0 +1,190 @@ |
||||
import { hashMessage } from "ethers/lib/utils" |
||||
import JSZip from "jszip" |
||||
import { fileSystem } from "../fileSystem" |
||||
const _paq = window._paq = window._paq || [] |
||||
export class fileSystemUtility { |
||||
migrate = async (fsFrom: fileSystem, fsTo: fileSystem) => { |
||||
try { |
||||
await fsFrom.checkWorkspaces() |
||||
await fsTo.checkWorkspaces() |
||||
|
||||
if (fsTo.hasWorkSpaces) { |
||||
console.log(`${fsTo.name} already has files`) |
||||
return true |
||||
} |
||||
|
||||
if (!fsFrom.hasWorkSpaces) { |
||||
console.log('no files to migrate') |
||||
return true |
||||
} |
||||
|
||||
const fromFiles = await this.copyFolderToJson('/', null, null, fsFrom.fs) |
||||
await this.populateWorkspace(fromFiles, fsTo.fs) |
||||
const toFiles = await this.copyFolderToJson('/', null, null, fsTo.fs) |
||||
|
||||
if (hashMessage(JSON.stringify(toFiles)) === hashMessage(JSON.stringify(fromFiles))) { |
||||
console.log('file migration successful') |
||||
return true |
||||
} else { |
||||
_paq.push(['_trackEvent', 'Migrate', 'error', 'hash mismatch']) |
||||
console.log('file migration failed falling back to ' + fsFrom.name) |
||||
fsTo.loaded = false |
||||
return false |
||||
} |
||||
} catch (err) { |
||||
console.log(err) |
||||
_paq.push(['_trackEvent', 'Migrate', 'error', err && err.message]) |
||||
console.log('file migration failed falling back to ' + fsFrom.name) |
||||
fsTo.loaded = false |
||||
return false |
||||
} |
||||
} |
||||
|
||||
downloadBackup = async (fs: fileSystem) => { |
||||
try { |
||||
const zip = new JSZip() |
||||
await fs.checkWorkspaces() |
||||
await this.copyFolderToJson('/', null, null, fs.fs, ({ path, content }) => { |
||||
zip.file(path, content) |
||||
}) |
||||
const blob = await zip.generateAsync({ type: 'blob' }) |
||||
const today = new Date() |
||||
const date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate() |
||||
const time = today.getHours() + 'h' + today.getMinutes() + 'min' |
||||
this.saveAs(blob, `remix-backup-at-${time}-${date}.zip`) |
||||
_paq.push(['_trackEvent','Backup','download','preload']) |
||||
} catch (err) { |
||||
_paq.push(['_trackEvent','Backup','error',err && err.message]) |
||||
console.log(err) |
||||
} |
||||
} |
||||
|
||||
populateWorkspace = async (json, fs) => { |
||||
for (const item in json) { |
||||
const isFolder = json[item].content === undefined |
||||
if (isFolder) { |
||||
await this.createDir(item, fs) |
||||
await this.populateWorkspace(json[item].children, fs) |
||||
} else { |
||||
await fs.writeFile(item, json[item].content, 'utf8') |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* copy the folder recursively |
||||
* @param {string} path is the folder to be copied over |
||||
* @param {Function} visitFile is a function called for each visited files |
||||
* @param {Function} visitFolder is a function called for each visited folders |
||||
*/ |
||||
copyFolderToJson = async (path, visitFile, visitFolder, fs, cb = null) => { |
||||
visitFile = visitFile || (() => { }) |
||||
visitFolder = visitFolder || (() => { }) |
||||
return await this._copyFolderToJsonInternal(path, visitFile, visitFolder, fs, cb) |
||||
} |
||||
|
||||
/** |
||||
* copy the folder recursively (internal use) |
||||
* @param {string} path is the folder to be copied over |
||||
* @param {Function} visitFile is a function called for each visited files |
||||
* @param {Function} visitFolder is a function called for each visited folders |
||||
*/ |
||||
async _copyFolderToJsonInternal(path, visitFile, visitFolder, fs, cb) { |
||||
visitFile = visitFile || function () { /* do nothing. */ } |
||||
visitFolder = visitFolder || function () { /* do nothing. */ } |
||||
|
||||
const json = {} |
||||
// path = this.removePrefix(path)
|
||||
if (await fs.exists(path)) { |
||||
const items = await fs.readdir(path) |
||||
visitFolder({ path }) |
||||
if (items.length !== 0) { |
||||
for (const item of items) { |
||||
const file: any = {} |
||||
const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}` |
||||
if ((await fs.stat(curPath)).isDirectory()) { |
||||
file.children = await this._copyFolderToJsonInternal(curPath, visitFile, visitFolder, fs, cb) |
||||
} else { |
||||
file.content = await fs.readFile(curPath, 'utf8') |
||||
if (cb) cb({ path: curPath, content: file.content }) |
||||
visitFile({ path: curPath, content: file.content }) |
||||
|
||||
} |
||||
json[curPath] = file |
||||
} |
||||
} |
||||
} |
||||
return json |
||||
} |
||||
|
||||
createDir = async (path, fs) => { |
||||
const paths = path.split('/') |
||||
if (paths.length && paths[0] === '') paths.shift() |
||||
let currentCheck = '' |
||||
for (const value of paths) { |
||||
currentCheck = currentCheck + (currentCheck ? '/' : '') + value |
||||
if (!await fs.exists(currentCheck)) { |
||||
await fs.mkdir(currentCheck) |
||||
} |
||||
} |
||||
} |
||||
|
||||
saveAs = (blob, name) => { |
||||
const node = document.createElement('a') |
||||
node.download = name |
||||
node.rel = 'noopener' |
||||
node.href = URL.createObjectURL(blob) |
||||
setTimeout(function () { URL.revokeObjectURL(node.href) }, 4E4) // 40s
|
||||
setTimeout(function () { |
||||
try { |
||||
node.dispatchEvent(new MouseEvent('click')) |
||||
} catch (e) { |
||||
const evt = document.createEvent('MouseEvents') |
||||
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, |
||||
20, false, false, false, false, 0, null) |
||||
node.dispatchEvent(evt) |
||||
} |
||||
}, 0) // 40s
|
||||
} |
||||
} |
||||
|
||||
|
||||
/* eslint-disable no-template-curly-in-string */ |
||||
export const migrationTestData = { |
||||
'.workspaces': { |
||||
children: { |
||||
'.workspaces/default_workspace': { |
||||
children: { |
||||
'.workspaces/default_workspace/README.txt': { |
||||
content: 'TEST README' |
||||
} |
||||
} |
||||
}, |
||||
'.workspaces/emptyspace': { |
||||
|
||||
}, |
||||
'.workspaces/workspace_test': { |
||||
children: { |
||||
'.workspaces/workspace_test/TEST_README.txt': { |
||||
content: 'TEST README' |
||||
}, |
||||
'.workspaces/workspace_test/test_contracts': { |
||||
children: { |
||||
'.workspaces/workspace_test/test_contracts/1_Storage.sol': { |
||||
content: 'testing' |
||||
}, |
||||
'.workspaces/workspace_test/test_contracts/artifacts': { |
||||
children: { |
||||
'.workspaces/workspace_test/test_contracts/artifacts/Storage_metadata.json': { |
||||
content: '{ "test": "data" }' |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,91 @@ |
||||
import LightningFS from "@isomorphic-git/lightning-fs" |
||||
import { fileSystem } from "../fileSystem" |
||||
|
||||
export class IndexedDBStorage extends LightningFS { |
||||
base: LightningFS.PromisifedFS |
||||
addSlash: (file: string) => string |
||||
extended: { exists: (path: string) => Promise<unknown>; rmdir: (path: any) => Promise<void>; readdir: (path: any) => Promise<string[]>; unlink: (path: any) => Promise<void>; mkdir: (path: any) => Promise<void>; readFile: (path: any, options: any) => Promise<Uint8Array>; rename: (from: any, to: any) => Promise<void>; writeFile: (path: any, content: any, options: any) => Promise<void>; stat: (path: any) => Promise<import("fs").Stats>; init(name: string, opt?: LightningFS.FSConstructorOptions): void; activate(): Promise<void>; deactivate(): Promise<void>; lstat(filePath: string): Promise<import("fs").Stats>; readlink(filePath: string): Promise<string>; symlink(target: string, filePath: string): Promise<void> } |
||||
constructor(name: string) { |
||||
super(name) |
||||
this.addSlash = (file) => { |
||||
if (!file.startsWith('/')) file = '/' + file |
||||
return file |
||||
} |
||||
this.base = this.promises |
||||
this.extended = { |
||||
...this.promises, |
||||
exists: async (path: string) => { |
||||
return new Promise((resolve) => { |
||||
this.base.stat(this.addSlash(path)).then(() => resolve(true)).catch(() => resolve(false)) |
||||
}) |
||||
}, |
||||
rmdir: async (path) => { |
||||
return this.base.rmdir(this.addSlash(path)) |
||||
}, |
||||
readdir: async (path) => { |
||||
return this.base.readdir(this.addSlash(path)) |
||||
}, |
||||
unlink: async (path) => { |
||||
return this.base.unlink(this.addSlash(path)) |
||||
}, |
||||
mkdir: async (path) => { |
||||
return this.base.mkdir(this.addSlash(path)) |
||||
}, |
||||
readFile: async (path, options) => { |
||||
return this.base.readFile(this.addSlash(path), options) |
||||
}, |
||||
rename: async (from, to) => { |
||||
return this.base.rename(this.addSlash(from), this.addSlash(to)) |
||||
}, |
||||
writeFile: async (path, content, options) => { |
||||
return this.base.writeFile(this.addSlash(path), content, options) |
||||
}, |
||||
stat: async (path) => { |
||||
return this.base.stat(this.addSlash(path)) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
export class indexedDBFileSystem extends fileSystem { |
||||
constructor() { |
||||
super() |
||||
this.name = 'indexedDB' |
||||
} |
||||
|
||||
load = async () => { |
||||
return new Promise((resolve, reject) => { |
||||
try { |
||||
const fs = new IndexedDBStorage('RemixFileSystem') |
||||
fs.init('RemixFileSystem') |
||||
this.fs = fs.extended |
||||
this.fsCallBack = fs |
||||
this.loaded = true |
||||
resolve(true) |
||||
} catch (e) { |
||||
reject(e) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
test = async () => { |
||||
return new Promise((resolve, reject) => { |
||||
if (!window.indexedDB) { |
||||
this.available = false |
||||
reject('No indexedDB on window') |
||||
} |
||||
const request = window.indexedDB.open("RemixTestDataBase"); |
||||
request.onerror = () => { |
||||
this.available = false |
||||
reject('Error creating test database') |
||||
}; |
||||
request.onsuccess = () => { |
||||
window.indexedDB.deleteDatabase("RemixTestDataBase"); |
||||
this.available = true |
||||
resolve(true) |
||||
}; |
||||
}) |
||||
} |
||||
|
||||
} |
@ -0,0 +1,57 @@ |
||||
import { fileSystem } from "../fileSystem"; |
||||
|
||||
export class localStorageFS extends fileSystem { |
||||
|
||||
constructor() { |
||||
super() |
||||
this.name = 'localstorage' |
||||
} |
||||
load = async () => { |
||||
const me = this |
||||
return new Promise((resolve, reject) => { |
||||
try { |
||||
const w = window as any |
||||
w.BrowserFS.install(window) |
||||
w.BrowserFS.configure({ |
||||
fs: 'LocalStorage' |
||||
}, async function (e) { |
||||
if (e) { |
||||
console.log('BrowserFS Error: ' + e) |
||||
reject(e) |
||||
} else { |
||||
me.fs = { ...window.require('fs') } |
||||
me.fsCallBack = window.require('fs') |
||||
me.fs.readdir = me.fs.readdirSync |
||||
me.fs.readFile = me.fs.readFileSync |
||||
me.fs.writeFile = me.fs.writeFileSync |
||||
me.fs.stat = me.fs.statSync |
||||
me.fs.unlink = me.fs.unlinkSync |
||||
me.fs.rmdir = me.fs.rmdirSync |
||||
me.fs.mkdir = me.fs.mkdirSync |
||||
me.fs.rename = me.fs.renameSync |
||||
me.fs.exists = me.fs.existsSync |
||||
me.loaded = true |
||||
resolve(true) |
||||
} |
||||
}) |
||||
} catch (e) { |
||||
console.log('BrowserFS is not ready!') |
||||
reject(e) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
test = async () => { |
||||
return new Promise((resolve, reject) => { |
||||
const test = 'test'; |
||||
try { |
||||
localStorage.setItem(test, test); |
||||
localStorage.removeItem(test); |
||||
resolve(true) |
||||
} catch(e) { |
||||
reject(e) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
} |
File diff suppressed because one or more lines are too long
@ -1,139 +0,0 @@ |
||||
// eslint-disable-next-line no-unused-vars
|
||||
async function migrateFilesFromLocalStorage (cb) { |
||||
let testmigration = false // migration loads test data into localstorage with browserfs
|
||||
// indexeddb will be empty by this point, so there is no danger but do a check for the origin to load test data so it runs only locally
|
||||
testmigration = window.location.hash.includes('e2e_testmigration=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:' |
||||
// eslint-disable-next-line no-undef
|
||||
BrowserFS.install(window) |
||||
// eslint-disable-next-line no-undef
|
||||
BrowserFS.configure({ |
||||
fs: 'LocalStorage' |
||||
}, async function (e) { |
||||
if (e) console.log(e) |
||||
|
||||
const browserFS = window.require('fs') |
||||
|
||||
/** |
||||
* copy the folder recursively (internal use) |
||||
* @param {string} path is the folder to be copied over |
||||
* @param {Function} visitFile is a function called for each visited files |
||||
* @param {Function} visitFolder is a function called for each visited folders |
||||
*/ |
||||
async function _copyFolderToJsonInternal (path, visitFile, visitFolder, fs) { |
||||
visitFile = visitFile || (() => { }) |
||||
visitFolder = visitFolder || (() => { }) |
||||
return new Promise((resolve, reject) => { |
||||
const json = {} |
||||
if (fs.existsSync(path)) { |
||||
try { |
||||
const items = fs.readdirSync(path) |
||||
visitFolder({ path }) |
||||
if (items.length !== 0) { |
||||
items.forEach(async (item, index) => { |
||||
const file = {} |
||||
const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}` |
||||
if (fs.statSync(curPath).isDirectory()) { |
||||
file.children = await _copyFolderToJsonInternal(curPath, visitFile, visitFolder, fs) |
||||
} else { |
||||
file.content = fs.readFileSync(curPath, 'utf8') |
||||
visitFile({ path: curPath, content: file.content }) |
||||
} |
||||
json[curPath] = file |
||||
}) |
||||
} |
||||
} catch (e) { |
||||
console.log(e) |
||||
return reject(e) |
||||
} |
||||
} |
||||
return resolve(json) |
||||
}) |
||||
} |
||||
|
||||
/** |
||||
* copy the folder recursively |
||||
* @param {string} path is the folder to be copied over |
||||
* @param {Function} visitFile is a function called for each visited files |
||||
* @param {Function} visitFolder is a function called for each visited folders |
||||
*/ |
||||
async function copyFolderToJson (path, visitFile, visitFolder, fs) { |
||||
visitFile = visitFile || (() => { }) |
||||
visitFolder = visitFolder || (() => { }) |
||||
return _copyFolderToJsonInternal(path, visitFile, visitFolder, fs) |
||||
} |
||||
|
||||
const populateWorkspace = async (json, fs) => { |
||||
for (const item in json) { |
||||
const isFolder = json[item].content === undefined |
||||
if (isFolder) { |
||||
await createDir(item, fs) |
||||
await populateWorkspace(json[item].children, fs) |
||||
} else { |
||||
try { |
||||
await fs.writeFile(item, json[item].content, 'utf8') |
||||
} catch (error) { |
||||
console.log(error) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
const createDir = async (path, fs) => { |
||||
const paths = path.split('/') |
||||
if (paths.length && paths[0] === '') paths.shift() |
||||
let currentCheck = '' |
||||
for (const value of paths) { |
||||
currentCheck = currentCheck + (currentCheck ? '/' : '') + value |
||||
if (!await fs.exists(currentCheck)) { |
||||
try { |
||||
await fs.mkdir(currentCheck) |
||||
} catch (error) { |
||||
console.log(error) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
//
|
||||
if (testmigration) await populateWorkspace(testData, browserFS) |
||||
const files = await copyFolderToJson('/', null, null, browserFS) |
||||
await populateWorkspace(files, window.remixFileSystem) |
||||
// eslint-disable-next-line no-undef
|
||||
if (cb) cb() |
||||
}) |
||||
} |
||||
|
||||
/* eslint-disable no-template-curly-in-string */ |
||||
const testData = { |
||||
'.workspaces': { |
||||
children: { |
||||
'.workspaces/default_workspace': { |
||||
children: { |
||||
'.workspaces/default_workspace/README.txt': { |
||||
content: 'TEST README' |
||||
} |
||||
} |
||||
}, |
||||
'.workspaces/workspace_test': { |
||||
children: { |
||||
'.workspaces/workspace_test/TEST_README.txt': { |
||||
content: 'TEST README' |
||||
}, |
||||
'.workspaces/workspace_test/test_contracts': { |
||||
children: { |
||||
'.workspaces/workspace_test/test_contracts/1_Storage.sol': { |
||||
content: 'testing' |
||||
}, |
||||
'.workspaces/workspace_test/test_contracts/artifacts': { |
||||
children: { |
||||
'.workspaces/workspace_test/test_contracts/artifacts/Storage_metadata.json': { |
||||
content: '{ "test": "data" }' |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue