import { Component, ElementRef, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { ReactiveFormsModule } from '@angular/forms';
import { WebContainer } from '@webcontainer/api';
import { FormControl } from '@angular/forms';
export const files = {
'index.js': {
file: {
contents: `
import express from 'express';
const app = express();
const port = 3111;
app.get('/', (req, res) => {
res.send('Welcome to a WebContainers app! 🥳');
});
app.listen(port, () => {
console.log(\`App is live at http://localhost:\${port}\`);
});`,
},
},
'package.json': {
file: {
contents: `
{
"name": "example-app",
"type": "module",
"dependencies": {
"express": "latest",
"nodemon": "latest"
},
"scripts": {
"start": "nodemon --watch './' index.js"
}
}`,
},
},
};
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterOutlet, ReactiveFormsModule],
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
webcontainerInstance!: WebContainer;
@ViewChild('iframeEl') iframeEl!: ElementRef;
textareaCtrl = new FormControl<string>('', {nonNullable: true});
async ngOnInit() {
this.webcontainerInstance = await WebContainer.boot({
coep: 'none'
});
await this.webcontainerInstance.mount(files);
const contentFile = await this.webcontainerInstance.fs.readFile('index.js', 'utf-8');
console.log(contentFile);
this.textareaCtrl.setValue(contentFile);
const exitCode = await this.installDependencies();
if (exitCode !== 0) {
throw new Error('Installation failed');
};
await this.startDevServer();
this.textareaCtrl.valueChanges.subscribe(async (value) => {
await this.writeIndexJS(value);
});
}
async installDependencies() {
const installProcess = await this.webcontainerInstance.spawn('npm', ['install']);
installProcess.output.pipeTo(new WritableStream({
write(data) {
console.log(data);
}
}));
return installProcess.exit;
}
async startDevServer() {
await this.webcontainerInstance.spawn('npm', ['run', 'start']);
const iframeEl = this.iframeEl.nativeElement;
this.webcontainerInstance.on('server-ready', (port, url) => {
iframeEl.src = url;
});
}
async writeIndexJS(content: string) {
await this.webcontainerInstance.fs.writeFile('/index.js', content);
};
}
<div class="container">
<div class="editor">
<textarea [formControl]="textareaCtrl">I am a textarea</textarea>
</div>
<div class="preview">
<iframe #iframeEl src="/assets/loading.html"></iframe>
</div>
</div>
* {
box-sizing: border-box;
}
body {
margin: 0;
height: 100vh;
}
.container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
height: 100%;
width: 100%;
}
textarea {
width: 100%;
height: 100%;
resize: none;
border-radius: 0.5rem;
background: black;
color: white;
padding: 0.5rem 1rem;
}
iframe {
height: 100%;
width: 100%;
border-radius: 0.5rem;
}