From 35315768366b6289f22d6ea815e56df145235e7e Mon Sep 17 00:00:00 2001 From: Muhammad Ayman Date: Mon, 1 Sep 2025 14:08:19 +0300 Subject: [PATCH 01/23] init --- YAEGI_INTEGRATION.md | 125 + e2e/specs/compilers.spec.ts | 3597 ++++++------ functions/vendors/templates.js | 5074 ++++++++--------- scripts/build.js | 1 + src/livecodes/languages/go-wasm/index.ts | 1 + .../languages/go-wasm/lang-go-wasm-script.ts | 191 + .../languages/go-wasm/lang-go-wasm.ts | 18 + src/livecodes/languages/languages.ts | 2 + src/livecodes/vendors.ts | 2 + src/sdk/models.ts | 6 + 10 files changed, 4697 insertions(+), 4320 deletions(-) create mode 100644 YAEGI_INTEGRATION.md create mode 100644 src/livecodes/languages/go-wasm/index.ts create mode 100644 src/livecodes/languages/go-wasm/lang-go-wasm-script.ts create mode 100644 src/livecodes/languages/go-wasm/lang-go-wasm.ts diff --git a/YAEGI_INTEGRATION.md b/YAEGI_INTEGRATION.md new file mode 100644 index 0000000000..9bd9640c9a --- /dev/null +++ b/YAEGI_INTEGRATION.md @@ -0,0 +1,125 @@ +# Yaegi WebAssembly Integration + +This document describes the integration of Yaegi (Go interpreter) compiled to WebAssembly into LiveCodes. + +## Overview + +Yaegi is a Go interpreter that can be compiled to WebAssembly, allowing Go code to run directly in the browser. This integration provides a new language option in LiveCodes called "Go (Wasm)" that uses Yaegi to execute Go code. + +## Files Added/Modified + +### New Files + +- `src/livecodes/languages/go-wasm/lang-go-wasm.ts` - Language specification +- `src/livecodes/languages/go-wasm/lang-go-wasm-script.ts` - WebAssembly implementation +- `src/livecodes/languages/go-wasm/index.ts` - Module exports + +### Modified Files + +- `src/livecodes/vendors.ts` - Added `yaegiWasmBaseUrl` +- `src/livecodes/languages/languages.ts` - Registered the new language +- `src/sdk/models.ts` - Added language types and script type +- `scripts/build.js` - Added script to build process +- `e2e/specs/compilers.spec.ts` - Added test case + +## Configuration + +The integration uses the following CDN URLs: + +- WASM file: `https://cdn.jsdelivr.net/npm/yaegi-wasm@1.0.1/src/yaegi-browser.wasm` +- Support script: `https://cdn.jsdelivr.net/npm/yaegi-wasm@1.0.1/src/wasm_exec.js` + +## Usage + +### In LiveCodes Editor + +1. Select "Go (Wasm)" from the language dropdown +2. Write Go code in the editor +3. The code will be executed using Yaegi WebAssembly + +### Example Go Code + +```go +package main + +import "fmt" + +func main() { + fmt.Println("Hello from Yaegi!") + + // Simple calculations + x := 10 + y := 20 + fmt.Printf("Sum: %d\n", x+y) + + // Loops + for i := 0; i < 5; i++ { + fmt.Printf("Count: %d\n", i) + } +} +``` + +### Supported File Extensions + +- `.wasm.go` +- `.yaegi` +- `.go-wasm` +- `.gowasm` +- `.goyae` + +## Implementation Details + +### WebAssembly Worker + +The implementation uses a Web Worker to load and execute Yaegi WebAssembly code. The worker: + +1. Loads the Go WebAssembly support script (`wasm_exec.js`) +2. Initializes Yaegi by loading the WASM file +3. Executes Go code and captures output + +### Code Execution + +The Go code is executed in a WebAssembly environment that provides: + +- Standard Go runtime features +- Console output capture +- Input/output redirection + +## Testing + +A test case has been added to `e2e/specs/compilers.spec.ts` that verifies: + +- The language is available in the UI +- Go code can be written and executed +- Output is correctly displayed + +## Limitations + +1. **Standard Library**: Not all Go standard library packages may be available in the WebAssembly version. + +2. **Performance**: WebAssembly execution may be slower than native Go execution. + +## Future Improvements + +1. **Better Error Handling**: Implement error messages and debugging capabilities. + +## Troubleshooting + +### Common Issues + +1. **Yaegi not loading**: Check that the CDN URLs are accessible and the WASM file is properly loaded. + +2. **Output not appearing**: Check the browser console for any JavaScript errors. + +### Debug Mode + +Enable browser developer tools to see detailed error messages and debug the WebAssembly execution. + +## Contributing + +To improve this integration: + +1. Test with various Go code examples +2. Report issues with specific Go features +3. Contribute improvements to error handling +4. Add support for additional Go packages diff --git a/e2e/specs/compilers.spec.ts b/e2e/specs/compilers.spec.ts index bba6112526..ce34a916a2 100644 --- a/e2e/specs/compilers.spec.ts +++ b/e2e/specs/compilers.spec.ts @@ -1,1783 +1,1814 @@ -import { expect } from '@playwright/test'; -import { getLoadedApp, waitForEditorFocus } from '../helpers'; -import { test } from '../test-fixtures'; - -test.describe('Compiler Results', () => { - test('HTML/CSS/JS', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.type('hello, '); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text="CSS"'); - await waitForEditorFocus(app); - await page.keyboard.type('body {color: blue;}'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text="JavaScript"'); - await waitForEditorFocus(app); - await page.keyboard.type('document.body.innerHTML += "world!"'); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('body'); - - expect(resultText).toContain('hello, world!'); - expect(await getResult().$eval('body', (e) => getComputedStyle(e).color)).toBe( - 'rgb(0, 0, 255)', - ); - }); - - test('Markdown', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Markdown'); - await waitForEditorFocus(app); - await app.page().keyboard.type('# Hi There'); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toBe('Hi There'); - }); - - test('MDX', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=MDX'); - await waitForEditorFocus(app); - await app.page().keyboard.type(` -import {Hello} from './script'; - - -`); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JSX'); - await waitForEditorFocus(app); - await app.page().keyboard.type(` -import React from 'react'; -export const Hello = (props) =>

Hello, {props.title}!

; -`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toBe('Hello, World!'); - }); - - test('Astro', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Astro'); - await waitForEditorFocus(app); - await app.page().keyboard.type(`--- -const title = "World"; ---- - -

Hello, {title}!

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toBe('Hello, World!'); - }); - - test('Pug', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Pug'); - await waitForEditorFocus(app); - await page.keyboard.type('h1 Hi There'); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toBe('Hi There'); - }); - - test('Haml', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "Haml"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text="Haml"'); - await waitForEditorFocus(app); - await page.keyboard.type('.content Hello, #{name}!'); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('.content'); - - expect(resultText).toContain('Hello, Haml!'); - }); - - test('AsciiDoc', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=AsciiDoc'); - await waitForEditorFocus(app); - await page.keyboard.type('== Hello, World!'); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h2'); - - expect(resultText).toContain('Hello, World!'); - }); - - test('Mustache', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "Mustache"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Mustache'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{name}}

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Mustache'); - }); - - test('Mustache dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'Mustache' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Mustache'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{name}}

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Mustache'); - }); - - test('Handlebars', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "Handlebars"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Handlebars'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{name}}

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Handlebars'); - }); - - test('Handlebars dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'Handlebars' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Handlebars'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{name}}

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Handlebars'); - }); - - test('Nunjucks', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "Nunjucks"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Nunjucks'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{name}}

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Nunjucks'); - }); - - test('Nunjucks dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'Nunjucks' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Nunjucks'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{name}}

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Nunjucks'); - }); - - test('EJS', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "EJS"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=EJS'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to <%= name %>

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to EJS'); - }); - - test('EJS dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'EJS' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=EJS'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to <%= name %>

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to EJS'); - }); - - test('Eta', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "Eta"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text="Eta"'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to <%= it.name %>

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Eta'); - }); - - test('Eta dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'Eta' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text="Eta"'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to <%= it.name %>

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Eta'); - }); - - test('Liquid', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name":"liquid"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Liquid'); - await waitForEditorFocus(app); - await page.keyboard.type(`{{ name | capitalize | prepend: "Welcome to "}}`); - - await waitForResultUpdate(); - const body = await getResult().$('body'); - - expect(await body?.innerHTML()).toContain('Welcome to Liquid'); - }); - - test('Liquid dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'liquid' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Liquid'); - await waitForEditorFocus(app); - await page.keyboard.type(`{{ name | capitalize | prepend: "Welcome to "}}`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const body = await getResult().$('body'); - - expect(await body?.innerHTML()).toContain('Welcome to Liquid'); - }); - - test('doT', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name":"doT"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=doT'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{=it.name}}

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to doT'); - }); - - test('doT dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'doT' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=doT'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{=it.name}}

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to doT'); - }); - - test('Twig', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "Twig"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Twig'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{ name }}

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Twig'); - }); - - test('Twig dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'Twig' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Twig'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{ name }}

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Twig'); - }); - - test('Vento', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "Vento"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Vento'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{ name }}

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Vento'); - }); - - test('Vento dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'Vento' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Vento'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{ name }}

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Vento'); - }); - - test('art-template', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "art-template"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=art-template'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{ name }}

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to art-template'); - }); - - test('art-template dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'art-template' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=art-template'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{ name }}

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to art-template'); - }); - - test('jinja', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "jinja"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text="Jinja"'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{ name }}

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to jinja'); - }); - - test('jinja dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'jinja' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text="Jinja"'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{ name }}

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to jinja'); - }); - - test('BBCode', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=BBCode'); - await waitForEditorFocus(app); - await app.page().keyboard.type('[quote]quoted text[/quote]'); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('blockquote'); - - expect(resultText).toBe('quoted text'); - }); - - test('MJML', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=MJML'); - await waitForEditorFocus(app); - await page.keyboard.type(` - - - - - - Hello MJML! - - - - - -`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('table'); - - expect(resultText).toContain('Hello MJML!'); - }); - - test('SCSS', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=SCSS'); - await waitForEditorFocus(app); - await page.keyboard.type( - `$font-stack: Helvetica, sans-serif; body { font: 100% $font-stack; }`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('style'); - - expect(resultText).toContain('font: 100% Helvetica, sans-serif;'); - }); - - test('Sass', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=Sass'); - await waitForEditorFocus(app); - await page.keyboard.type(`$font-stack: Helvetica, sans-serif\nbody\n font: 100% $font-stack`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('style'); - - expect(resultText).toContain('font: 100% Helvetica, sans-serif;'); - }); - - test('Less', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=Less'); - await waitForEditorFocus(app); - await page.keyboard.type(`@width: 10px; #header { width: @width; }`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('style'); - - expect(resultText).toContain('width: 10px;'); - }); - - test('Stylus', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=Stylus'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`font-size = 14px\nbody\n font font-size Arial, sans-serif`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('style'); - - expect(resultText).toContain('font: 14px Arial, sans-serif;'); - }); - - test('Stylis', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=Stylis'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - '[namespace] {\n div {\n display: flex;\n\n @media screen {\n color: blue;\n }\n }\n\n div {\n transform: translateZ(0);\n\n h1, h2 {\n color: red;\n }\n }\n}', - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('style'); - - expect(resultText).toContain( - '[namespace] div{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}@media screen{[namespace] div{color:blue;}}[namespace] div{-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0);}[namespace] div h1,[namespace] div h2{color:red;}', - ); - }); - - test('PostCSS/postcssImportUrl', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=Import Url'); - await app.click('text=CSS'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`@import "github-markdown-css";`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('style'); - - expect(resultText).toContain('.markdown-body'); - }); - - test('PostCSS/Autoprefixer', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=Autoprefixer'); - await app.click('text=CSS'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`.example { user-select: none; }`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('style'); - - expect(resultText).toContain('-webkit-user-select: none;'); - }); - - test('PostCSS/Preset Env', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=Preset Env'); - await app.click('text=CSS'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - `:root { --mainColor: #12345678; --secondaryColor: lab(32.5 38.5 -47.6 / 90%); }`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('style'); - - expect(resultText).toContain('--mainColor: rgba(18,52,86,0.47059);'); - }); - - test('Babel', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Babel'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`[1, 2, 3].map(n => n + 1);`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('body > script'); - - expect(resultText).toContain( - `[1, 2, 3].map(function (n) { - return n + 1; -});`, - ); - }); - - test('Sucrase', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Sucrase'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`const Greet = (name: string) => <>Hello {name}!;`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('body > script'); - - expect(resultText).toContain( - `const Greet = (name) => React.createElement(React.Fragment, null, "Hello " , name, "!");`, - ); - }); - - test('TypeScript', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=TypeScript'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - `type Fish = { swim: () => void }; -type Bird = { fly: () => void }; -declare function getSmallPet(): Fish | Bird; -// ---cut--- -function isFish(pet: Fish | Bird): pet is Fish { - return (pet as Fish).swim !== undefined; -}`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('body > script'); - - expect(resultText).toContain( - `// ---cut--- -function isFish(pet) { - return pet.swim !== undefined; -}`, - ); - }); - - test('Flow', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Flow'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - 'function foo(x: ?number): string {if (x) { return x; } return "default string"; }', - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('body > script'); - - expect(resultText).toContain( - 'function foo(x ) {if (x) { return x; } return "default string"; }', - ); - }); - - test('JSX', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`
Loading...
`); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text="JSX"'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` -const Hello = (props) =>

Hello, {props.name}

-export default () => ; -`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, React`); - }); - - test('TSX', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text="TSX"'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` -interface Props { name: string } -const Hello = (props: Props) =>

Hello, {props.name}

-export default () => ; -`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, React`); - }); - - test('React', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`
Loading...
`); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text="React"'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` -const Hello = (props) =>

Hello, {props.name}

-export default () => ; -`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, React`); - }); - - test('React (TSX)', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text="React (TSX)"'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` -interface Props { name: string } -const Hello = (props: Props) =>

Hello, {props.name}

-export default () => ; -`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, React`); - }); - - test('Vue', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await (await app.$('[data-lang="vue"]'))?.click(); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` - -`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, Vue`); - expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); - }); - - test('Vue JSX', async ({ page, getTestUrl }) => { - const sfc = ` - - -`; - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([title="Change Language"], 3)'); - await (await app.$('[data-lang="vue"]'))?.click(); - await waitForEditorFocus(app); - await page.keyboard.insertText(sfc); - - await waitForResultUpdate(); - - await getResult().click('text=Click me'); - await getResult().click('text=Click me'); - await getResult().click('text=Click me'); - - const titleText = await getResult().innerText('h1'); - expect(titleText).toBe('Hello, Vue!'); - - const counterText = await getResult().innerText('text=You clicked'); - expect(counterText).toBe('You clicked 3 times.'); - }); - - test('Vue import', async ({ page, getTestUrl }) => { - const sfc = ` - - -`; - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await (await app.$('[data-lang="vue"]'))?.click(); - await waitForEditorFocus(app); - await page.keyboard.insertText(sfc); - - await waitForResultUpdate(); - - // css import - const headHTML = await getResult().innerHTML('head'); - expect(headHTML).toContain( - '', - ); - - // bare module import - const uuidText = await getResult().innerText('#uuid'); - expect(uuidText.length).toBeGreaterThan(10); - - // import vue component - await getResult().click(':nth-match(button, 1)'); - await getResult().click(':nth-match(button, 1)'); - await getResult().click(':nth-match(button, 1)'); - - // import vue component that has relative imports and fetches absolute url - const buttonText = await getResult().innerText(':nth-match(button, 1)'); - expect(buttonText).toBe('Count is: 3, double is: 6'); - }); - - test('Vue langs', async ({ page, getTestUrl }) => { - const sfc = ` - - - - -`; - - await page.goto( - getTestUrl({ - x: 'code/N4IgLglmA2CmIC4QFUB2kawCYAIAKATgPYBWsAxmCADQhawDO5BEADpEaoiDeAIYBzBogDaAXVp9KEAG6wAolihEC3Ji3a8AtnwIBrAK6tEoaH1QCDg+EgAWYLdF7lOYWOm4gAvrQZgAnnAmIGYWVgI2IOQMwrQu6O5USN6+zGxJpuaW1twyBvBxroncADxuWqxmbjihAgC8ADogWlhNAHwNBA2oODgAxDgAErDQ0ETUOMDAOKh8WrA4Xl4AhJ3dvSWsOAjkZjGNIAAkfoGwAHRuAB5g7QxE8zhXYCUA9KwdXaiv5ZV8bh-dNZfdTpGpZA5gBhNHAMWBgIwAno4eJ+GZzWAIGFgFgWHB1HAAcgAavkcABmAkAbiBrxB7ERNJOcDBFgOTBi0K0RCwBjgiN6hwAZq4ALR+KR6THDaBySDkPgTBjmBhi2AsQXUz4C1gsHQEfwilxjAiYvoAVgtmvWOGOFE4WF0BqNKkxAgIsHcVtQQN6tgAjJMfb0cML0JiAEwABkjAFIbaGwGKwBKvcHkURjZjDjqIHqnRmVKnFkCgxdYNdA1rgwnMX7o3GhaLxeQ9EXes6TTbYfEHfrDQWCEWvDSXkzYG0UiAxwxbB7IaIJFO0uxhAhxHEYoRGHDPLQdURyIw7gRV+uogY-PcAMpwyAWVfAHwgXOsFTzhCP2gBViMExPtx+A+IRZOEkTfowy5UIUCQeMkT5yCeECcNwAAs3hAA', - }), - ); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await (await app.$('[data-lang="vue"]'))?.click(); - await waitForEditorFocus(app); - // await page.keyboard.insertText(sfc); - - await waitForResultUpdate(); - - const headerText = await getResult().innerText('h1'); - - // markdown, scss, typescript - expect(headerText).toContain(`Hello, Vue 3!`); - expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(85, 85, 85)'); - - // css modules - expect(await getResult().$eval('p', (e) => getComputedStyle(e).color)).toBe('rgb(0, 128, 0)'); - }); - - test('Vue + Tailwind CSS', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await (await app.$('[data-lang="vue"]'))?.click(); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` -`, - ); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=Tailwind CSS'); - await app.click('text=CSS'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - `@tailwind base; -@tailwind components; -@tailwind utilities; -`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Tailwind in Vue SFC`); - expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe( - 'rgb(220, 38, 38)', - ); - }); - - test('Vue 2', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Vue 2'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` - -`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, Vue 2`); - expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); - }); - - test('Svelte', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await (await app.$('[data-lang="svelte"]'))?.click(); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` - -
-

Hello, {title}

-
-`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, Svelte`); - expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); - }); - - test('Malina.js', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Malina.js'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` - -
-

Hello, {title}

-
-`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, Malina.js`); - expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); - }); - - test('Stencil', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText(''); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Stencil'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - `import { Component, Prop, h } from "@stencil/core"; -@Component({ - tag: "my-app", - styles: "h1 { color: blue; }", -}) -export class App { - @Prop() title: string; - render() { - return ( -

Hello, {this.title}

- ); - } -}`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, Stencil`); - expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); - }); - - test('CoffeeScript', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=CoffeeScript'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`square = (x) -> x * x`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('body > script'); - - expect(resultText).toContain(`var square;`); - expect(resultText).toContain(`square = function(x) { - return x * x; -};`); - }); - - test('LiveScript', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, World

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=LiveScript'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`{ capitalize, join, map, words } = require 'prelude-ls' -title = 'live script' -|> words -|> map capitalize -|> join '' -(document.getElementById \\title).textContent = title`); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, LiveScript`); - }); - - test('Riot.js', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText(''); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Riot.js'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, {props.title}

'); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, Riot.js`); - }); - - test('AssemblyScript', async ({ page, getTestUrl }) => { - test.fixme(); - - await page.goto(getTestUrl()); - - const { app, getResult } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - `

Hello, World

- `, - ); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=AssemblyScript'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`export function getTitle(): string {return "AssemblyScript";`); - // workaround for monaco auto-complete - await page.keyboard.press('Delete'); - await page.keyboard.insertText(`}`); - - await app.waitForTimeout(15000); - const resultText = await getResult().innerText('text=Hello, AssemblyScript'); - - expect(resultText).toContain(`Hello, AssemblyScript`); - }); - - test('Python', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, World

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Python'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`from browser import document -title = 'Python' -document['header'].html = f"Hello, {title}"`); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, Python`); - }); - - test('Pyodide', async ({ page, getTestUrl }) => { - test.skip(); - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, World

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Pyodide'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`from js import document -title = 'Python' -document.getElementById('header').innerHTML = f"Hello, {title}"`); - - await waitForResultUpdate(); - await getResult().waitForSelector('text=Hello, Python'); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, Python`); - }); - - test('Ruby', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, world

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Ruby'); - await waitForEditorFocus(app); - - await page.keyboard.insertText(`require "native" -title = 'Ruby' -$$.document.querySelector('#title').innerHTML = title`); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('text=Hello, Ruby'); - - expect(resultText).toContain(`Hello, Ruby`); - }); - - test('Go', async ({ page, getTestUrl, editor }) => { - test.slow(); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, world

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Go'); - await waitForEditorFocus(app); - - await page.keyboard.insertText(`package main -import "syscall/js" -func main() { - js.Global().Get("document").Call("querySelector", "#title").Set("innerHTML", "Golang") -}`); - - await waitForResultUpdate({ delay: 4000, timeout: 60_000 }); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, Golang`); - }); - - test('PHP', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, world

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=PHP'); - await waitForEditorFocus(app); - - // go below pre-inserted 'getElementById('title')->textContent = $title;`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, PHP`); - }); - - test('Perl', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, world

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Perl'); - await waitForEditorFocus(app); - - await page.keyboard.insertText(`use strict; -my $title = 'Perl'; -JS::inline('document.getElementById("title").innerHTML') = $title;`); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, Perl`); - }); - - test('Lua', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, world

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Lua'); - await waitForEditorFocus(app); - - await page.keyboard.insertText(`js = require "js" -window = js.global -document = window.document -document:getElementById("title").innerHTML = "Lua"`); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, Lua`); - }); - - test('Scheme', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, world

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Scheme'); - await waitForEditorFocus(app); - - await page.keyboard.insertText(`(let ((title "Scheme")) - (set-content! "#title" title))`); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, Scheme`); - }); -}); +import { expect } from '@playwright/test'; +import { getLoadedApp, waitForEditorFocus } from '../helpers'; +import { test } from '../test-fixtures'; + +test.describe('Compiler Results', () => { + test('HTML/CSS/JS', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.type('hello, '); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text="CSS"'); + await waitForEditorFocus(app); + await page.keyboard.type('body {color: blue;}'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="JavaScript"'); + await waitForEditorFocus(app); + await page.keyboard.type('document.body.innerHTML += "world!"'); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('body'); + + expect(resultText).toContain('hello, world!'); + expect(await getResult().$eval('body', (e) => getComputedStyle(e).color)).toBe( + 'rgb(0, 0, 255)', + ); + }); + + test('Markdown', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Markdown'); + await waitForEditorFocus(app); + await app.page().keyboard.type('# Hi There'); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toBe('Hi There'); + }); + + test('MDX', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=MDX'); + await waitForEditorFocus(app); + await app.page().keyboard.type(` +import {Hello} from './script'; + + +`); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JSX'); + await waitForEditorFocus(app); + await app.page().keyboard.type(` +import React from 'react'; +export const Hello = (props) =>

Hello, {props.title}!

; +`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toBe('Hello, World!'); + }); + + test('Astro', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Astro'); + await waitForEditorFocus(app); + await app.page().keyboard.type(`--- +const title = "World"; +--- + +

Hello, {title}!

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toBe('Hello, World!'); + }); + + test('Pug', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Pug'); + await waitForEditorFocus(app); + await page.keyboard.type('h1 Hi There'); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toBe('Hi There'); + }); + + test('Haml', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "Haml"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="Haml"'); + await waitForEditorFocus(app); + await page.keyboard.type('.content Hello, #{name}!'); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('.content'); + + expect(resultText).toContain('Hello, Haml!'); + }); + + test('AsciiDoc', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=AsciiDoc'); + await waitForEditorFocus(app); + await page.keyboard.type('== Hello, World!'); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h2'); + + expect(resultText).toContain('Hello, World!'); + }); + + test('Mustache', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "Mustache"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Mustache'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{name}}

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Mustache'); + }); + + test('Mustache dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'Mustache' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Mustache'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{name}}

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Mustache'); + }); + + test('Handlebars', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "Handlebars"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Handlebars'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{name}}

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Handlebars'); + }); + + test('Handlebars dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'Handlebars' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Handlebars'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{name}}

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Handlebars'); + }); + + test('Nunjucks', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "Nunjucks"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Nunjucks'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{name}}

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Nunjucks'); + }); + + test('Nunjucks dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'Nunjucks' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Nunjucks'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{name}}

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Nunjucks'); + }); + + test('EJS', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "EJS"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=EJS'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to <%= name %>

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to EJS'); + }); + + test('EJS dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'EJS' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=EJS'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to <%= name %>

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to EJS'); + }); + + test('Eta', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "Eta"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="Eta"'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to <%= it.name %>

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Eta'); + }); + + test('Eta dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'Eta' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="Eta"'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to <%= it.name %>

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Eta'); + }); + + test('Liquid', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name":"liquid"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Liquid'); + await waitForEditorFocus(app); + await page.keyboard.type(`{{ name | capitalize | prepend: "Welcome to "}}`); + + await waitForResultUpdate(); + const body = await getResult().$('body'); + + expect(await body?.innerHTML()).toContain('Welcome to Liquid'); + }); + + test('Liquid dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'liquid' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Liquid'); + await waitForEditorFocus(app); + await page.keyboard.type(`{{ name | capitalize | prepend: "Welcome to "}}`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const body = await getResult().$('body'); + + expect(await body?.innerHTML()).toContain('Welcome to Liquid'); + }); + + test('doT', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name":"doT"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=doT'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{=it.name}}

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to doT'); + }); + + test('doT dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'doT' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=doT'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{=it.name}}

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to doT'); + }); + + test('Twig', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "Twig"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Twig'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{ name }}

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Twig'); + }); + + test('Twig dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'Twig' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Twig'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{ name }}

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Twig'); + }); + + test('Vento', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "Vento"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Vento'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{ name }}

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Vento'); + }); + + test('Vento dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'Vento' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Vento'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{ name }}

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Vento'); + }); + + test('art-template', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "art-template"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=art-template'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{ name }}

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to art-template'); + }); + + test('art-template dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'art-template' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=art-template'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{ name }}

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to art-template'); + }); + + test('jinja', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "jinja"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="Jinja"'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{ name }}

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to jinja'); + }); + + test('jinja dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'jinja' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="Jinja"'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{ name }}

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to jinja'); + }); + + test('BBCode', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=BBCode'); + await waitForEditorFocus(app); + await app.page().keyboard.type('[quote]quoted text[/quote]'); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('blockquote'); + + expect(resultText).toBe('quoted text'); + }); + + test('MJML', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=MJML'); + await waitForEditorFocus(app); + await page.keyboard.type(` + + + + + + Hello MJML! + + + + + +`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('table'); + + expect(resultText).toContain('Hello MJML!'); + }); + + test('SCSS', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=SCSS'); + await waitForEditorFocus(app); + await page.keyboard.type( + `$font-stack: Helvetica, sans-serif; body { font: 100% $font-stack; }`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('style'); + + expect(resultText).toContain('font: 100% Helvetica, sans-serif;'); + }); + + test('Sass', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=Sass'); + await waitForEditorFocus(app); + await page.keyboard.type(`$font-stack: Helvetica, sans-serif\nbody\n font: 100% $font-stack`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('style'); + + expect(resultText).toContain('font: 100% Helvetica, sans-serif;'); + }); + + test('Less', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=Less'); + await waitForEditorFocus(app); + await page.keyboard.type(`@width: 10px; #header { width: @width; }`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('style'); + + expect(resultText).toContain('width: 10px;'); + }); + + test('Stylus', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=Stylus'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`font-size = 14px\nbody\n font font-size Arial, sans-serif`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('style'); + + expect(resultText).toContain('font: 14px Arial, sans-serif;'); + }); + + test('Stylis', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=Stylis'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + '[namespace] {\n div {\n display: flex;\n\n @media screen {\n color: blue;\n }\n }\n\n div {\n transform: translateZ(0);\n\n h1, h2 {\n color: red;\n }\n }\n}', + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('style'); + + expect(resultText).toContain( + '[namespace] div{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}@media screen{[namespace] div{color:blue;}}[namespace] div{-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0);}[namespace] div h1,[namespace] div h2{color:red;}', + ); + }); + + test('PostCSS/postcssImportUrl', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=Import Url'); + await app.click('text=CSS'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`@import "github-markdown-css";`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('style'); + + expect(resultText).toContain('.markdown-body'); + }); + + test('PostCSS/Autoprefixer', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=Autoprefixer'); + await app.click('text=CSS'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`.example { user-select: none; }`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('style'); + + expect(resultText).toContain('-webkit-user-select: none;'); + }); + + test('PostCSS/Preset Env', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=Preset Env'); + await app.click('text=CSS'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + `:root { --mainColor: #12345678; --secondaryColor: lab(32.5 38.5 -47.6 / 90%); }`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('style'); + + expect(resultText).toContain('--mainColor: rgba(18,52,86,0.47059);'); + }); + + test('Babel', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Babel'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`[1, 2, 3].map(n => n + 1);`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('body > script'); + + expect(resultText).toContain( + `[1, 2, 3].map(function (n) { + return n + 1; +});`, + ); + }); + + test('Sucrase', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Sucrase'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`const Greet = (name: string) => <>Hello {name}!;`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('body > script'); + + expect(resultText).toContain( + `const Greet = (name) => React.createElement(React.Fragment, null, "Hello " , name, "!");`, + ); + }); + + test('TypeScript', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=TypeScript'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + `type Fish = { swim: () => void }; +type Bird = { fly: () => void }; +declare function getSmallPet(): Fish | Bird; +// ---cut--- +function isFish(pet: Fish | Bird): pet is Fish { + return (pet as Fish).swim !== undefined; +}`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('body > script'); + + expect(resultText).toContain( + `// ---cut--- +function isFish(pet) { + return pet.swim !== undefined; +}`, + ); + }); + + test('Flow', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Flow'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + 'function foo(x: ?number): string {if (x) { return x; } return "default string"; }', + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('body > script'); + + expect(resultText).toContain( + 'function foo(x ) {if (x) { return x; } return "default string"; }', + ); + }); + + test('JSX', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`
Loading...
`); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="JSX"'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` +const Hello = (props) =>

Hello, {props.name}

+export default () => ; +`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, React`); + }); + + test('TSX', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="TSX"'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` +interface Props { name: string } +const Hello = (props: Props) =>

Hello, {props.name}

+export default () => ; +`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, React`); + }); + + test('React', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`
Loading...
`); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="React"'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` +const Hello = (props) =>

Hello, {props.name}

+export default () => ; +`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, React`); + }); + + test('React (TSX)', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="React (TSX)"'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` +interface Props { name: string } +const Hello = (props: Props) =>

Hello, {props.name}

+export default () => ; +`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, React`); + }); + + test('Vue', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await (await app.$('[data-lang="vue"]'))?.click(); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` + +`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, Vue`); + expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); + }); + + test('Vue JSX', async ({ page, getTestUrl }) => { + const sfc = ` + + +`; + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + await app.click(':nth-match([title="Change Language"], 3)'); + await (await app.$('[data-lang="vue"]'))?.click(); + await waitForEditorFocus(app); + await page.keyboard.insertText(sfc); + + await waitForResultUpdate(); + + await getResult().click('text=Click me'); + await getResult().click('text=Click me'); + await getResult().click('text=Click me'); + + const titleText = await getResult().innerText('h1'); + expect(titleText).toBe('Hello, Vue!'); + + const counterText = await getResult().innerText('text=You clicked'); + expect(counterText).toBe('You clicked 3 times.'); + }); + + test('Vue import', async ({ page, getTestUrl }) => { + const sfc = ` + + +`; + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await (await app.$('[data-lang="vue"]'))?.click(); + await waitForEditorFocus(app); + await page.keyboard.insertText(sfc); + + await waitForResultUpdate(); + + // css import + const headHTML = await getResult().innerHTML('head'); + expect(headHTML).toContain( + '', + ); + + // bare module import + const uuidText = await getResult().innerText('#uuid'); + expect(uuidText.length).toBeGreaterThan(10); + + // import vue component + await getResult().click(':nth-match(button, 1)'); + await getResult().click(':nth-match(button, 1)'); + await getResult().click(':nth-match(button, 1)'); + + // import vue component that has relative imports and fetches absolute url + const buttonText = await getResult().innerText(':nth-match(button, 1)'); + expect(buttonText).toBe('Count is: 3, double is: 6'); + }); + + test('Vue langs', async ({ page, getTestUrl }) => { + const sfc = ` + + + + +`; + + await page.goto( + getTestUrl({ + x: 'code/N4IgLglmA2CmIC4QFUB2kawCYAIAKATgPYBWsAxmCADQhawDO5BEADpEaoiDeAIYBzBogDaAXVp9KEAG6wAolihEC3Ji3a8AtnwIBrAK6tEoaH1QCDg+EgAWYLdF7lOYWOm4gAvrQZgAnnAmIGYWVgI2IOQMwrQu6O5USN6+zGxJpuaW1twyBvBxroncADxuWqxmbjihAgC8ADogWlhNAHwNBA2oODgAxDgAErDQ0ETUOMDAOKh8WrA4Xl4AhJ3dvSWsOAjkZjGNIAAkfoGwAHRuAB5g7QxE8zhXYCUA9KwdXaiv5ZV8bh-dNZfdTpGpZA5gBhNHAMWBgIwAno4eJ+GZzWAIGFgFgWHB1HAAcgAavkcABmAkAbiBrxB7ERNJOcDBFgOTBi0K0RCwBjgiN6hwAZq4ALR+KR6THDaBySDkPgTBjmBhi2AsQXUz4C1gsHQEfwilxjAiYvoAVgtmvWOGOFE4WF0BqNKkxAgIsHcVtQQN6tgAjJMfb0cML0JiAEwABkjAFIbaGwGKwBKvcHkURjZjDjqIHqnRmVKnFkCgxdYNdA1rgwnMX7o3GhaLxeQ9EXes6TTbYfEHfrDQWCEWvDSXkzYG0UiAxwxbB7IaIJFO0uxhAhxHEYoRGHDPLQdURyIw7gRV+uogY-PcAMpwyAWVfAHwgXOsFTzhCP2gBViMExPtx+A+IRZOEkTfowy5UIUCQeMkT5yCeECcNwAAs3hAA', + }), + ); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await (await app.$('[data-lang="vue"]'))?.click(); + await waitForEditorFocus(app); + // await page.keyboard.insertText(sfc); + + await waitForResultUpdate(); + + const headerText = await getResult().innerText('h1'); + + // markdown, scss, typescript + expect(headerText).toContain(`Hello, Vue 3!`); + expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(85, 85, 85)'); + + // css modules + expect(await getResult().$eval('p', (e) => getComputedStyle(e).color)).toBe('rgb(0, 128, 0)'); + }); + + test('Vue + Tailwind CSS', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await (await app.$('[data-lang="vue"]'))?.click(); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` +`, + ); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=Tailwind CSS'); + await app.click('text=CSS'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + `@tailwind base; +@tailwind components; +@tailwind utilities; +`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Tailwind in Vue SFC`); + expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe( + 'rgb(220, 38, 38)', + ); + }); + + test('Vue 2', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Vue 2'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` + +`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, Vue 2`); + expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); + }); + + test('Svelte', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await (await app.$('[data-lang="svelte"]'))?.click(); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` + +
+

Hello, {title}

+
+`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, Svelte`); + expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); + }); + + test('Malina.js', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Malina.js'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` + +
+

Hello, {title}

+
+`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, Malina.js`); + expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); + }); + + test('Stencil', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText(''); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Stencil'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + `import { Component, Prop, h } from "@stencil/core"; +@Component({ + tag: "my-app", + styles: "h1 { color: blue; }", +}) +export class App { + @Prop() title: string; + render() { + return ( +

Hello, {this.title}

+ ); + } +}`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, Stencil`); + expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); + }); + + test('CoffeeScript', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=CoffeeScript'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`square = (x) -> x * x`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('body > script'); + + expect(resultText).toContain(`var square;`); + expect(resultText).toContain(`square = function(x) { + return x * x; +};`); + }); + + test('LiveScript', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, World

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=LiveScript'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`{ capitalize, join, map, words } = require 'prelude-ls' +title = 'live script' +|> words +|> map capitalize +|> join '' +(document.getElementById \\title).textContent = title`); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, LiveScript`); + }); + + test('Riot.js', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText(''); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Riot.js'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, {props.title}

'); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, Riot.js`); + }); + + test('AssemblyScript', async ({ page, getTestUrl }) => { + test.fixme(); + + await page.goto(getTestUrl()); + + const { app, getResult } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + `

Hello, World

+ `, + ); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=AssemblyScript'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`export function getTitle(): string {return "AssemblyScript";`); + // workaround for monaco auto-complete + await page.keyboard.press('Delete'); + await page.keyboard.insertText(`}`); + + await app.waitForTimeout(15000); + const resultText = await getResult().innerText('text=Hello, AssemblyScript'); + + expect(resultText).toContain(`Hello, AssemblyScript`); + }); + + test('Python', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, World

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Python'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`from browser import document +title = 'Python' +document['header'].html = f"Hello, {title}"`); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, Python`); + }); + + test('Pyodide', async ({ page, getTestUrl }) => { + test.skip(); + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, World

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Pyodide'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`from js import document +title = 'Python' +document.getElementById('header').innerHTML = f"Hello, {title}"`); + + await waitForResultUpdate(); + await getResult().waitForSelector('text=Hello, Python'); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, Python`); + }); + + test('Ruby', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, world

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Ruby'); + await waitForEditorFocus(app); + + await page.keyboard.insertText(`require "native" +title = 'Ruby' +$$.document.querySelector('#title').innerHTML = title`); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('text=Hello, Ruby'); + + expect(resultText).toContain(`Hello, Ruby`); + }); + + test('Go', async ({ page, getTestUrl, editor }) => { + test.slow(); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, world

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Go'); + await waitForEditorFocus(app); + + await page.keyboard.insertText(`package main +import "syscall/js" +func main() { + js.Global().Get("document").Call("querySelector", "#title").Set("innerHTML", "Golang") +}`); + + await waitForResultUpdate({ delay: 4000, timeout: 60_000 }); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, Golang`); + }); + + test('PHP', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, world

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=PHP'); + await waitForEditorFocus(app); + + // go below pre-inserted 'getElementById('title')->textContent = $title;`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, PHP`); + }); + + test('Perl', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, world

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Perl'); + await waitForEditorFocus(app); + + await page.keyboard.insertText(`use strict; +my $title = 'Perl'; +JS::inline('document.getElementById("title").innerHTML') = $title;`); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, Perl`); + }); + + test('Lua', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, world

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Lua'); + await waitForEditorFocus(app); + + await page.keyboard.insertText(`js = require "js" +window = js.global +document = window.document +document:getElementById("title").innerHTML = "Lua"`); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, Lua`); + }); + + test('Scheme', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, world

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Scheme'); + await waitForEditorFocus(app); + + await page.keyboard.insertText(`(let ((title "Scheme")) + (set-content! "#title" title))`); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, Scheme`); + }); + + test('Go (Yaegi WebAssembly)', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, world

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Go (Wasm)'); + await waitForEditorFocus(app); + + await page.keyboard.insertText(`package main + +import "fmt" + +func main() { + fmt.Println("Hello from Yaegi!") + fmt.Println("Go WebAssembly is working!") +}`); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('body'); + + expect(resultText).toContain(`Hello from Yaegi!`); + expect(resultText).toContain(`Go WebAssembly is working!`); + }); +}); diff --git a/functions/vendors/templates.js b/functions/vendors/templates.js index 10dbce55f7..d5b92aef2c 100644 --- a/functions/vendors/templates.js +++ b/functions/vendors/templates.js @@ -447,10 +447,10 @@ body { font-size: 3.5rem; } } -`.trimStart()},script:{language:"javascript",content:""},stylesheets:["{{ __CDN_URL__ }}bootstrap@5.3.0/dist/css/bootstrap.min.css"],scripts:["{{ __CDN_URL__ }}bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"],cssPreset:"",imports:{},types:{}};var y={name:"coffeescript",title:getTemplateName("templates.starter.coffeescript","CoffeeScript Starter"),thumbnail:"assets/templates/coffeescript.svg",activeEditor:"script",markup:{language:"html",content:` +`.trimStart()},script:{language:"javascript",content:""},stylesheets:["{{ __CDN_URL__ }}bootstrap@5.3.0/dist/css/bootstrap.min.css"],scripts:["{{ __CDN_URL__ }}bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"],cssPreset:"",imports:{},types:{}};var y={name:"civet",title:getTemplateName("templates.starter.civet","Civet Starter"),thumbnail:"assets/templates/civet.png",activeEditor:"script",markup:{language:"html",content:`

Hello, World!

- +

You clicked 0 times.

@@ -463,25 +463,25 @@ body { .logo { width: 150px; } -`.trimStart()},script:{language:"coffeescript",content:` -titleElement = document.getElementById 'title' -counterElement = document.getElementById 'counter' -button = document.getElementById 'counter-button' +`.trimStart()},script:{language:"civet",content:` +titleElement := document.getElementById 'title' +counterElement := document.getElementById 'counter' +button := document.getElementById 'counter-button' -title = 'CoffeeScript' +title := 'Civet' titleElement.innerText = title -counter = (count) -> -> count += 1 -increment = counter 0 +counter := (count: number) => => count += 1 +increment := counter 0 +function handleClick: void counterElement.innerText = increment() -button.addEventListener('click', - -> counterElement.innerText = increment()) -`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var x={name:"go",title:getTemplateName("templates.starter.go","Go Starter"),thumbnail:"assets/templates/go.svg",activeEditor:"script",markup:{language:"html",content:` +button.addEventListener 'click', handleClick +`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var x={name:"clio",title:getTemplateName("templates.starter.clio","Clio Starter"),thumbnail:"assets/templates/clio.png",activeEditor:"script",markup:{language:"html",content:`
-

Hello, World!

- +

Hello, World!

+

You clicked 0 times.

- +
`.trimStart()},style:{language:"css",content:` .container, @@ -490,62 +490,38 @@ button.addEventListener('click', font: 1em sans-serif; } .logo { - width: 250px; + width: 150px; } -`.trimStart()},script:{language:"go",content:` -package main - -import ( - "fmt" - "syscall/js" - "time" -) - -func main() { - title := querySelector("#title") - title.Set("innerHTML", "Golang") +`.trimStart()},script:{language:"clio",content:` +fn capitalize str: + (str.charAt 0 -> .toUpperCase) + (str.slice 1 -> .toLowerCase) - registerCounter() +fn greet name: + f"Hello, {name}!" - // yes, you can use goroutines (check the console) - go greet() - fmt.Println("Hello!") -} +fn setTitle name: + title = document.querySelector "#title" + title.innerText = name -> capitalize -> greet -func querySelector(id string) js.Value { - return js.Global().Get("document").Call("querySelector", id) -} +fn increment value: + (Number value) + 1 -func registerCounter() { - btn := querySelector("#counter-button") - counter := querySelector("#counter") - count := 0 +fn activateBtn btn: + btn.disabled = false + btn.innerText = "Click me" + btn - var cb js.Func - cb = js.FuncOf(func(this js.Value, args []js.Value) interface{} { - count += 1 - counter.Set("innerHTML", count) - return nil - }) - btn.Call("addEventListener", "click", cb) -} +fn onBtnClick: + counter = document.querySelector "#counter" + counter.innerText = increment counter.innerText -func greet() { - if hours, _, _ := time.Now().Clock(); hours < 12 { - fmt.Println("Good morning") - } else if hours < 18 { - fmt.Println("Good afternoon") - } else { - fmt.Println("Good evening") - } -} -`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var w={name:"jquery",title:getTemplateName("templates.starter.jquery","jQuery Starter"),thumbnail:"assets/templates/jquery.svg",activeEditor:"script",markup:{language:"html",content:` -
-

Hello, World!

- -

You clicked 0 times.

- -
+export fn main argv: + setTitle "clio" + document.querySelector "#counter-button" + -> activateBtn + -> .addEventListener "click" onBtnClick +`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var w={name:"clojurescript",title:getTemplateName("templates.starter.clojurescript","ClojureScript Starter"),thumbnail:"assets/templates/cljs.svg",activeEditor:"script",markup:{language:"html",content:` +
Loading...
`.trimStart()},style:{language:"css",content:` .container, .container button { @@ -553,24 +529,43 @@ func greet() { font: 1em sans-serif; } .logo { - width: 300px; + width: 150px; } -`.trimStart()},script:{language:"javascript",content:` -import $ from "jquery"; +`.trimStart()},script:{language:"clojurescript",content:` +(ns react.component + (:require + ;; you may use npm packages + ["canvas-confetti$default" :as confetti] + ["react$default" :as React] + ["react" :refer [useState]] + ["react-dom/client" :refer [createRoot]])) -$("#title").text('jQuery'); +(defn Counter [^:js {:keys [name]}] + (let [[counter setCount] (useState 0)] + #jsx [:div + {:className "container"} + [:h1 (str "Hello, " name "!")] + [:img + {:className "logo" + :alt "logo" + :src "{{ __livecodes_baseUrl__ }}assets/templates/cljs.svg"}] + [:p "You clicked " counter " times."] + [:button + {:onClick (fn [] + (if (= (mod counter 3) 0) (confetti)) + (setCount (inc counter)))} + "Click me"]])) -let count = 0; -$("#counter-button").click(() => { - count += 1; - $("#counter").text(count); -}); -`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var S={name:"knockout",title:getTemplateName("templates.starter.knockout","Knockout Starter"),thumbnail:"assets/templates/knockout.svg",activeEditor:"script",markup:{language:"html",content:` +(def title "ClojureScript") +(print (str "Hello, " title "!")) +(defonce root (createRoot (js/document.querySelector "#app"))) +(.render root #jsx [Counter #js {:name title}]) +`.trimStart()}};var S={name:"coffeescript",title:getTemplateName("templates.starter.coffeescript","CoffeeScript Starter"),thumbnail:"assets/templates/coffeescript.svg",activeEditor:"script",markup:{language:"html",content:`
-

Hello, World!

- -

You clicked 0 times.

- +

Hello, World!

+ +

You clicked 0 times.

+
`.trimStart()},style:{language:"css",content:` .container, @@ -579,27 +574,25 @@ $("#counter-button").click(() => { font: 1em sans-serif; } .logo { - width: 250px; + width: 150px; } -`.trimStart()},script:{language:"javascript",content:` -import ko from "knockout"; +`.trimStart()},script:{language:"coffeescript",content:` +titleElement = document.getElementById 'title' +counterElement = document.getElementById 'counter' +button = document.getElementById 'counter-button' -class ClickCounterViewModel { - constructor() { - this.title = 'Knockout'; - this.numberOfClicks = ko.observable(0); +title = 'CoffeeScript' +titleElement.innerText = title - this.registerClick = function () { - this.numberOfClicks(this.numberOfClicks() + 1); - }; - } -} +counter = (count) -> -> count += 1 +increment = counter 0 -ko.applyBindings(new ClickCounterViewModel()); -`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var k={name:"livescript",title:getTemplateName("templates.starter.livescript","LiveScript Starter"),thumbnail:"assets/templates/livescript.svg",activeEditor:"script",markup:{language:"html",content:` +button.addEventListener('click', + -> counterElement.innerText = increment()) +`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var k={name:"commonlisp",title:getTemplateName("templates.starter.commonlisp","Common Lisp Starter"),thumbnail:"assets/templates/commonlisp.svg",activeEditor:"script",markup:{language:"html",content:`
-

Hello, World!

- +

Hello, World!

+

You clicked 0 times.

@@ -612,31 +605,70 @@ ko.applyBindings(new ClickCounterViewModel()); .logo { width: 150px; } -`.trimStart()},script:{language:"livescript",content:` -{ capitalize, join, map, words } = require 'prelude-ls' - -title = 'live script' -|> words -|> map capitalize -|> join '' - -(document.getElementById \\title).innerText = title +`.trimStart()},script:{language:"commonlisp",content:` +(defun set-attribute (&key selector attribute value) + (let ((node + (#j:document:querySelector selector))) + (setf (jscl::oget node attribute) value) + node)) -increment = (count) -> -> count += 1 -counter = increment 0 +(let ((title "Common Lisp")) + (set-attribute :selector "#title" :attribute "innerHTML" + :value (format nil "Hello, ~A!" title))) -counter-element = document.getElementById \\counter -button = document.getElementById \\counter-button +(let ((counter 0)) + (set-attribute :selector "#counter-button" :attribute "onclick" + :value #'(lambda (ev) + (setf counter (+ counter 1)) + (set-attribute :selector "#counter" :attribute "innerHTML" + :value counter)))) -button.addEventListener \\click, - -> counter-element.innerText = counter! -`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var _={name:"lua",title:getTemplateName("templates.starter.lua","Lua Starter"),thumbnail:"assets/templates/lua.svg",activeEditor:"script",markup:{language:"html",content:` +(#j:console:clear) +(write "Hello, Common Lisp!") +`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var _={name:"cpp",title:getTemplateName("templates.starter.cpp","C++ Starter"),thumbnail:"assets/templates/cpp.svg",activeEditor:"script",markup:{language:"html",content:`
-

Hello, World!

- -

You clicked 0 times.

+

Hello, World!

+ +

You clicked 0 times.

+ + +`.trimStart(), + }, + style: { + language: 'css', + content: ` +.container, +.container button { + text-align: center; + font: 1em sans-serif; +} +.logo { + width: 150px; +} +`.trimStart(), + }, + script: { + language: 'go-wasm', + content: ` +package main + +import "fmt" + +func main() { + fmt.Println("Go (Wasm)") + + // we need to read stdin and increment count + fmt.Println("0") +} +`.trimStart(), + }, +}; diff --git a/src/livecodes/templates/starter/index.ts b/src/livecodes/templates/starter/index.ts index 1075d26834..9f8a22eb10 100644 --- a/src/livecodes/templates/starter/index.ts +++ b/src/livecodes/templates/starter/index.ts @@ -21,6 +21,7 @@ import { diagramsStarter } from './diagrams-starter'; import { fennelStarter } from './fennel-starter'; import { gleamStarter } from './gleam-starter'; import { goStarter } from './go-starter'; +import { goWasmStarter } from './go-wasm-starter'; import { imbaStarter } from './imba-starter'; import { javaStarter } from './java-starter'; import { javascriptStarter } from './javascript-starter'; @@ -111,6 +112,7 @@ export const starterTemplates = [ rubyStarter, rubyWasmStarter, goStarter, + goWasmStarter, phpStarter, phpWasmStarter, cppStarter, diff --git a/src/sdk/models.ts b/src/sdk/models.ts index 9acf3171b4..985b8d35fb 100644 --- a/src/sdk/models.ts +++ b/src/sdk/models.ts @@ -1423,6 +1423,7 @@ export type TemplateName = | 'ruby' | 'ruby-wasm' | 'go' + | 'go-wasm' | 'php' | 'php-wasm' | 'cpp' From f05fe7f92a21969a2fe9c80f8e9a7b54ed8984d5 Mon Sep 17 00:00:00 2001 From: Hatem Hosny Date: Thu, 4 Sep 2025 01:04:27 +0300 Subject: [PATCH 11/23] fixes --- docs/src/components/LanguageSliders.tsx | 1 + docs/src/components/TemplateList.tsx | 1 + src/livecodes/UI/command-menu-actions.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/docs/src/components/LanguageSliders.tsx b/docs/src/components/LanguageSliders.tsx index e1daff5ac6..960c4933a6 100644 --- a/docs/src/components/LanguageSliders.tsx +++ b/docs/src/components/LanguageSliders.tsx @@ -83,6 +83,7 @@ export default function Sliders() { { name: 'ruby', title: 'Ruby' }, { name: 'ruby-wasm', title: 'Ruby (Wasm)' }, { name: 'go', title: 'Go' }, + { name: 'go-wasm', title: 'Go (Wasm)' }, { name: 'php', title: 'PHP' }, { name: 'php-wasm', title: 'PHP (Wasm)' }, { name: 'cpp', title: 'C++' }, diff --git a/docs/src/components/TemplateList.tsx b/docs/src/components/TemplateList.tsx index 126f377f72..a246d4d47a 100644 --- a/docs/src/components/TemplateList.tsx +++ b/docs/src/components/TemplateList.tsx @@ -42,6 +42,7 @@ const templates = [ { name: 'ruby', title: 'Ruby Starter', thumbnail: 'ruby.svg' }, { name: 'ruby-wasm', title: 'Ruby (Wasm) Starter', thumbnail: 'ruby.svg' }, { name: 'go', title: 'Go Starter', thumbnail: 'go.svg' }, + { name: 'go-wasm', title: 'Go (Wasm) Starter', thumbnail: 'go.svg' }, { name: 'php', title: 'PHP Starter', thumbnail: 'php.svg' }, { name: 'php-wasm', title: 'PHP (Wasm) Starter', thumbnail: 'php.svg' }, { name: 'cpp', title: 'C++ Starter', thumbnail: 'cpp.svg' }, diff --git a/src/livecodes/UI/command-menu-actions.ts b/src/livecodes/UI/command-menu-actions.ts index d42de5ca55..708f5ba1f7 100644 --- a/src/livecodes/UI/command-menu-actions.ts +++ b/src/livecodes/UI/command-menu-actions.ts @@ -298,6 +298,7 @@ export const getCommandMenuActions = ({ 'ruby', 'ruby-wasm', 'go', + 'go-wasm', 'php', 'php-wasm', 'cpp', From 3a78adeda8124c87594576488913a5f13cf1520a Mon Sep 17 00:00:00 2001 From: Muhammad Ayman Date: Wed, 10 Sep 2025 22:34:08 +0300 Subject: [PATCH 12/23] Stdin Buffer Management --- functions/vendors/templates.js | 5191 +++++++++-------- .../languages/go-wasm/lang-go-wasm-script.ts | 6 +- .../templates/starter/go-wasm-starter.ts | 225 +- src/livecodes/vendors.ts | 2 +- 4 files changed, 2816 insertions(+), 2608 deletions(-) diff --git a/functions/vendors/templates.js b/functions/vendors/templates.js index 2d666d2b52..58b26099cf 100644 --- a/functions/vendors/templates.js +++ b/functions/vendors/templates.js @@ -447,10 +447,10 @@ body { font-size: 3.5rem; } } -`.trimStart()},script:{language:"javascript",content:""},stylesheets:["{{ __CDN_URL__ }}bootstrap@5.3.0/dist/css/bootstrap.min.css"],scripts:["{{ __CDN_URL__ }}bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"],cssPreset:"",imports:{},types:{}};var y={name:"coffeescript",title:getTemplateName("templates.starter.coffeescript","CoffeeScript Starter"),thumbnail:"assets/templates/coffeescript.svg",activeEditor:"script",markup:{language:"html",content:` +`.trimStart()},script:{language:"javascript",content:""},stylesheets:["{{ __CDN_URL__ }}bootstrap@5.3.0/dist/css/bootstrap.min.css"],scripts:["{{ __CDN_URL__ }}bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"],cssPreset:"",imports:{},types:{}};var y={name:"civet",title:getTemplateName("templates.starter.civet","Civet Starter"),thumbnail:"assets/templates/civet.png",activeEditor:"script",markup:{language:"html",content:`

Hello, World!

- +

You clicked 0 times.

@@ -463,25 +463,25 @@ body { .logo { width: 150px; } -`.trimStart()},script:{language:"coffeescript",content:` -titleElement = document.getElementById 'title' -counterElement = document.getElementById 'counter' -button = document.getElementById 'counter-button' +`.trimStart()},script:{language:"civet",content:` +titleElement := document.getElementById 'title' +counterElement := document.getElementById 'counter' +button := document.getElementById 'counter-button' -title = 'CoffeeScript' +title := 'Civet' titleElement.innerText = title -counter = (count) -> -> count += 1 -increment = counter 0 +counter := (count: number) => => count += 1 +increment := counter 0 +function handleClick: void counterElement.innerText = increment() -button.addEventListener('click', - -> counterElement.innerText = increment()) -`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var x={name:"go",title:getTemplateName("templates.starter.go","Go Starter"),thumbnail:"assets/templates/go.svg",activeEditor:"script",markup:{language:"html",content:` +button.addEventListener 'click', handleClick +`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var x={name:"clio",title:getTemplateName("templates.starter.clio","Clio Starter"),thumbnail:"assets/templates/clio.png",activeEditor:"script",markup:{language:"html",content:`
-

Hello, World!

- +

Hello, World!

+

You clicked 0 times.

- +
`.trimStart()},style:{language:"css",content:` .container, @@ -490,62 +490,38 @@ button.addEventListener('click', font: 1em sans-serif; } .logo { - width: 250px; + width: 150px; } -`.trimStart()},script:{language:"go",content:` -package main - -import ( - "fmt" - "syscall/js" - "time" -) - -func main() { - title := querySelector("#title") - title.Set("innerHTML", "Golang") +`.trimStart()},script:{language:"clio",content:` +fn capitalize str: + (str.charAt 0 -> .toUpperCase) + (str.slice 1 -> .toLowerCase) - registerCounter() +fn greet name: + f"Hello, {name}!" - // yes, you can use goroutines (check the console) - go greet() - fmt.Println("Hello!") -} +fn setTitle name: + title = document.querySelector "#title" + title.innerText = name -> capitalize -> greet -func querySelector(id string) js.Value { - return js.Global().Get("document").Call("querySelector", id) -} +fn increment value: + (Number value) + 1 -func registerCounter() { - btn := querySelector("#counter-button") - counter := querySelector("#counter") - count := 0 +fn activateBtn btn: + btn.disabled = false + btn.innerText = "Click me" + btn - var cb js.Func - cb = js.FuncOf(func(this js.Value, args []js.Value) interface{} { - count += 1 - counter.Set("innerHTML", count) - return nil - }) - btn.Call("addEventListener", "click", cb) -} +fn onBtnClick: + counter = document.querySelector "#counter" + counter.innerText = increment counter.innerText -func greet() { - if hours, _, _ := time.Now().Clock(); hours < 12 { - fmt.Println("Good morning") - } else if hours < 18 { - fmt.Println("Good afternoon") - } else { - fmt.Println("Good evening") - } -} -`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var w={name:"jquery",title:getTemplateName("templates.starter.jquery","jQuery Starter"),thumbnail:"assets/templates/jquery.svg",activeEditor:"script",markup:{language:"html",content:` -
-

Hello, World!

- -

You clicked 0 times.

- -
+export fn main argv: + setTitle "clio" + document.querySelector "#counter-button" + -> activateBtn + -> .addEventListener "click" onBtnClick +`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var w={name:"clojurescript",title:getTemplateName("templates.starter.clojurescript","ClojureScript Starter"),thumbnail:"assets/templates/cljs.svg",activeEditor:"script",markup:{language:"html",content:` +
Loading...
`.trimStart()},style:{language:"css",content:` .container, .container button { @@ -553,24 +529,43 @@ func greet() { font: 1em sans-serif; } .logo { - width: 300px; + width: 150px; } -`.trimStart()},script:{language:"javascript",content:` -import $ from "jquery"; +`.trimStart()},script:{language:"clojurescript",content:` +(ns react.component + (:require + ;; you may use npm packages + ["canvas-confetti$default" :as confetti] + ["react$default" :as React] + ["react" :refer [useState]] + ["react-dom/client" :refer [createRoot]])) -$("#title").text('jQuery'); +(defn Counter [^:js {:keys [name]}] + (let [[counter setCount] (useState 0)] + #jsx [:div + {:className "container"} + [:h1 (str "Hello, " name "!")] + [:img + {:className "logo" + :alt "logo" + :src "{{ __livecodes_baseUrl__ }}assets/templates/cljs.svg"}] + [:p "You clicked " counter " times."] + [:button + {:onClick (fn [] + (if (= (mod counter 3) 0) (confetti)) + (setCount (inc counter)))} + "Click me"]])) -let count = 0; -$("#counter-button").click(() => { - count += 1; - $("#counter").text(count); -}); -`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var S={name:"knockout",title:getTemplateName("templates.starter.knockout","Knockout Starter"),thumbnail:"assets/templates/knockout.svg",activeEditor:"script",markup:{language:"html",content:` +(def title "ClojureScript") +(print (str "Hello, " title "!")) +(defonce root (createRoot (js/document.querySelector "#app"))) +(.render root #jsx [Counter #js {:name title}]) +`.trimStart()}};var S={name:"coffeescript",title:getTemplateName("templates.starter.coffeescript","CoffeeScript Starter"),thumbnail:"assets/templates/coffeescript.svg",activeEditor:"script",markup:{language:"html",content:`
-

Hello, World!

- -

You clicked 0 times.

- +

Hello, World!

+ +

You clicked 0 times.

+
`.trimStart()},style:{language:"css",content:` .container, @@ -579,27 +574,25 @@ $("#counter-button").click(() => { font: 1em sans-serif; } .logo { - width: 250px; + width: 150px; } -`.trimStart()},script:{language:"javascript",content:` -import ko from "knockout"; +`.trimStart()},script:{language:"coffeescript",content:` +titleElement = document.getElementById 'title' +counterElement = document.getElementById 'counter' +button = document.getElementById 'counter-button' -class ClickCounterViewModel { - constructor() { - this.title = 'Knockout'; - this.numberOfClicks = ko.observable(0); +title = 'CoffeeScript' +titleElement.innerText = title - this.registerClick = function () { - this.numberOfClicks(this.numberOfClicks() + 1); - }; - } -} +counter = (count) -> -> count += 1 +increment = counter 0 -ko.applyBindings(new ClickCounterViewModel()); -`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var k={name:"livescript",title:getTemplateName("templates.starter.livescript","LiveScript Starter"),thumbnail:"assets/templates/livescript.svg",activeEditor:"script",markup:{language:"html",content:` +button.addEventListener('click', + -> counterElement.innerText = increment()) +`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var k={name:"commonlisp",title:getTemplateName("templates.starter.commonlisp","Common Lisp Starter"),thumbnail:"assets/templates/commonlisp.svg",activeEditor:"script",markup:{language:"html",content:`
-

Hello, World!

- +

Hello, World!

+

You clicked 0 times.

@@ -612,31 +605,70 @@ ko.applyBindings(new ClickCounterViewModel()); .logo { width: 150px; } -`.trimStart()},script:{language:"livescript",content:` -{ capitalize, join, map, words } = require 'prelude-ls' - -title = 'live script' -|> words -|> map capitalize -|> join '' - -(document.getElementById \\title).innerText = title +`.trimStart()},script:{language:"commonlisp",content:` +(defun set-attribute (&key selector attribute value) + (let ((node + (#j:document:querySelector selector))) + (setf (jscl::oget node attribute) value) + node)) -increment = (count) -> -> count += 1 -counter = increment 0 +(let ((title "Common Lisp")) + (set-attribute :selector "#title" :attribute "innerHTML" + :value (format nil "Hello, ~A!" title))) -counter-element = document.getElementById \\counter -button = document.getElementById \\counter-button +(let ((counter 0)) + (set-attribute :selector "#counter-button" :attribute "onclick" + :value #'(lambda (ev) + (setf counter (+ counter 1)) + (set-attribute :selector "#counter" :attribute "innerHTML" + :value counter)))) -button.addEventListener \\click, - -> counter-element.innerText = counter! -`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var _={name:"lua",title:getTemplateName("templates.starter.lua","Lua Starter"),thumbnail:"assets/templates/lua.svg",activeEditor:"script",markup:{language:"html",content:` +(#j:console:clear) +(write "Hello, Common Lisp!") +`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var _={name:"cpp",title:getTemplateName("templates.starter.cpp","C++ Starter"),thumbnail:"assets/templates/cpp.svg",activeEditor:"script",markup:{language:"html",content:`
-

Hello, World!

- -

You clicked 0 times.

+

Hello, World!

+ +

You clicked 0 times.

+ + `.trimStart(), @@ -59,13 +104,71 @@ export const goWasmStarter: Template = { style: { language: 'css', content: ` -.container, -.container button { - text-align: center; - font: 1em sans-serif; +.container { + max-width: 800px; + margin: 0 auto; + padding: 20px; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } + .logo { width: 150px; + display: block; + margin: 20px auto; +} + +.demo-section { + background: #f5f5f5; + padding: 20px; + margin: 20px 0; + border-radius: 8px; + border-left: 4px solid #00add8; +} + +.demo-section h2 { + margin-top: 0; + color: #333; +} + +button { + background: #00add8; + color: white; + border: none; + padding: 10px 20px; + border-radius: 4px; + cursor: pointer; + font-size: 16px; + margin: 10px 5px; +} + +button:hover:not(:disabled) { + background: #0099c7; +} + +button:disabled { + background: #ccc; + cursor: not-allowed; +} + +input[type="text"], input[type="number"] { + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 16px; + margin: 5px; + width: 200px; +} + +#counter { + font-weight: bold; + color: #00add8; + font-size: 24px; +} + +#greeting, #result { + font-weight: bold; + color: #333; + margin-top: 10px; } `.trimStart(), }, @@ -74,13 +177,53 @@ export const goWasmStarter: Template = { content: ` package main -import "fmt" +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) func main() { - fmt.Println("Go (Wasm)") - - // we need to read stdin and increment count - fmt.Println("0") + // Read input from stdin + scanner := bufio.NewScanner(os.Stdin) + + if scanner.Scan() { + input := strings.TrimSpace(scanner.Text()) + + // Try to parse as number (for counter demo) + if count, err := strconv.Atoi(input); err == nil { + // Counter demo - just return the number + fmt.Println(count) + return + } + + // Try to parse as two numbers separated by newline (for math demo) + if strings.Contains(input, "\\n") { + parts := strings.Split(input, "\\n") + if len(parts) == 2 { + if num1, err1 := strconv.Atoi(parts[0]); err1 == nil { + if num2, err2 := strconv.Atoi(parts[1]); err2 == nil { + // Math demo + sum := num1 + num2 + product := num1 * num2 + fmt.Printf("Sum: %d + %d = %d\\n", num1, num2, sum) + fmt.Printf("Product: %d × %d = %d\\n", num1, num2, product) + return + } + } + } + } + + // Greeting demo - treat as name + fmt.Printf("Hello, %s! Welcome to Go WebAssembly!\\n", input) + fmt.Println("This is running in your browser using Go compiled to WebAssembly.") + } else { + // No input provided + fmt.Println("Hello from Go WebAssembly!") + fmt.Println("This program demonstrates stdin handling in Go WASM.") + } } `.trimStart(), }, diff --git a/src/livecodes/vendors.ts b/src/livecodes/vendors.ts index 451794b133..2207984b32 100644 --- a/src/livecodes/vendors.ts +++ b/src/livecodes/vendors.ts @@ -99,7 +99,7 @@ export const cppWasmBaseUrl = /* @__PURE__ */ getUrl('@chriskoch/cpp-wasm@1.0.2/ export const csharpWasmBaseUrl = /* @__PURE__ */ getUrl('@seth0x41/csharp-wasm@1.0.3/'); -export const yaegiWasmBaseUrl = /* @__PURE__ */ getUrl('yaegi-wasm@1.0.1/src/'); +export const yaegiWasmBaseUrl = /* @__PURE__ */ getUrl('yaegi-wasm@1.0.2/src/'); export const csstreeUrl = /* @__PURE__ */ getUrl('css-tree@2.3.1/dist/csstree.js'); From 9208fb847afe864bde28f448634189d08fe7afb8 Mon Sep 17 00:00:00 2001 From: Muhammad Ayman Date: Wed, 10 Sep 2025 22:44:50 +0300 Subject: [PATCH 13/23] JavaScript gets current count from Ui --- src/livecodes/templates/starter/go-wasm-starter.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/livecodes/templates/starter/go-wasm-starter.ts b/src/livecodes/templates/starter/go-wasm-starter.ts index b20701ae72..91dcb1a2a2 100644 --- a/src/livecodes/templates/starter/go-wasm-starter.ts +++ b/src/livecodes/templates/starter/go-wasm-starter.ts @@ -53,9 +53,8 @@ export const goWasmStarter: Template = { // Counter demo incrementBtn.onclick = async () => { const currentCount = parseInt(document.querySelector("#counter").textContent); - const newCount = currentCount + 1; - const {output, error} = await livecodes.goWasm.run(newCount.toString()); + const {output, error} = await livecodes.goWasm.run(currentCount.toString()); if (error) { console.error('Error:', error); } else { @@ -194,8 +193,9 @@ func main() { // Try to parse as number (for counter demo) if count, err := strconv.Atoi(input); err == nil { - // Counter demo - just return the number - fmt.Println(count) + // Counter demo - increment and return the new number + newCount := count + 1 + fmt.Println(newCount) return } From 8e77243b8ed4a1c91a67e08a6787b2d71112ab60 Mon Sep 17 00:00:00 2001 From: Muhammad Ayman Date: Wed, 10 Sep 2025 22:46:22 +0300 Subject: [PATCH 14/23] fix Loading... issue --- src/livecodes/templates/starter/go-wasm-starter.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/livecodes/templates/starter/go-wasm-starter.ts b/src/livecodes/templates/starter/go-wasm-starter.ts index 91dcb1a2a2..7bef3c2418 100644 --- a/src/livecodes/templates/starter/go-wasm-starter.ts +++ b/src/livecodes/templates/starter/go-wasm-starter.ts @@ -45,10 +45,13 @@ export const goWasmStarter: Template = { const greetBtn = document.querySelector("#greet-btn"); const calculateBtn = document.querySelector("#calculate-btn"); - // Enable buttons + // Enable buttons and update text incrementBtn.disabled = false; + incrementBtn.textContent = "Increment"; greetBtn.disabled = false; + greetBtn.textContent = "Greet"; calculateBtn.disabled = false; + calculateBtn.textContent = "Calculate"; // Counter demo incrementBtn.onclick = async () => { From 169f23cd37a5624d17274a7484f86d356aa09d27 Mon Sep 17 00:00:00 2001 From: Muhammad Ayman Date: Wed, 10 Sep 2025 22:59:25 +0300 Subject: [PATCH 15/23] finish the starter --- .../templates/starter/go-wasm-starter.ts | 47 +------------------ 1 file changed, 2 insertions(+), 45 deletions(-) diff --git a/src/livecodes/templates/starter/go-wasm-starter.ts b/src/livecodes/templates/starter/go-wasm-starter.ts index 7bef3c2418..a280059656 100644 --- a/src/livecodes/templates/starter/go-wasm-starter.ts +++ b/src/livecodes/templates/starter/go-wasm-starter.ts @@ -26,14 +26,7 @@ export const goWasmStarter: Template = {

-
-

Math Operations

-

Enter two numbers:

- - - -

-
+ `.trimStart(), @@ -202,22 +174,7 @@ func main() { return } - // Try to parse as two numbers separated by newline (for math demo) - if strings.Contains(input, "\\n") { - parts := strings.Split(input, "\\n") - if len(parts) == 2 { - if num1, err1 := strconv.Atoi(parts[0]); err1 == nil { - if num2, err2 := strconv.Atoi(parts[1]); err2 == nil { - // Math demo - sum := num1 + num2 - product := num1 * num2 - fmt.Printf("Sum: %d + %d = %d\\n", num1, num2, sum) - fmt.Printf("Product: %d × %d = %d\\n", num1, num2, product) - return - } - } - } - } + // Greeting demo - treat as name fmt.Printf("Hello, %s! Welcome to Go WebAssembly!\\n", input) From b5e57a8fc72a794a968d03fadc2525053d0b36dd Mon Sep 17 00:00:00 2001 From: Muhammad Ayman Date: Thu, 11 Sep 2025 12:44:39 +0300 Subject: [PATCH 16/23] add info --- functions/vendors/templates.js | 205 +++++++++++++----- src/livecodes/html/language-info.html | 8 + .../i18n/locales/en/language-info.ts | 5 + 3 files changed, 167 insertions(+), 51 deletions(-) diff --git a/functions/vendors/templates.js b/functions/vendors/templates.js index 58b26099cf..2bbc7ed68f 100644 --- a/functions/vendors/templates.js +++ b/functions/vendors/templates.js @@ -1029,7 +1029,7 @@ svg .container h3:not(:nth-child(1)) { margin-top: 3em; } -`.trimStart()},script:{language:"javascript",content:""},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var $={name:"fennel",title:getTemplateName("templates.starter.fennel","Fennel Starter"),thumbnail:"assets/templates/fennel.svg",activeEditor:"script",markup:{language:"html",content:` +`.trimStart()},script:{language:"javascript",content:""},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var q={name:"fennel",title:getTemplateName("templates.starter.fennel","Fennel Starter"),thumbnail:"assets/templates/fennel.svg",activeEditor:"script",markup:{language:"html",content:`

Hello, World!

@@ -1063,7 +1063,7 @@ svg (global counter (Counter:new nil)) (global button (document:querySelector "#counter-button")) (button:addEventListener :click (fn [] (counter:increment) (counter:show))) -`.trimStart()}};var L=["esm.sh","skypack","esm.run","jsdelivr.esm","fastly.jsdelivr.esm","gcore.jsdelivr.esm","testingcf.jsdelivr.esm","jsdelivr.b-cdn.esm","jspm"],B=["jsdelivr","fastly.jsdelivr","unpkg","gcore.jsdelivr","testingcf.jsdelivr","jsdelivr.b-cdn","npmcdn"],M=["jsdelivr.gh","fastly.jsdelivr.gh","statically","gcore.jsdelivr.gh","testingcf.jsdelivr.gh","jsdelivr.b-cdn.gh"],r={getModuleUrl:(e,{isModule:s=!0,defaultCDN:i="esm.sh",external:o}={})=>{e=e.replace(/#nobundle/g,"");let a=n=>!o||!n.includes("https://esm.sh")?n:n.includes("?")?`${n}&external=${o}`:`${n}?external=${o}`,l=q(e,s,i);return l?a(l):s?a("https://esm.sh/"+e):"https://cdn.jsdelivr.net/npm/"+e},getUrl:(e,s)=>e.startsWith("http")||e.startsWith("data:")?e:q(e,!1,s||$t())||e,cdnLists:{npm:B,module:L,gh:M},checkCDNs:async(e,s)=>{let i=[s,...r.cdnLists.npm].filter(Boolean);for(let o of i)try{if((await fetch(r.getUrl(e,o),{method:"HEAD"})).ok)return o}catch{}return r.cdnLists.npm[0]}},$t=()=>{if(globalThis.appCDN)return globalThis.appCDN;try{return new URL(location.href).searchParams.get("appCDN")||r.cdnLists.npm[0]}catch{return r.cdnLists.npm[0]}},q=(e,s,i)=>{let o=s&&e.startsWith("unpkg:")?"?module":"";e.startsWith("gh:")?e=e.replace("gh",M[0]):e.includes(":")||(e=(i||(s?L[0]:B[0]))+":"+e);for(let a of qt){let[l,n]=a;if(l.test(e))return e.replace(l,n)+o}return null},qt=[[/^(esm\.sh:)(.+)/i,"https://esm.sh/$2"],[/^(npm:)(.+)/i,"https://esm.sh/$2"],[/^(node:)(.+)/i,"https://esm.sh/$2"],[/^(jsr:)(.+)/i,"https://esm.sh/jsr/$2"],[/^(pr:)(.+)/i,"https://esm.sh/pr/$2"],[/^(pkg\.pr\.new:)(.+)/i,"https://esm.sh/pkg.pr.new/$2"],[/^(skypack:)(.+)/i,"https://cdn.skypack.dev/$2"],[/^(jsdelivr:)(.+)/i,"https://cdn.jsdelivr.net/npm/$2"],[/^(fastly\.jsdelivr:)(.+)/i,"https://fastly.jsdelivr.net/npm/$2"],[/^(gcore\.jsdelivr:)(.+)/i,"https://gcore.jsdelivr.net/npm/$2"],[/^(testingcf\.jsdelivr:)(.+)/i,"https://testingcf.jsdelivr.net/npm/$2"],[/^(jsdelivr\.b-cdn:)(.+)/i,"https://jsdelivr.b-cdn.net/npm/$2"],[/^(jsdelivr\.gh:)(.+)/i,"https://cdn.jsdelivr.net/gh/$2"],[/^(fastly\.jsdelivr\.gh:)(.+)/i,"https://fastly.jsdelivr.net/gh/$2"],[/^(gcore\.jsdelivr\.gh:)(.+)/i,"https://gcore.jsdelivr.net/gh/$2"],[/^(testingcf\.jsdelivr\.gh:)(.+)/i,"https://testingcf.jsdelivr.net/gh/$2"],[/^(jsdelivr\.b-cdn\.gh:)(.+)/i,"https://jsdelivr.b-cdn.net/gh/$2"],[/^(statically:)(.+)/i,"https://cdn.statically.io/gh/$2"],[/^(esm\.run:)(.+)/i,"https://esm.run/$2"],[/^(jsdelivr\.esm:)(.+)/i,"https://cdn.jsdelivr.net/npm/$2/+esm"],[/^(fastly\.jsdelivr\.esm:)(.+)/i,"https://fastly.jsdelivr.net/npm/$2/+esm"],[/^(gcore\.jsdelivr\.esm:)(.+)/i,"https://gcore.jsdelivr.net/npm/$2/+esm"],[/^(testingcf\.jsdelivr\.esm:)(.+)/i,"https://testingcf.jsdelivr.net/npm/$2/+esm"],[/^(jsdelivr\.b-cdn\.esm:)(.+)/i,"https://jsdelivr.b-cdn.net/npm/$2/+esm"],[/^(jspm:)(.+)/i,"https://jspm.dev/$2"],[/^(esbuild:)(.+)/i,"https://esbuild.vercel.app/$2"],[/^(bundle\.run:)(.+)/i,"https://bundle.run/$2"],[/^(unpkg:)(.+)/i,"https://unpkg.com/$2"],[/^(npmcdn:)(.+)/i,"https://npmcdn.com/$2"],[/^(bundlejs:)(.+)/i,"https://deno.bundlejs.com/?file&q=$2"],[/^(bundle:)(.+)/i,"https://deno.bundlejs.com/?file&q=$2"],[/^(deno:)(.+)/i,"https://deno.bundlejs.com/?file&q=https://deno.land/x/$2/mod.ts"],[/^(https:\/\/deno\.land\/.+)/i,"https://deno.bundlejs.com/?file&q=$1"],[/^(github:|https:\/\/github\.com\/)(.[^\/]+?)\/(.[^\/]+?)\/(?!releases\/)(?:(?:blob|raw)\/)?(.+?\/.+)/i,"https://deno.bundlejs.com/?file&q=https://cdn.jsdelivr.net/gh/$2/$3@$4"],[/^(gist\.github:)(.+?\/[0-9a-f]+\/raw\/(?:[0-9a-f]+\/)?.+)$/i,"https://gist.githack.com/$2"],[/^(gitlab:|https:\/\/gitlab\.com\/)([^\/]+.*\/[^\/]+)\/(?:raw|blob)\/(.+?)(?:\?.*)?$/i,"https://deno.bundlejs.com/?file&q=https://gl.githack.com/$2/raw/$3"],[/^(bitbucket:|https:\/\/bitbucket\.org\/)([^\/]+\/[^\/]+)\/(?:raw|src)\/(.+?)(?:\?.*)?$/i,"https://deno.bundlejs.com/?file&q=https://bb.githack.com/$2/raw/$3"],[/^(bitbucket:)snippets\/([^\/]+\/[^\/]+)\/revisions\/([^\/\#\?]+)(?:\?[^#]*)?(?:\#file-(.+?))$/i,"https://bb.githack.com/!api/2.0/snippets/$2/$3/files/$4"],[/^(bitbucket:)snippets\/([^\/]+\/[^\/\#\?]+)(?:\?[^#]*)?(?:\#file-(.+?))$/i,"https://bb.githack.com/!api/2.0/snippets/$2/HEAD/files/$3"],[/^(bitbucket:)\!api\/2.0\/snippets\/([^\/]+\/[^\/]+\/[^\/]+)\/files\/(.+?)(?:\?.*)?$/i,"https://bb.githack.com/!api/2.0/snippets/$2/files/$3"],[/^(api\.bitbucket:)2.0\/snippets\/([^\/]+\/[^\/]+\/[^\/]+)\/files\/(.+?)(?:\?.*)?$/i,"https://bb.githack.com/!api/2.0/snippets/$2/files/$3"],[/^(rawgit:)(.+?\/[0-9a-f]+\/raw\/(?:[0-9a-f]+\/)?.+)$/i,"https://gist.githack.com/$2"],[/^(rawgit:|https:\/\/raw\.githubusercontent\.com)(\/[^\/]+\/[^\/]+|[0-9A-Za-z-]+\/[0-9a-f]+\/raw)\/(.+)/i,"https://deno.bundlejs.com/?file&q=https://raw.githack.com/$2/$3"]];var{getUrl:Lt,getModuleUrl:te}=r;var c=Lt("gh:live-codes/gleam-precompiled@v0.5.0/");var p=c+"build/packages/plinth/src/plinth/",m=c+"build/dev/javascript/plinth/plinth/",P={name:"gleam",title:getTemplateName("templates.starter.gleam","Gleam Starter"),thumbnail:"assets/templates/gleam.svg",activeEditor:"script",markup:{language:"html",content:` +`.trimStart()}};var L=["esm.sh","skypack","esm.run","jsdelivr.esm","fastly.jsdelivr.esm","gcore.jsdelivr.esm","testingcf.jsdelivr.esm","jsdelivr.b-cdn.esm","jspm"],B=["jsdelivr","fastly.jsdelivr","unpkg","gcore.jsdelivr","testingcf.jsdelivr","jsdelivr.b-cdn","npmcdn"],M=["jsdelivr.gh","fastly.jsdelivr.gh","statically","gcore.jsdelivr.gh","testingcf.jsdelivr.gh","jsdelivr.b-cdn.gh"],r={getModuleUrl:(e,{isModule:s=!0,defaultCDN:i="esm.sh",external:o}={})=>{e=e.replace(/#nobundle/g,"");let a=n=>!o||!n.includes("https://esm.sh")?n:n.includes("?")?`${n}&external=${o}`:`${n}?external=${o}`,l=$(e,s,i);return l?a(l):s?a("https://esm.sh/"+e):"https://cdn.jsdelivr.net/npm/"+e},getUrl:(e,s)=>e.startsWith("http")||e.startsWith("data:")?e:$(e,!1,s||qt())||e,cdnLists:{npm:B,module:L,gh:M},checkCDNs:async(e,s)=>{let i=[s,...r.cdnLists.npm].filter(Boolean);for(let o of i)try{if((await fetch(r.getUrl(e,o),{method:"HEAD"})).ok)return o}catch{}return r.cdnLists.npm[0]}},qt=()=>{if(globalThis.appCDN)return globalThis.appCDN;try{return new URL(location.href).searchParams.get("appCDN")||r.cdnLists.npm[0]}catch{return r.cdnLists.npm[0]}},$=(e,s,i)=>{let o=s&&e.startsWith("unpkg:")?"?module":"";e.startsWith("gh:")?e=e.replace("gh",M[0]):e.includes(":")||(e=(i||(s?L[0]:B[0]))+":"+e);for(let a of $t){let[l,n]=a;if(l.test(e))return e.replace(l,n)+o}return null},$t=[[/^(esm\.sh:)(.+)/i,"https://esm.sh/$2"],[/^(npm:)(.+)/i,"https://esm.sh/$2"],[/^(node:)(.+)/i,"https://esm.sh/$2"],[/^(jsr:)(.+)/i,"https://esm.sh/jsr/$2"],[/^(pr:)(.+)/i,"https://esm.sh/pr/$2"],[/^(pkg\.pr\.new:)(.+)/i,"https://esm.sh/pkg.pr.new/$2"],[/^(skypack:)(.+)/i,"https://cdn.skypack.dev/$2"],[/^(jsdelivr:)(.+)/i,"https://cdn.jsdelivr.net/npm/$2"],[/^(fastly\.jsdelivr:)(.+)/i,"https://fastly.jsdelivr.net/npm/$2"],[/^(gcore\.jsdelivr:)(.+)/i,"https://gcore.jsdelivr.net/npm/$2"],[/^(testingcf\.jsdelivr:)(.+)/i,"https://testingcf.jsdelivr.net/npm/$2"],[/^(jsdelivr\.b-cdn:)(.+)/i,"https://jsdelivr.b-cdn.net/npm/$2"],[/^(jsdelivr\.gh:)(.+)/i,"https://cdn.jsdelivr.net/gh/$2"],[/^(fastly\.jsdelivr\.gh:)(.+)/i,"https://fastly.jsdelivr.net/gh/$2"],[/^(gcore\.jsdelivr\.gh:)(.+)/i,"https://gcore.jsdelivr.net/gh/$2"],[/^(testingcf\.jsdelivr\.gh:)(.+)/i,"https://testingcf.jsdelivr.net/gh/$2"],[/^(jsdelivr\.b-cdn\.gh:)(.+)/i,"https://jsdelivr.b-cdn.net/gh/$2"],[/^(statically:)(.+)/i,"https://cdn.statically.io/gh/$2"],[/^(esm\.run:)(.+)/i,"https://esm.run/$2"],[/^(jsdelivr\.esm:)(.+)/i,"https://cdn.jsdelivr.net/npm/$2/+esm"],[/^(fastly\.jsdelivr\.esm:)(.+)/i,"https://fastly.jsdelivr.net/npm/$2/+esm"],[/^(gcore\.jsdelivr\.esm:)(.+)/i,"https://gcore.jsdelivr.net/npm/$2/+esm"],[/^(testingcf\.jsdelivr\.esm:)(.+)/i,"https://testingcf.jsdelivr.net/npm/$2/+esm"],[/^(jsdelivr\.b-cdn\.esm:)(.+)/i,"https://jsdelivr.b-cdn.net/npm/$2/+esm"],[/^(jspm:)(.+)/i,"https://jspm.dev/$2"],[/^(esbuild:)(.+)/i,"https://esbuild.vercel.app/$2"],[/^(bundle\.run:)(.+)/i,"https://bundle.run/$2"],[/^(unpkg:)(.+)/i,"https://unpkg.com/$2"],[/^(npmcdn:)(.+)/i,"https://npmcdn.com/$2"],[/^(bundlejs:)(.+)/i,"https://deno.bundlejs.com/?file&q=$2"],[/^(bundle:)(.+)/i,"https://deno.bundlejs.com/?file&q=$2"],[/^(deno:)(.+)/i,"https://deno.bundlejs.com/?file&q=https://deno.land/x/$2/mod.ts"],[/^(https:\/\/deno\.land\/.+)/i,"https://deno.bundlejs.com/?file&q=$1"],[/^(github:|https:\/\/github\.com\/)(.[^\/]+?)\/(.[^\/]+?)\/(?!releases\/)(?:(?:blob|raw)\/)?(.+?\/.+)/i,"https://deno.bundlejs.com/?file&q=https://cdn.jsdelivr.net/gh/$2/$3@$4"],[/^(gist\.github:)(.+?\/[0-9a-f]+\/raw\/(?:[0-9a-f]+\/)?.+)$/i,"https://gist.githack.com/$2"],[/^(gitlab:|https:\/\/gitlab\.com\/)([^\/]+.*\/[^\/]+)\/(?:raw|blob)\/(.+?)(?:\?.*)?$/i,"https://deno.bundlejs.com/?file&q=https://gl.githack.com/$2/raw/$3"],[/^(bitbucket:|https:\/\/bitbucket\.org\/)([^\/]+\/[^\/]+)\/(?:raw|src)\/(.+?)(?:\?.*)?$/i,"https://deno.bundlejs.com/?file&q=https://bb.githack.com/$2/raw/$3"],[/^(bitbucket:)snippets\/([^\/]+\/[^\/]+)\/revisions\/([^\/\#\?]+)(?:\?[^#]*)?(?:\#file-(.+?))$/i,"https://bb.githack.com/!api/2.0/snippets/$2/$3/files/$4"],[/^(bitbucket:)snippets\/([^\/]+\/[^\/\#\?]+)(?:\?[^#]*)?(?:\#file-(.+?))$/i,"https://bb.githack.com/!api/2.0/snippets/$2/HEAD/files/$3"],[/^(bitbucket:)\!api\/2.0\/snippets\/([^\/]+\/[^\/]+\/[^\/]+)\/files\/(.+?)(?:\?.*)?$/i,"https://bb.githack.com/!api/2.0/snippets/$2/files/$3"],[/^(api\.bitbucket:)2.0\/snippets\/([^\/]+\/[^\/]+\/[^\/]+)\/files\/(.+?)(?:\?.*)?$/i,"https://bb.githack.com/!api/2.0/snippets/$2/files/$3"],[/^(rawgit:)(.+?\/[0-9a-f]+\/raw\/(?:[0-9a-f]+\/)?.+)$/i,"https://gist.githack.com/$2"],[/^(rawgit:|https:\/\/raw\.githubusercontent\.com)(\/[^\/]+\/[^\/]+|[0-9A-Za-z-]+\/[0-9a-f]+\/raw)\/(.+)/i,"https://deno.bundlejs.com/?file&q=https://raw.githack.com/$2/$3"]];var{getUrl:Lt,getModuleUrl:te}=r;var c=Lt("gh:live-codes/gleam-precompiled@v0.5.0/");var p=c+"build/packages/plinth/src/plinth/",m=c+"build/dev/javascript/plinth/plinth/",P={name:"gleam",title:getTemplateName("templates.starter.gleam","Gleam Starter"),thumbnail:"assets/templates/gleam.svg",activeEditor:"script",markup:{language:"html",content:`

Hello, World!

@@ -1131,7 +1131,7 @@ pub fn hello(str: String) -> String // npm module @external(javascript, "npm:cowsay2", "say") pub fn cowsay(str: String) -> String -`.trimStart()},customSettings:{imports:{"my_pkg/greet.js":c+"demo/greet.js"},gleam:{modules:{"plinth/browser/document":{srcUrl:p+"browser/document.gleam",compiledUrl:m+"browser/document.mjs"},"plinth/browser/element":{srcUrl:p+"browser/element.gleam",compiledUrl:m+"browser/element.mjs"},"plinth/browser/event":{srcUrl:p+"browser/event.gleam",compiledUrl:m+"browser/event.mjs"}}}}};var N={name:"go",title:getTemplateName("templates.starter.go","Go Starter"),thumbnail:"assets/templates/go.svg",activeEditor:"script",markup:{language:"html",content:` +`.trimStart()},customSettings:{imports:{"my_pkg/greet.js":c+"demo/greet.js"},gleam:{modules:{"plinth/browser/document":{srcUrl:p+"browser/document.gleam",compiledUrl:m+"browser/document.mjs"},"plinth/browser/element":{srcUrl:p+"browser/element.gleam",compiledUrl:m+"browser/element.mjs"},"plinth/browser/event":{srcUrl:p+"browser/event.gleam",compiledUrl:m+"browser/event.mjs"}}}}};var R={name:"go",title:getTemplateName("templates.starter.go","Go Starter"),thumbnail:"assets/templates/go.svg",activeEditor:"script",markup:{language:"html",content:`

Hello, World!

@@ -1194,72 +1194,175 @@ func greet() { fmt.Println("Good evening") } } -`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var R={name:"go-wasm",title:"C++ (Wasm) Starter",thumbnail:"assets/templates/go.svg",activeEditor:"script",markup:{language:"html",content:` +`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var N={name:"go-wasm",title:"Go (Wasm) Starter",thumbnail:"assets/templates/go.svg",activeEditor:"script",markup:{language:"html",content:`
-

Hello, World!

- -

You clicked 0 times.

- +

Go WebAssembly Demo

+ + +
+

Interactive Counter

+

Current count: 0

+ +
+ +
+

Stdin Input Demo

+

Enter your name:

+ + +

+
+ +