armo-breadboard
Version:
Edit a live React component's source in real time.
115 lines (95 loc) • 2.36 kB
JavaScript
import { FakeHistory } from 'fake-history'
export function create() {
// TODO: should be paused by default
return new WindowWrapper()
}
export default class WindowWrapper {
constructor(console) {
this.seq = 1
this.timeouts = []
this.intervals = []
this.frames = []
this.window = {
console: console,
alert: window.alert,
prompt: window.prompt,
confirm: window.confirm,
setTimeout: (cb, ms) => {
const id = window.setTimeout(cb, ms)
this.timeouts.push(id)
return id
},
setInterval: (cb, ms) => {
const id = window.setInterval(cb, ms)
this.intervals.push(id)
return id
},
requestAnimationFrame: (cb) => {
const id = window.requestAnimationFrame(cb)
this.frames.push(id)
return id
},
fetch: (...args) => {
const seq = this.seq
return new Promise((resolve, reject) =>
window.fetch(...args).then(
(...success) => {
if (seq === this.seq) {
resolve(...success)
}
},
(...failure) => {
if (seq === this.seq) {
reject(...failure)
}
}
)
)
},
History: new FakeHistory(),
}
}
eval(source, globals={}) {
const exports = {}
const module = { exports: exports }
const windowKeys = Object.keys(window)
const globalKeys = Object.keys(globals)
const execute = new Function(
'window',
'module',
'exports',
...globalKeys,
...windowKeys,
source
)
execute(
window,
module,
exports,
...globalKeys.map(key => globals[key]),
...windowKeys.map(key => this.window[key])
)
const component = exports.default
return exports.default
}
reset() {
for (let timeout of this.timeouts) {
window.clearTimeout(timeout)
}
for (let interval of this.intervals) {
window.clearInterval(interval)
}
for (let frame of this.frames) {
window.cancelAnimationFrame(frame)
}
this.timeouts.length = 0
this.intervals.length = 0
this.frames.length = 0
this.window.console.clear()
this.seq++
}
destroy() {
this.reset()
this.window.console = null
}
}