From bbc04605428a8db9016560ebaacc9410b849aac9 Mon Sep 17 00:00:00 2001 From: Naman Goel Date: Mon, 4 Dec 2023 16:44:35 -0800 Subject: [PATCH] Configure local development for playground Add codemirror editor chore: fix build chore: add serve package to dependencies feat: adjust build setup to use vite feat: allow file editing and updates the preview update apps/docs/components/Playground.js Co-authored-by: Naman Goel update apps/docs/components/Playground.js Co-authored-by: Naman Goel update apps/docs/components/Playground.js Co-authored-by: Naman Goel update apps/docs/components/Playground.js Co-authored-by: Naman Goel update apps/docs/components/Playground.js feat: WIP preview with rollup setup - note: rollup command not generating dist directory and associated files feat: add preview functionality chore: format App.jsx boilerplate preview with vite setup feat: WIP failed to load error message and reload preview button chore: update package-lock.json chore: refactor error handling feat: WIP failed to load error message feat: WIP failed to load error message feat: failed to load error message and reload preview button feat: failed to load error message chore: add console logs chore: check if iframe exists before reloading preview chore: add error handling in reloadWebContainer function --- apps/docs/components/Playground.js | 205 ++++- .../docs/components/playground-utils/files.js | 605 ++++++++++--- apps/docs/package.json | 6 +- package-lock.json | 827 +++++++++++++++--- package.json | 3 + 5 files changed, 1384 insertions(+), 262 deletions(-) diff --git a/apps/docs/components/Playground.js b/apps/docs/components/Playground.js index 00ec559e..5dc572b8 100644 --- a/apps/docs/components/Playground.js +++ b/apps/docs/components/Playground.js @@ -7,13 +7,19 @@ * @format */ -import * as React from 'react'; -import {useEffect, useState, useRef} from 'react'; -import * as stylex from '@stylexjs/stylex'; import BrowserOnly from '@docusaurus/BrowserOnly'; -import {WebContainer} from '@webcontainer/api'; -import {files} from './playground-utils/files'; -import {UnControlled as CodeMirror} from 'react-codemirror2'; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faBars, faRotateRight } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import * as stylex from '@stylexjs/stylex'; +import { WebContainer, reloadPreview } from '@webcontainer/api'; +import * as React from 'react'; +import { useEffect, useRef, useState } from 'react'; +import { UnControlled as CodeMirror } from 'react-codemirror2'; +import 'codemirror/mode/javascript/javascript'; +import { files } from './playground-utils/files'; + +library.add(faBars, faRotateRight); async function wcSpawn(instance, ...args) { console.log('Running:', args.join(' ')); @@ -54,14 +60,27 @@ async function makeWebcontainer() { export default function Playground() { const instance = useRef(null); const [url, setUrl] = useState(null); + const debounceTimeout = useRef(null); + const [code, setCode] = useState( + files.src.directory['App.jsx'].file.contents, + ); + const [error, setError] = useState(null); + const loadingTimeout = useRef(null); + const urlRef = useRef(null); const build = async () => { const containerInstance = instance.current; - if (!containerInstance) return; + if (!containerInstance) { + console.log('error due to failed instance'); + setError( + 'WebContainer failed to load. Please try reloading or use a different browser.', + ); + return; + } - console.log('Trying to run `npm start`...'); - const process = await containerInstance.spawn('npm', ['start']); - console.log('Spawned `npm start`...'); + console.log('Trying to run `npm run dev`...'); + const process = await containerInstance.spawn('npm', ['run', 'dev']); + console.log('Spawned `npm run dev`...'); process.output.pipeTo( new WritableStream({ write(data) { @@ -69,56 +88,166 @@ export default function Playground() { }, }), ); - console.log('Waiting for server-ready event...'); containerInstance.on('server-ready', (port, url) => { console.log('server-ready', port, url); - // TODO: Figure out hot reloading - // TODO: Figure out how to start server *after* build - setTimeout(() => { - setUrl(url); - }, 5000); + setUrl(url); + urlRef.current = url; }); }; + const updateFiles = async () => { + const containerInstance = instance.current; + const filePath = './src/App.jsx'; + const updatedCode = code; + await containerInstance.fs.writeFile(filePath, updatedCode); + }; + + const handleCodeChange = (newCode) => { + if (debounceTimeout.current) { + clearTimeout(debounceTimeout.current); + debounceTimeout.current = null; + } + + debounceTimeout.current = setTimeout(async () => { + setCode(newCode); + if (url) { + try { + await updateFiles(); + console.log('Successfully applied changes.'); + } catch (err) { + console.error(err); + } + } + }, 2000); + }; + + const reloadWebContainer = async () => { + if (!url) return; + const iframe = document.querySelector('iframe'); + if (!iframe) return; + try { + if (error) { + setError(null); + } + console.log('Reloading container preview...'); + await reloadPreview(iframe); + } catch (err) { + console.error(`Error reloading preview: ${err.message}`); + setError( + 'WebContainer failed to load. Please try reloading or use a different browser.', + ); + } + }; + useEffect(() => { require('codemirror/mode/javascript/javascript'); makeWebcontainer().then((i) => { instance.current = i; - build(); + build().then(() => { + loadingTimeout.current = setTimeout(() => { + console.log('running loading timeout'); + console.log('instance: ', instance.current); + console.log('url ref: ', urlRef.current); + if (!urlRef.current) { + console.log('error due to timeout...'); + setError( + 'WebContainer failed to load. Please try reloading or use a different browser.', + ); + } + }, 10000); + }); }); + () => { instance.current.unmount(); + if (debounceTimeout.current) { + clearTimeout(debounceTimeout.current); + } + if (loadingTimeout.current) { + clearTimeout(loadingTimeout.current); + } }; }, []); return ( -
- - {() => ( - <> - - {url ? ( -