diff --git a/.nvmrc b/.nvmrc index d78bf0a56c..564e92d084 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v18.20.4 +v24.4.1 diff --git a/Dockerfile b/Dockerfile index 65402a492c..65b79df75e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:24.1.0-alpine3.21 AS builder +FROM node:24.4.1-alpine3.22 AS builder RUN apk update --no-cache && apk add --no-cache git @@ -11,9 +11,9 @@ COPY server/package*.json server/ RUN npm ci -COPY . . - ARG SELF_HOSTED +ARG HOST_NAME +ARG PORT ARG SELF_HOSTED_SHARE ARG SELF_HOSTED_BROADCAST ARG BROADCAST_PORT @@ -21,13 +21,25 @@ ARG SANDBOX_HOST_NAME ARG SANDBOX_PORT ARG FIREBASE_CONFIG ARG DOCS_BASE_URL +ARG LOCAL_MODULES +ARG NODE_OPTIONS + +COPY scripts/download-modules.js scripts/ +COPY src/livecodes/vendors.ts src/livecodes/ +COPY src/sdk/package.sdk.json src/sdk/ + +RUN if [ "$LOCAL_MODULES" == "true" ]; \ + then npm run download-modules; \ + fi + +COPY . . RUN if [ "$DOCS_BASE_URL" == "null" ]; \ then npm run build:app; \ else npm run build; \ fi -FROM node:24.1.0-alpine3.21 AS server +FROM node:24.4.1-alpine3.22 AS server RUN addgroup -S appgroup RUN adduser -S appuser -G appgroup diff --git a/README.md b/README.md index 900089383e..7798ed9929 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ A [feature-rich](https://livecodes.io/docs/features/), open-source, **client-sid [![LiveCodes: npm version](https://img.shields.io/npm/v/livecodes)](https://www.npmjs.com/package/livecodes) [![LiveCodes: npm downloads](https://img.shields.io/npm/dm/livecodes)](https://www.npmjs.com/package/livecodes) [![LiveCodes: jsdelivr downloads](https://data.jsdelivr.com/v1/package/npm/livecodes/badge?style=rounded)](https://www.jsdelivr.com/package/npm/livecodes) -[![LiveCodes: languages](https://img.shields.io/badge/languages-96-blue)](https://livecodes.io/docs/languages/) +[![LiveCodes: languages](https://img.shields.io/badge/languages-97-blue)](https://livecodes.io/docs/languages/) [![LiveCodes: docs](https://img.shields.io/badge/Documentation-575757?logo=gitbook&logoColor=white)](https://livecodes.io/docs/) [![LiveCodes: llms.txt](https://img.shields.io/badge/llms.txt-575757?logo=googledocs&logoColor=white)](https://livecodes.io/docs/llms.txt) [![LiveCodes: llms-full.txt](https://img.shields.io/badge/llms--full.txt-575757?logo=googledocs&logoColor=white)](https://livecodes.io/docs/llms-full.txt) diff --git a/docker-compose.yml b/docker-compose.yml index e8bb4ca09c..dfcc8c54ed 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,6 +5,8 @@ services: dockerfile: Dockerfile args: - SELF_HOSTED=true + - HOST_NAME=${HOST_NAME:-livecodes.localhost} + - PORT=${PORT:-443} - SELF_HOSTED_SHARE=${SELF_HOSTED_SHARE:-true} - SELF_HOSTED_BROADCAST=${SELF_HOSTED_BROADCAST:-true} - BROADCAST_PORT=${BROADCAST_PORT:-3030} @@ -12,6 +14,8 @@ services: - SANDBOX_PORT=${SANDBOX_PORT:-8090} - FIREBASE_CONFIG=${FIREBASE_CONFIG:-} - DOCS_BASE_URL=${DOCS_BASE_URL:-null} + - LOCAL_MODULES=${LOCAL_MODULES:-false} + - NODE_OPTIONS=--max-old-space-size=4096 restart: unless-stopped environment: - SELF_HOSTED=true @@ -26,6 +30,7 @@ services: - LOG_URL=${LOG_URL:-null} - VALKEY_HOST=valkey - VALKEY_PORT=6379 + - NODE_OPTIONS=--max-old-space-size=4096 volumes: - ./assets:/srv/build/assets depends_on: diff --git a/docs/docs/advanced/docker.mdx b/docs/docs/advanced/docker.mdx index 1e0188300a..e8a3a02a63 100644 --- a/docs/docs/advanced/docker.mdx +++ b/docs/docs/advanced/docker.mdx @@ -169,10 +169,56 @@ The hostname and many other options can be set using [environment variables](#en Runs code in a separate origin [sandboxed iframe](https://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/) to prevent cross-site scripting. +- Local modules + + See [Local Modules](#local-modules) section for details. + - [404 page](https://livecodes.io/404) Custom 404 page for resources that are not found. +## Local Modules + +(EXPERIMENTAL) + +LiveCodes depends on a large number of external modules to support a wide range of features (e.g. code editors, compilers, formatters, etc). +These modules are loaded from CDNs (e.g. [jsDelivr](https://www.jsdelivr.com/), [unpkg](https://unpkg.com/), etc). +So, in spite of being a client-side app, LiveCodes requires an internet connection to load these modules. + +However, if you want to be able to run LiveCodes without an internet connection, you can download all these modules locally by setting the [environment variable](#environment-variables) `LOCAL_MODULES=true` (it is set to `false` by default). +In this case, all modules are downloaded during build (~ 1.5 GB), and are served locally. + +When working without internet connection, all resources have to be available locally. So, automatic [module resolution](../features/module-resolution.mdx) (e.g. of npm modules imported in user code) and loading type definitions for [intellisense](../features/intellisense.mdx) and auto-complete will not work out of the box. +However, you can provide the list of modules to load using the [`imports`](../configuration/configuration-object.mdx#imports) property of the [config](../configuration/configuration-object.mdx) object (see [Custom Module Resolution](../features/module-resolution.mdx#custom-module-resolution)). +Similarly, you can also provide the list of type definitions to load using the [`types`](../configuration/configuration-object.mdx#types) property (see [Custom Types](../features/intellisense.mdx#custom-types)). +These modules and type definitions should be prepared in advance and made available to the running app (e.g. in the `/assets` directory - see [Volumes](#volumes)). + +Example: + +To run [React starter template](https://livecodes.io/?template=react) locally without internet connection, you need to: + +(assuming that the app is running on this URL: https://livecodes.localhost/) + +- Add [these files](https://github.com/hatemhosny/custom-modules-demo/tree/main/modules) to the [`/assets` directory](#volumes). +- Open React starter template: https://livecodes.localhost/?template=react +- Add this code to custom settings (Project menu → Custom Settings): + +```json +{ + "imports": { + "react": "https://livecodes.localhost/assets/react.js", + "react/compiler-runtime": "https://livecodes.localhost/assets/react-compiler-runtime.js", + "react/jsx-runtime": "https://livecodes.localhost/assets/react-jsx-runtime.js", + "react-dom": "https://livecodes.localhost/assets/react-dom.js", + "react-dom/client": "https://livecodes.localhost/assets/react-dom-client.js", + "scheduler": "https://livecodes.localhost/assets/scheduler.js" + }, + "types": { + "react": "https://livecodes.localhost/assets/react.d.ts" + } +} +``` + ## Environment Variables The app can be customized by setting different environment variables. @@ -205,6 +251,7 @@ The following environment variables are supported: | `FIREBASE_CONFIG` | [Firebase config object](https://firebase.google.com/docs/web/learn-more#config-object) (JSON), used for [authentication](../features/github-integration.mdx) | | | `DOCS_BASE_URL` | [Base URL](../features/self-hosting.mdx#custom-build) of the documentation (e.g. `/docs/`) | `null` | | `LOG_URL` | Full URL to send [server-side analytics](https://github.com/live-codes/livecodes/blob/develop/functions/index.ts) (e.g. `https://api.website.com/log`) | `null` | +| `LOCAL_MODULES` | Download and use all modules locally (see [Local Modules](#local-modules)) | `false` | :::info note When running in a non-local environment (e.g. VPS), diff --git a/docs/docs/languages/go-wasm.mdx b/docs/docs/languages/go-wasm.mdx new file mode 100644 index 0000000000..1f8c108758 --- /dev/null +++ b/docs/docs/languages/go-wasm.mdx @@ -0,0 +1,93 @@ +# Go (Wasm) + +[Go](https://go.dev/) (Golang), is an open-source, statically typed, and compiled programming language developed by Google. It is designed for simplicity, efficiency, and strong support for concurrency, making it well-suited for building scalable and high-performance applications. + +LiveCodes uses [Yaegi](https://github.com/traefik/yaegi), the Go interpreter (running on WebAssembly), to run Go in the browser. + +:::info Note + +LiveCodes also supports running Go using [GopherJS](https://github.com/gopherjs/gopherjs) which is a Go to JavaScript compiler. Read documentation [here](./go.mdx). + +::: + +## Demo + +import LiveCodes from '../../src/components/LiveCodes.tsx'; + +export const params = { + 'go-wasm': 'package main\n\nimport "fmt"\n\nfunc main() {\n\tfmt.Println("Hello, World!")\n}\n', + console: 'full', +}; + + + +## Usage + +LiveCodes runs Go in the browser, including the [standard library](https://pkg.go.dev/std). + + +### Communication with JavaScript + +The Go code runs in the context of the [result page](../features/result.mdx). +A few helper properties and methods are available in the browser global `livecodes.goWasm` object: + +- `livecodes.goWasm.input`: the initial standard input that is passed to the Go code. +- `livecodes.goWasm.loaded`: A promise that resolves when the Go environment is loaded. Any other helpers should be used after this promise resolves. +- `livecodes.goWasm.output`: the standard output. +- `livecodes.goWasm.error`: the standard error. +- `livecodes.goWasm.exitCode`: the exit code. +- `livecodes.goWasm.run`: a function that runs the Go code with new input. This function takes a string as input and returns a promise that resolves when the Go code is done running. The promise resolves with an object containing the `input`, `output`, `error`, and `exitCode` properties. + +See the [example below](#example-usage) for more details. + +## Language Info + +### Name + +`go-wasm` + +### Extensions + +`wasm.go`, `go-wasm`, `gowasm` + +### Editor + +`script` + +## Compiler + +[Yaegi](https://github.com/traefik/yaegi), compiled to WebAssembly ([yaegi-wasm](https://github.com/Muhammad-Ayman/yaegi-wasm)) + +### Version + +Yaegi v0.16.1, running go1.25.0 + +## Code Formatting + +Using [GopherJS](https://github.com/gopherjs/gopherjs) + +## Example Usage + +This example demonstrates standard library usage and JavaScript interoperability (also check the code in the HTML editor): + + + + +## Live Reload + +By default, new code changes are sent to the result page for re-evaluation without a full page reload, to avoid the need to reload the Go environment. + +This behavior can be disabled by adding the code comment `// __livecodes_reload__` to the code, which will force a full page reload. +This comment can be added in the [`hiddenContent` property of the editor](../configuration/configuration-object.mdx#markup) for embedded playgrounds. + +## Starter Template + +https://livecodes.io/?template=go-wasm + +## Links + +- [Go](https://go.dev/) +- [Go documentation](https://go.dev/doc/) +- [Go standard library](https://pkg.go.dev/std) +- [Yaegi](https://github.com/traefik/yaegi) +- [Go using GopherJS](./go.mdx) in LiveCodes diff --git a/docs/docs/languages/go.mdx b/docs/docs/languages/go.mdx index 2e71b7f967..e3a584e872 100644 --- a/docs/docs/languages/go.mdx +++ b/docs/docs/languages/go.mdx @@ -1,3 +1,72 @@ # Go (Golang) -TODO... +[Go](https://go.dev/) (Golang), is an open-source, statically typed, and compiled programming language developed by Google. It is designed for simplicity, efficiency, and strong support for concurrency, making it well-suited for building scalable and high-performance applications. + +LiveCodes uses [GopherJS](https://github.com/gopherjs/gopherjs) which is a Go to JavaScript compiler, to run Go in the browser. + +:::info Note + +LiveCodes also supports running Go using [Yaegi](https://github.com/traefik/yaegi), the Go interpreter (running on WebAssembly). Read documentation [here](./go-wasm.mdx). + +::: + +## Demo + +import LiveCodes from '../../src/components/LiveCodes.tsx'; + +export const params = { + 'go': 'package main\n\nimport "fmt"\n\nfunc main() {\n\tfmt.Println("Hello, World!")\n}\n', + console: 'full', +}; + + + +## Usage + +LiveCodes runs Go in the browser, including the [standard library](https://pkg.go.dev/std). + +JavaScript interoperability and DOM access is achieved using [package `js`](https://pkg.go.dev/syscall/js) (see the [example below](#example-usage)). + +## Language Info + +### Name + +`go` + +### Extensions + +`go`, `golang` + +### Editor + +`script` + +## Compiler + +[GopherJS](https://github.com/gopherjs/gopherjs), the Go to JavaScript compiler. + +### Version + +GopherJS v1.19.0 beta1, running Go 1.19.13 + +## Code Formatting + +Using [GopherJS](https://github.com/gopherjs/gopherjs) + +## Example Usage + +This example demonstrates standard library usage, JavaScript interoperability and DOM access: + + + +## Starter Template + +https://livecodes.io/?template=go + +## Links + +- [Go](https://go.dev/) +- [Go documentation](https://go.dev/doc/) +- [Go standard library](https://pkg.go.dev/std) +- [GopherJS](https://github.com/gopherjs/gopherjs) +- [Go using Yaegi](./go-wasm.mdx) in LiveCodes diff --git a/docs/docs/languages/markdown.mdx b/docs/docs/languages/markdown.mdx index 4793abb742..1e01cc18d9 100644 --- a/docs/docs/languages/markdown.mdx +++ b/docs/docs/languages/markdown.mdx @@ -1,3 +1,160 @@ # Markdown -TODO... +[Markdown](https://daringfireball.net/projects/markdown/) is a text-to-HTML conversion tool for web writers. +Markdown allows you to write using an easy-to-read, easy-to-write plain text format, then convert it to structurally valid HTML. + +Markdown is now one of the world's most popular markup languages. + +:::info Note + +Please note that MDX is also supported in LiveCodes and is [documented here](./mdx.mdx). + +::: + +## Demo + +import LiveCodes from '../../src/components/LiveCodes.tsx'; + +export const markdownConfig = { + markup: { + language: 'markdown', + content: `## Markdown + +_Hello_ **World** + +Ordered List: + +1. item +2. item +3. item + +Unordered list: + +- item +- item +- item + +Link: + +[link](https://livecodes.io) + +Image: + +![image](https://placehold.co/300x200) + +Table: + +| header 1 | header 2 | +| -------- | -------- | +| cell 1 | cell 2 | +| cell 3 | cell 4 | + +Quote: + +> blockquote + +Code: + +\`\`\`python +print("Hello, World!") +\`\`\` +` + }, +} + + + +## Styles + +By default, no styes are added. Only HTML output is generated from the Markdown code. + +If you want to style the result page similar to GitHub Markdown, you can use [`github-markdown-css`](https://github.com/sindresorhus/github-markdown-css). +Note that the body needs to have a `class="markdown-body"` for the styles to be applied. + +```js title="Script Editor (JS)" +document.body.classList.add('markdown-body'); +``` + +Example: + +export const styledMarkdownParams = { + template: 'markdown', + activeEditor: 'style', + css: `@import 'github-markdown-css'; + +.markdown-body { + box-sizing: border-box; + min-width: 200px; + max-width: 980px; + margin: 0 auto; + padding: 45px; +} + +@media (max-width: 767px) { + .markdown-body { + padding: 15px; + } +} +`, +} + + + +## Language Info + +### Name + +`markdown` + +### Aliases + +`md`, `mdown`, `mkdn` + +### Extension + +`.md` + +### Editor + +`script` + +## Compiler + +[Marked](https://marked.js.org/) + +### Version + +`marked`: v13.0.2 + +## Code Formatting + +Using [Prettier](https://prettier.io/). + +## Custom Settings + +[Custom settings](../advanced/custom-settings.mdx) added to the property `markdown` are passed as a JSON object to [`marked.parse`](https://marked.js.org/using_advanced). Please check the [documentation](https://marked.js.org/using_advanced#options) for full reference. + +Please note that custom settings should be valid JSON (i.e. functions are not allowed). + +**Example:** + +```json title="Custom Settings" +{ + "markdown": { + "gfm": true, + "breaks": true + } +} +``` + +## Starter Template + +https://livecodes.io/?template=markdown + +## Links + +- [Markdown](https://daringfireball.net/projects/markdown/) +- [Marked](https://marked.js.org/) +- [The Markdown Guide](https://www.markdownguide.org/) +- [MDX support in LiveCodes](./mdx.mdx) + diff --git a/docs/docs/languages/mdx.mdx b/docs/docs/languages/mdx.mdx index 24ff946ef7..878089b313 100644 --- a/docs/docs/languages/mdx.mdx +++ b/docs/docs/languages/mdx.mdx @@ -1,3 +1,109 @@ # MDX -TODO... +[MDX](https://mdxjs.com/) allows using JSX in [Markdown](./markdown.mdx), creating dynamic and component-driven content for websites, documentation, and applications. + +:::info Note + +Please note that Markdown is also supported in LiveCodes and is [documented here](./markdown.mdx). + +::: + +## Demo + +import LiveCodes from '../../src/components/LiveCodes.tsx'; + +export const mdxConfig = { + markup: { + language: 'mdx', + content: `import { Counter } from './script'; + +# Counter + + +`,}, + style: { + language: 'css', + content: `body, body button { + text-align: center; + font: 1em sans-serif; +} +`, + }, + script: { + language: 'jsx', + content: `import { useState } from "react"; + +export function Counter() { + const [count, setCount] = useState(0); + return ( +
+

You clicked {count} times.

+ +
+ ); +} +`, + }, +} + + + +## Usage + +Components can be imported from the script editor that uses [JSX](./jsx.mdx) or [React](./react.mdx) by importing from `'./script'` (with no extension). + +Example: + +```js +import { ComponentName } from './script'; +``` + +## Language Info + +### Name + +`mdx` + +### Extension + +`.mdx` + +### Editor + +`script` + +## Compiler + +[MDX](https://mdxjs.com/) + +### Version + +`@mdx-js/mdx`: v3.1.0 + +## Code Formatting + +Using [Prettier](https://prettier.io/). + +## Custom Settings + +[Custom settings](../advanced/custom-settings.mdx) added to the property `mdx` are passed as a JSON object to [`mdx.compile`](https://mdxjs.com/packages/mdx/#compilefile-options). Please check the [documentation](https://mdxjs.com/packages/mdx/#compileoptions) for full reference. + +Please note that custom settings should be valid JSON (i.e. functions are not allowed). + + +## Starter Template + +https://livecodes.io/?template=mdx + +## Links + +- [MDX](https://mdxjs.com/) +- [JSX](https://react.dev/learn/writing-markup-with-jsx) +- [React](https://react.dev/) +- [Markdown support in LiveCodes](./markdown.mdx) +- [JSX support in LiveCodes](./jsx.mdx) +- [React support in LiveCodes](./react.mdx) + + diff --git a/docs/docs/languages/python-wasm.mdx b/docs/docs/languages/python-wasm.mdx index a174a2b301..e98f819e3b 100644 --- a/docs/docs/languages/python-wasm.mdx +++ b/docs/docs/languages/python-wasm.mdx @@ -101,7 +101,7 @@ Check the [starter template](#starter-template) for an example. ### Version -Pyodide v0.25.1, running Python v3.11.3 +Pyodide v0.28.0, running Python v3.13.2 ## Code Formatting 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/e2e/specs/compilers.spec.ts b/e2e/specs/compilers.spec.ts index bba6112526..d18dd75263 100644 --- a/e2e/specs/compilers.spec.ts +++ b/e2e/specs/compilers.spec.ts @@ -1,1783 +1,1783 @@ -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`); + }); +}); diff --git a/eslint.config.mjs b/eslint.config.mjs index aab8c872fb..1b84e6be5e 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -31,6 +31,7 @@ export default [ '**/.docusaurus', '**/.jest', '**/.storybook', + '**/.cache', 'functions/vendors', ], }, diff --git a/functions/vendors/templates.js b/functions/vendors/templates.js index 10dbce55f7..2bbc7ed68f 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.

+ + + {{polyfillScript}} diff --git a/src/livecodes/html/language-info.html b/src/livecodes/html/language-info.html index 940044e9ac..c990bac90d 100644 --- a/src/livecodes/html/language-info.html +++ b/src/livecodes/html/language-info.html @@ -534,6 +534,11 @@

Go

>Learn X in Y minutes, where X=Go +
  • + LiveCodes Documentations +
  • Load starter templateGo
  • +
    +

    Go (Wasm)

    +
    + Go interpreter running on WebAssembly, using Yaegi +
    + +

    Haml

    diff --git a/src/livecodes/i18n/locales/ar/language-info.lokalise.json b/src/livecodes/i18n/locales/ar/language-info.lokalise.json index 4b572acc34..7b5634a8e7 100644 --- a/src/livecodes/i18n/locales/ar/language-info.lokalise.json +++ b/src/livecodes/i18n/locales/ar/language-info.lokalise.json @@ -222,6 +222,15 @@ "go.name": { "translation": "Go" }, + "goWasm.desc": { + "translation": "مترجم Go يعمل على WebAssembly، باستخدام Yaegi" + }, + "goWasm.link": { + "translation": "اذهب إلى الموقع الرسمي يايجي تعلم X في دقائق Y، حيث X = اذهب وثائق LiveCodes تحميل قالب البداية تحميل قالب البداية " + }, + "goWasm.name": { + "translation": "Go (Wasm)" + }, "haml.desc": { "translation": "مترجم Haml لقوالب العرض في جافا سكريبت على جانب العميل باستخدام clientside-haml-js." }, diff --git a/src/livecodes/i18n/locales/ar/language-info.ts b/src/livecodes/i18n/locales/ar/language-info.ts index 706f696c6c..2326716de7 100644 --- a/src/livecodes/i18n/locales/ar/language-info.ts +++ b/src/livecodes/i18n/locales/ar/language-info.ts @@ -125,6 +125,11 @@ const languageInfo: I18nLangInfoTranslation = { link: '<1><2>موقع جو <3><4>وثائق جو <5> <6>مستودع GopherJS <7> <8>تعلم X في Y دقيقة، حيث X=Go <9><10>تحميل قالب البداية', name: 'Go', }, + goWasm: { + desc: 'مترجم Go يعمل على WebAssembly، باستخدام Yaegi', + link: '<1><2>اذهب إلى الموقع الرسمي <3><4>يايجي <5> <6>تعلم X في دقائق Y، حيث X = اذهب <7> <8>وثائق LiveCodes <9> <10>تحميل قالب البداية تحميل قالب البداية ', + name: 'Go (Wasm)', + }, haml: { desc: 'مترجم Haml لقوالب العرض في جافا سكريبت على جانب العميل باستخدام clientside-haml-js.', link: '<1><2>الموقع الرسمي لـ Haml <3> <4>وثائق Haml <5> <6>مستودع GitHub لـ clientside-haml-js <7> <8>تعلم X في Y دقيقة، حيث X=haml <9> <10>وثائق LiveCodes ', diff --git a/src/livecodes/i18n/locales/ar/translation.lokalise.json b/src/livecodes/i18n/locales/ar/translation.lokalise.json index 0145456d6a..b2887000d5 100644 --- a/src/livecodes/i18n/locales/ar/translation.lokalise.json +++ b/src/livecodes/i18n/locales/ar/translation.lokalise.json @@ -1950,6 +1950,9 @@ "templates.starter.cpp": { "translation": "قالب C++" }, + "templates.starter.cpp-wasm": { + "translation": "قالب C++ (Wasm)" + }, "templates.starter.daisyui": { "translation": "قالب daisyUI" }, @@ -1965,6 +1968,9 @@ "templates.starter.go": { "translation": "قالب Go" }, + "templates.starter.go-wasm": { + "translation": "قالب Go (Wasm)" + }, "templates.starter.heading": { "translation": "قوالب البداية" }, diff --git a/src/livecodes/i18n/locales/ar/translation.ts b/src/livecodes/i18n/locales/ar/translation.ts index 4d6465c08b..fc62be86ff 100644 --- a/src/livecodes/i18n/locales/ar/translation.ts +++ b/src/livecodes/i18n/locales/ar/translation.ts @@ -989,11 +989,13 @@ const translation: I18nTranslation = { coffeescript: 'قالب CoffeeScript', commonlisp: 'قالب Common Lisp', cpp: 'قالب C++', + 'cpp-wasm': 'قالب C++ (Wasm)', daisyui: 'قالب daisyUI', diagrams: 'قالب الرسوم البيانية', fennel: 'قالب Fennel', gleam: 'قالب Gleam', go: 'قالب Go', + 'go-wasm': 'قالب Go (Wasm)', heading: 'قوالب البداية', imba: 'قالب Imba', java: 'قالب Java', diff --git a/src/livecodes/i18n/locales/de/language-info.lokalise.json b/src/livecodes/i18n/locales/de/language-info.lokalise.json index 955b04645a..b9160550d2 100644 --- a/src/livecodes/i18n/locales/de/language-info.lokalise.json +++ b/src/livecodes/i18n/locales/de/language-info.lokalise.json @@ -222,6 +222,15 @@ "go.name": { "translation": "Go" }, + "goWasm.desc": { + "translation": "Go-Interpreter, der auf WebAssembly läuft, unter Verwendung von Yaegi" + }, + "goWasm.link": { + "translation": "Go offizielle Website Yaegi Lerne X in Y Minuten, wobei X=Go LiveCodes Dokumentationen Lade Startvorlage " + }, + "goWasm.name": { + "translation": "Go (Wasm)" + }, "haml.desc": { "translation": "Haml-Compiler für clientseitige JavaScript-View-Templates unter Verwendung von clientside-haml-js." }, diff --git a/src/livecodes/i18n/locales/de/language-info.ts b/src/livecodes/i18n/locales/de/language-info.ts index 65309a94da..6e1402c08a 100644 --- a/src/livecodes/i18n/locales/de/language-info.ts +++ b/src/livecodes/i18n/locales/de/language-info.ts @@ -128,6 +128,11 @@ const languageInfo: I18nLangInfoTranslation = { link: '<1><2>Go Website <3><4>Go Dokumentation <5> <6>GopherJS Repository <7> <8>Lerne X in Y Minuten, wobei X=Go <9><10>Starter-Vorlage laden', name: 'Go', }, + goWasm: { + desc: 'Go-Interpreter, der auf WebAssembly läuft, unter Verwendung von Yaegi', + link: '<1><2>Go offizielle Website <3><4>Yaegi <5> <6>Lerne X in Y Minuten, wobei X=Go <7> <8>LiveCodes Dokumentationen <9> <10>Lade Startvorlage ', + name: 'Go (Wasm)', + }, haml: { desc: 'Haml-Compiler für clientseitige JavaScript-View-Templates unter Verwendung von clientside-haml-js.', link: '<1><2>Haml offizielle Website <3> <4>Haml Dokumentation <5> <6>clientside-haml-js GitHub Repository <7> <8>Lerne X in Y Minuten, wobei X=haml <9> <10>LiveCodes Dokumentation ', diff --git a/src/livecodes/i18n/locales/de/translation.lokalise.json b/src/livecodes/i18n/locales/de/translation.lokalise.json index 2832c23d75..1d05f2540e 100644 --- a/src/livecodes/i18n/locales/de/translation.lokalise.json +++ b/src/livecodes/i18n/locales/de/translation.lokalise.json @@ -1950,6 +1950,9 @@ "templates.starter.cpp": { "translation": "C++-Starter" }, + "templates.starter.cpp-wasm": { + "translation": "C++ (Wasm)-Starter" + }, "templates.starter.daisyui": { "translation": "daisyUI-Starter" }, @@ -1965,6 +1968,9 @@ "templates.starter.go": { "translation": "Go-Starter" }, + "templates.starter.go-wasm": { + "translation": "Go (Wasm)-Starter" + }, "templates.starter.heading": { "translation": "Starter-Vorlagen" }, diff --git a/src/livecodes/i18n/locales/de/translation.ts b/src/livecodes/i18n/locales/de/translation.ts index c12c3da4be..c8e50df7d8 100644 --- a/src/livecodes/i18n/locales/de/translation.ts +++ b/src/livecodes/i18n/locales/de/translation.ts @@ -991,11 +991,13 @@ const translation: I18nTranslation = { coffeescript: 'CoffeeScript-Starter', commonlisp: 'Common Lisp-Starter', cpp: 'C++-Starter', + 'cpp-wasm': 'C++ (Wasm)-Starter', daisyui: 'daisyUI-Starter', diagrams: 'Diagramm-Starter', fennel: 'Fennel-Starter', gleam: 'Gleam-Starter', go: 'Go-Starter', + 'go-wasm': 'Go (Wasm)-Starter', heading: 'Starter-Vorlagen', imba: 'Imba-Starter', java: 'Java-Starter', diff --git a/src/livecodes/i18n/locales/en/language-info.lokalise.json b/src/livecodes/i18n/locales/en/language-info.lokalise.json index bf064d71d7..d73b7f3b13 100644 --- a/src/livecodes/i18n/locales/en/language-info.lokalise.json +++ b/src/livecodes/i18n/locales/en/language-info.lokalise.json @@ -289,13 +289,25 @@ "translation": "Here, it is compiled to JavaScript using GopherJS." }, "go.link": { - "notes": "### ###\n
  • \n\n### ###\n\n\n### ###\n
  • \n\n### ###\n\n\n### ###\n
  • \n\n### ###\n\n\n### ###\n
  • \n\n### ###\n\n\n### ###\n
  • \n\n### ###\n\n\n", - "translation": "Go website Go documentation GopherJS repo Learn X in Y minutes, where X=Go Load starter template " + "notes": "### ###\n
  • \n\n### ###\n\n\n### ###\n
  • \n\n### ###\n\n\n### ###\n
  • \n\n### ###\n\n\n### ###\n
  • \n\n### ###\n\n\n### ###\n
  • \n\n### ###\n\n\n### ###\n
  • \n\n### ###\n\n\n", + "translation": "Go website Go documentation GopherJS repo Learn X in Y minutes, where X=Go LiveCodes Documentations Load starter template " }, "go.name": { "notes": "", "translation": "Go" }, + "goWasm.desc": { + "notes": "", + "translation": "Go interpreter running on WebAssembly, using Yaegi" + }, + "goWasm.link": { + "notes": "### ###\n
  • \n\n### ###\n\n\n### ###\n
  • \n\n### ###\n\n\n### ###\n
  • \n\n### ###\n\n\n### ###\n
  • \n\n### ###\n\n\n### ###\n
  • \n\n### ###\n\n\n", + "translation": "Go official website Yaegi Learn X in Y minutes, where X=Go LiveCodes Documentations Load starter template " + }, + "goWasm.name": { + "notes": "", + "translation": "Go (Wasm)" + }, "haml.desc": { "notes": "", "translation": "Haml compiler for client side javascript view templates using clientside-haml-js." diff --git a/src/livecodes/i18n/locales/en/language-info.ts b/src/livecodes/i18n/locales/en/language-info.ts index 820583ea22..849bf97bb1 100644 --- a/src/livecodes/i18n/locales/en/language-info.ts +++ b/src/livecodes/i18n/locales/en/language-info.ts @@ -131,9 +131,14 @@ const languageInfo = { desc1: 'Go (Golang) is an open source programming language that makes it easy to build simple, reliable, and efficient software.', desc2: 'Here, it is compiled to JavaScript using GopherJS.', - link: '<1><2>Go website <3><4>Go documentation <5> <6>GopherJS repo <7> <8>Learn X in Y minutes, where X=Go <9> <10>Load starter template ', + link: '<1><2>Go website <3><4>Go documentation <5> <6>GopherJS repo <7> <8>Learn X in Y minutes, where X=Go <9> <10>LiveCodes Documentations <11> <12>Load starter template ', name: 'Go', }, + goWasm: { + desc: 'Go interpreter running on WebAssembly, using Yaegi', + link: '<1><2>Go official website <3><4>Yaegi <5> <6>Learn X in Y minutes, where X=Go <7> <8>LiveCodes Documentations <9> <10>Load starter template ', + name: 'Go (Wasm)', + }, haml: { desc: 'Haml compiler for client side javascript view templates using clientside-haml-js.', link: '<1><2>Haml official website <3> <4>Haml documentation <5> <6>clientside-haml-js GitHub repo <7> <8>Learn X in Y minutes, where X=haml <9> <10>LiveCodes Documentations ', diff --git a/src/livecodes/i18n/locales/en/translation.lokalise.json b/src/livecodes/i18n/locales/en/translation.lokalise.json index d6d83d19e2..83f8a79203 100644 --- a/src/livecodes/i18n/locales/en/translation.lokalise.json +++ b/src/livecodes/i18n/locales/en/translation.lokalise.json @@ -2600,6 +2600,10 @@ "notes": "", "translation": "C++ Starter" }, + "templates.starter.cpp-wasm": { + "notes": "", + "translation": "C++ (Wasm) Starter" + }, "templates.starter.daisyui": { "notes": "", "translation": "daisyUI Starter" @@ -2620,6 +2624,10 @@ "notes": "", "translation": "Go Starter" }, + "templates.starter.go-wasm": { + "notes": "", + "translation": "Go (Wasm) Starter" + }, "templates.starter.heading": { "notes": "", "translation": "Starter Templates" diff --git a/src/livecodes/i18n/locales/en/translation.ts b/src/livecodes/i18n/locales/en/translation.ts index 7346431e9b..9e7810b549 100644 --- a/src/livecodes/i18n/locales/en/translation.ts +++ b/src/livecodes/i18n/locales/en/translation.ts @@ -996,11 +996,13 @@ const translation = { coffeescript: 'CoffeeScript Starter', commonlisp: 'Common Lisp Starter', cpp: 'C++ Starter', + 'cpp-wasm': 'C++ (Wasm) Starter', daisyui: 'daisyUI Starter', diagrams: 'Diagrams Starter', fennel: 'Fennel Starter', gleam: 'Gleam Starter', go: 'Go Starter', + 'go-wasm': 'Go (Wasm) Starter', heading: 'Starter Templates', imba: 'Imba Starter', java: 'Java Starter', diff --git a/src/livecodes/i18n/locales/es/language-info.lokalise.json b/src/livecodes/i18n/locales/es/language-info.lokalise.json index fdd413f099..53dec063ad 100644 --- a/src/livecodes/i18n/locales/es/language-info.lokalise.json +++ b/src/livecodes/i18n/locales/es/language-info.lokalise.json @@ -222,6 +222,15 @@ "go.name": { "translation": "Go" }, + "goWasm.desc": { + "translation": "Intérprete Go ejecutándose en WebAssembly, usando Yaegi" + }, + "goWasm.link": { + "translation": "Go sitio web oficial Yaegi Aprenda X en Y minutos, donde X=Go LiveCodes Documentations Cargar plantilla de inicio " + }, + "goWasm.name": { + "translation": "Go (Wasm)" + }, "haml.desc": { "translation": "Compilador Haml para plantillas de vista JavaScript del lado del cliente usando clientside-haml-js." }, diff --git a/src/livecodes/i18n/locales/es/language-info.ts b/src/livecodes/i18n/locales/es/language-info.ts index 1914077a67..79238313ca 100644 --- a/src/livecodes/i18n/locales/es/language-info.ts +++ b/src/livecodes/i18n/locales/es/language-info.ts @@ -128,6 +128,11 @@ const languageInfo: I18nLangInfoTranslation = { link: '<1><2>Sitio web de Go <3><4>Documentación de Go <5> <6>Repositorio de GopherJS <7> <8>Aprende X en Y minutos, donde X=Go <9><10>Cargar plantilla inicial', name: 'Go', }, + goWasm: { + desc: 'Intérprete Go ejecutándose en WebAssembly, usando Yaegi', + link: '<1><2>Go sitio web oficial <3><4>Yaegi <5> <6>Aprenda X en Y minutos, donde X=Go <7> <8>LiveCodes Documentations <9> <10>Cargar plantilla de inicio ', + name: 'Go (Wasm)', + }, haml: { desc: 'Compilador Haml para plantillas de vista JavaScript del lado del cliente usando clientside-haml-js.', link: '<1><2>Sitio web oficial de Haml <3> <4>Documentación de Haml <5> <6>Repositorio GitHub de clientside-haml-js <7> <8>Aprende X en Y minutos, donde X=haml <9> <10>Documentación de LiveCodes ', diff --git a/src/livecodes/i18n/locales/es/translation.lokalise.json b/src/livecodes/i18n/locales/es/translation.lokalise.json index 8ad355d08a..fcff96d645 100644 --- a/src/livecodes/i18n/locales/es/translation.lokalise.json +++ b/src/livecodes/i18n/locales/es/translation.lokalise.json @@ -1950,6 +1950,9 @@ "templates.starter.cpp": { "translation": "Inicio C++" }, + "templates.starter.cpp-wasm": { + "translation": "Inicio C++ (Wasm)" + }, "templates.starter.daisyui": { "translation": "Inicio daisyUI" }, @@ -1965,6 +1968,9 @@ "templates.starter.go": { "translation": "Inicio Go" }, + "templates.starter.go-wasm": { + "translation": "Inicio Go (Wasm)" + }, "templates.starter.heading": { "translation": "Plantillas de Inicio" }, diff --git a/src/livecodes/i18n/locales/es/translation.ts b/src/livecodes/i18n/locales/es/translation.ts index ec6f69b986..4530743f01 100644 --- a/src/livecodes/i18n/locales/es/translation.ts +++ b/src/livecodes/i18n/locales/es/translation.ts @@ -989,11 +989,13 @@ const translation: I18nTranslation = { coffeescript: 'Inicio CoffeeScript', commonlisp: 'Inicio Common Lisp', cpp: 'Inicio C++', + 'cpp-wasm': 'Inicio C++ (Wasm)', daisyui: 'Inicio daisyUI', diagrams: 'Inicio Diagramas', fennel: 'Inicio Fennel', gleam: 'Inicio Gleam', go: 'Inicio Go', + 'go-wasm': 'Inicio Go (Wasm)', heading: 'Plantillas de Inicio', imba: 'Inicio Imba', java: 'Inicio Java', diff --git a/src/livecodes/i18n/locales/fa/language-info.lokalise.json b/src/livecodes/i18n/locales/fa/language-info.lokalise.json index 565b396ff4..6adf3cc2f7 100644 --- a/src/livecodes/i18n/locales/fa/language-info.lokalise.json +++ b/src/livecodes/i18n/locales/fa/language-info.lokalise.json @@ -222,6 +222,15 @@ "go.name": { "translation": "Go" }, + "goWasm.desc": { + "translation": "Go interpreter در حال اجرا در WebAssembly، با استفاده از Yaegi" + }, + "goWasm.link": { + "translation": "برو وب سایت رسمی یاگی X را در دقیقه Y یاد بگیرید، جایی که X=Go مستندات LiveCodes قالب استارت را بارگذاری کنید " + }, + "goWasm.name": { + "translation": "Go (Wasm)" + }, "haml.desc": { "translation": "کامپایلر Haml برای قالب‌های نما جاوااسکریپت سمت کلاینت با استفاده از clientside-haml-js." }, diff --git a/src/livecodes/i18n/locales/fa/language-info.ts b/src/livecodes/i18n/locales/fa/language-info.ts index 7aecddd0d2..a298caa32f 100644 --- a/src/livecodes/i18n/locales/fa/language-info.ts +++ b/src/livecodes/i18n/locales/fa/language-info.ts @@ -127,6 +127,11 @@ const languageInfo: I18nLangInfoTranslation = { link: '<1><2>وب‌سایت Go <3><4>مستندات Go <5> <6>مخزن GopherJS <7> <8>یادگیری X در Y دقیقه، جایی که X=Go <9> <10>بارگذاری قالب شروع کننده ', name: 'Go', }, + goWasm: { + desc: 'Go interpreter در حال اجرا در WebAssembly، با استفاده از Yaegi', + link: '<1><2>برو وب سایت رسمی <3><4>یاگی <5> <6>X را در دقیقه Y یاد بگیرید، جایی که X=Go <7> <8>مستندات LiveCodes <9> <10>قالب استارت را بارگذاری کنید ', + name: 'Go (Wasm)', + }, haml: { desc: 'کامپایلر Haml برای قالب‌های نما جاوااسکریپت سمت کلاینت با استفاده از clientside-haml-js.', link: '<1><2>وب‌سایت رسمی Haml <3> <4>مستندات Haml <5> <6>مخزن GitHub clientside-haml-js <7> <8>یادگیری X در Y دقیقه، جایی که X=haml <9> <10>مستندات LiveCodes ', diff --git a/src/livecodes/i18n/locales/fa/translation.lokalise.json b/src/livecodes/i18n/locales/fa/translation.lokalise.json index 6914086373..d869534f47 100644 --- a/src/livecodes/i18n/locales/fa/translation.lokalise.json +++ b/src/livecodes/i18n/locales/fa/translation.lokalise.json @@ -1950,6 +1950,9 @@ "templates.starter.cpp": { "translation": "شروع کننده C++" }, + "templates.starter.cpp-wasm": { + "translation": "شروع کننده Go (Wasm)" + }, "templates.starter.daisyui": { "translation": "شروع کننده daisyUI" }, @@ -1965,6 +1968,9 @@ "templates.starter.go": { "translation": "شروع کننده Go" }, + "templates.starter.go-wasm": { + "translation": "شروع کننده Go (Wasm)" + }, "templates.starter.heading": { "translation": "قالب‌های شروع کننده" }, diff --git a/src/livecodes/i18n/locales/fa/translation.ts b/src/livecodes/i18n/locales/fa/translation.ts index f8bba0f514..a5d0c93cc8 100644 --- a/src/livecodes/i18n/locales/fa/translation.ts +++ b/src/livecodes/i18n/locales/fa/translation.ts @@ -990,11 +990,13 @@ const translation: I18nTranslation = { coffeescript: 'شروع کننده CoffeeScript', commonlisp: 'شروع کننده Common Lisp', cpp: 'شروع کننده C++', + 'cpp-wasm': 'شروع کننده Go (Wasm)', daisyui: 'شروع کننده daisyUI', diagrams: 'شروع کننده نمودارها', fennel: 'شروع کننده Fennel', gleam: 'شروع کننده Gleam', go: 'شروع کننده Go', + 'go-wasm': 'شروع کننده Go (Wasm)', heading: 'قالب‌های شروع کننده', imba: 'شروع کننده Imba', java: 'شروع کننده جاوا', diff --git a/src/livecodes/i18n/locales/fr/language-info.lokalise.json b/src/livecodes/i18n/locales/fr/language-info.lokalise.json index 8784f5634b..487be6ec2d 100644 --- a/src/livecodes/i18n/locales/fr/language-info.lokalise.json +++ b/src/livecodes/i18n/locales/fr/language-info.lokalise.json @@ -222,6 +222,15 @@ "go.name": { "translation": "Go" }, + "goWasm.desc": { + "translation": "Interpréteur Go fonctionnant sur WebAssembly, utilisant Yaegi" + }, + "goWasm.link": { + "translation": "Site officiel de Go Yaegi Apprendre X en Y minutes, où X=Go LiveCodes Documentations Charger un modèle de démarrage " + }, + "goWasm.name": { + "translation": "Go (Wasm)" + }, "haml.desc": { "translation": "Compilateur Haml pour les modèles de vue JavaScript côté client utilisant clientside-haml-js." }, diff --git a/src/livecodes/i18n/locales/fr/language-info.ts b/src/livecodes/i18n/locales/fr/language-info.ts index 64c5cf3187..85f55ccc73 100644 --- a/src/livecodes/i18n/locales/fr/language-info.ts +++ b/src/livecodes/i18n/locales/fr/language-info.ts @@ -128,6 +128,11 @@ const languageInfo: I18nLangInfoTranslation = { link: '<1><2>Site web de Go <3><4>Documentation de Go <5> <6>Dépôt GopherJS <7> <8>Apprenez X en Y minutes, où X=Go <9><10>Charger le modèle de démarrage', name: 'Go', }, + goWasm: { + desc: 'Interpréteur Go fonctionnant sur WebAssembly, utilisant Yaegi', + link: '<1><2>Site officiel de Go <3><4>Yaegi <5> <6>Apprendre X en Y minutes, où X=Go <7> <8>LiveCodes Documentations <9> <10>Charger un modèle de démarrage ', + name: 'Go (Wasm)', + }, haml: { desc: 'Compilateur Haml pour les modèles de vue JavaScript côté client utilisant clientside-haml-js.', link: '<1><2>Site officiel de Haml <3> <4>Documentation de Haml <5> <6>Dépôt GitHub de clientside-haml-js <7> <8>Apprenez X en Y minutes, où X=haml <9> <10>Documentation LiveCodes ', diff --git a/src/livecodes/i18n/locales/fr/translation.lokalise.json b/src/livecodes/i18n/locales/fr/translation.lokalise.json index c9930fcf5f..448c55ba22 100644 --- a/src/livecodes/i18n/locales/fr/translation.lokalise.json +++ b/src/livecodes/i18n/locales/fr/translation.lokalise.json @@ -1950,6 +1950,9 @@ "templates.starter.cpp": { "translation": "Démarrage C++" }, + "templates.starter.cpp-wasm": { + "translation": "Démarrage C++ (Wasm)" + }, "templates.starter.daisyui": { "translation": "Démarrage daisyUI" }, @@ -1965,6 +1968,9 @@ "templates.starter.go": { "translation": "Démarrage Go" }, + "templates.starter.go-wasm": { + "translation": "Démarrage Go (Wasm)" + }, "templates.starter.heading": { "translation": "Modèles de Démarrage" }, diff --git a/src/livecodes/i18n/locales/fr/translation.ts b/src/livecodes/i18n/locales/fr/translation.ts index bf8b5614fb..4b02dcc1aa 100644 --- a/src/livecodes/i18n/locales/fr/translation.ts +++ b/src/livecodes/i18n/locales/fr/translation.ts @@ -991,11 +991,13 @@ const translation: I18nTranslation = { coffeescript: 'Démarrage CoffeeScript', commonlisp: 'Démarrage Common Lisp', cpp: 'Démarrage C++', + 'cpp-wasm': 'Démarrage C++ (Wasm)', daisyui: 'Démarrage daisyUI', diagrams: 'Démarrage Diagrammes', fennel: 'Démarrage Fennel', gleam: 'Démarrage Gleam', go: 'Démarrage Go', + 'go-wasm': 'Démarrage Go (Wasm)', heading: 'Modèles de Démarrage', imba: 'Démarrage Imba', java: 'Démarrage Java', diff --git a/src/livecodes/i18n/locales/hi/language-info.lokalise.json b/src/livecodes/i18n/locales/hi/language-info.lokalise.json index 06b89ff54e..5c4b26c454 100644 --- a/src/livecodes/i18n/locales/hi/language-info.lokalise.json +++ b/src/livecodes/i18n/locales/hi/language-info.lokalise.json @@ -222,6 +222,15 @@ "go.name": { "translation": "Go" }, + "goWasm.desc": { + "translation": "Yaegi का उपयोग करके WebAssembly पर चलने वाले दुभाषिया पर जाएं" + }, + "goWasm.link": { + "translation": "आधिकारिक वेबसाइट पर जाएं यागी Y मिनट में X जानें, जहां X=Go लाइवकोड्स दस्तावेज़ीकरण स्टार्टर टेम्पलेट लोड करें " + }, + "goWasm.name": { + "translation": "Go (Wasm)" + }, "haml.desc": { "translation": "क्लाइंटसाइड-haml-js का उपयोग करके क्लाइंट साइड जावास्क्रिप्ट व्यू टेम्प्लेट्स के लिए Haml कंपाइलर।" }, diff --git a/src/livecodes/i18n/locales/hi/language-info.ts b/src/livecodes/i18n/locales/hi/language-info.ts index 457d5f91e1..f829ebf33b 100644 --- a/src/livecodes/i18n/locales/hi/language-info.ts +++ b/src/livecodes/i18n/locales/hi/language-info.ts @@ -127,6 +127,11 @@ const languageInfo: I18nLangInfoTranslation = { link: '<1><2>Go वेबसाइट <3><4>Go दस्तावेज़ीकरण <5> <6>GopherJS रेपो <7> <8>X को Y मिनटों में सीखें, जहां X=Go <9><10>स्टार्टर टेम्पलेट लोड करें', name: 'Go', }, + goWasm: { + desc: 'Yaegi का उपयोग करके WebAssembly पर चलने वाले दुभाषिया पर जाएं', + link: '<1><2>आधिकारिक वेबसाइट पर जाएं <3><4>यागी <5> <6>Y मिनट में X जानें, जहां X=Go <7> <8>लाइवकोड्स दस्तावेज़ीकरण <9> <10>स्टार्टर टेम्पलेट लोड करें ', + name: 'Go (Wasm)', + }, haml: { desc: 'क्लाइंटसाइड-haml-js का उपयोग करके क्लाइंट साइड जावास्क्रिप्ट व्यू टेम्प्लेट्स के लिए Haml कंपाइलर।', link: '<1><2>Haml आधिकारिक वेबसाइट <3> <4>Haml दस्तावेज़ीकरण <5> <6>clientside-haml-js GitHub रेपो <7> <8>X को Y मिनटों में सीखें, जहां X=haml <9> <10>LiveCodes दस्तावेज़ीकरण ', diff --git a/src/livecodes/i18n/locales/hi/translation.lokalise.json b/src/livecodes/i18n/locales/hi/translation.lokalise.json index 721c9a91ca..5903fb36b4 100644 --- a/src/livecodes/i18n/locales/hi/translation.lokalise.json +++ b/src/livecodes/i18n/locales/hi/translation.lokalise.json @@ -1950,6 +1950,9 @@ "templates.starter.cpp": { "translation": "C++ स्टार्टर" }, + "templates.starter.cpp-wasm": { + "translation": "C++ (Wasm) स्टार्टर" + }, "templates.starter.daisyui": { "translation": "daisyUI स्टार्टर" }, @@ -1965,6 +1968,9 @@ "templates.starter.go": { "translation": "Go स्टार्टर" }, + "templates.starter.go-wasm": { + "translation": "Go (Wasm) स्टार्टर" + }, "templates.starter.heading": { "translation": "स्टार्टर टेम्पलेट्स" }, diff --git a/src/livecodes/i18n/locales/hi/translation.ts b/src/livecodes/i18n/locales/hi/translation.ts index 9411c5ed16..9da6a3082f 100644 --- a/src/livecodes/i18n/locales/hi/translation.ts +++ b/src/livecodes/i18n/locales/hi/translation.ts @@ -989,11 +989,13 @@ const translation: I18nTranslation = { coffeescript: 'CoffeeScript स्टार्टर', commonlisp: 'Common Lisp स्टार्टर', cpp: 'C++ स्टार्टर', + 'cpp-wasm': 'C++ (Wasm) स्टार्टर', daisyui: 'daisyUI स्टार्टर', diagrams: 'आरेख स्टार्टर', fennel: 'Fennel स्टार्टर', gleam: 'Gleam स्टार्टर', go: 'Go स्टार्टर', + 'go-wasm': 'Go (Wasm) स्टार्टर', heading: 'स्टार्टर टेम्पलेट्स', imba: 'Imba स्टार्टर', java: 'Java स्टार्टर', diff --git a/src/livecodes/i18n/locales/it/language-info.lokalise.json b/src/livecodes/i18n/locales/it/language-info.lokalise.json index 940742e123..3dbf79b54c 100644 --- a/src/livecodes/i18n/locales/it/language-info.lokalise.json +++ b/src/livecodes/i18n/locales/it/language-info.lokalise.json @@ -222,6 +222,15 @@ "go.name": { "translation": "Go" }, + "goWasm.desc": { + "translation": "Interprete Go in esecuzione su WebAssembly, utilizzando Yaegi" + }, + "goWasm.link": { + "translation": "Sito ufficiale di Go Yaegi Imparare X in Y minuti, dove X=Go LiveCodes Documentations Caricare il modello di partenza " + }, + "goWasm.name": { + "translation": "Go (Wasm)" + }, "haml.desc": { "translation": "Compilatore Haml per template di vista JavaScript lato client utilizzando clientside-haml-js." }, diff --git a/src/livecodes/i18n/locales/it/language-info.ts b/src/livecodes/i18n/locales/it/language-info.ts index cd6bb68c34..a174242cdb 100644 --- a/src/livecodes/i18n/locales/it/language-info.ts +++ b/src/livecodes/i18n/locales/it/language-info.ts @@ -127,6 +127,11 @@ const languageInfo: I18nLangInfoTranslation = { link: '<1><2>Sito web di Go <3><4>Documentazione di Go <5> <6>Repo di GopherJS <7> <8>Impara X in Y minuti, dove X=Go <9><10>Carica modello di partenza', name: 'Go', }, + goWasm: { + desc: 'Interprete Go in esecuzione su WebAssembly, utilizzando Yaegi', + link: '<1><2>Sito ufficiale di Go <3><4>Yaegi <5> <6>Imparare X in Y minuti, dove X=Go <7> <8>LiveCodes Documentations <9> <10>Caricare il modello di partenza ', + name: 'Go (Wasm)', + }, haml: { desc: 'Compilatore Haml per template di vista JavaScript lato client utilizzando clientside-haml-js.', link: '<1><2>Sito ufficiale di Haml <3> <4>Documentazione di Haml <5> <6>Repository GitHub di clientside-haml-js <7> <8>Impara X in Y minuti, dove X=haml <9> <10>Documentazione LiveCodes ', diff --git a/src/livecodes/i18n/locales/it/translation.lokalise.json b/src/livecodes/i18n/locales/it/translation.lokalise.json index 657f112407..aa2447dab8 100644 --- a/src/livecodes/i18n/locales/it/translation.lokalise.json +++ b/src/livecodes/i18n/locales/it/translation.lokalise.json @@ -1950,6 +1950,9 @@ "templates.starter.cpp": { "translation": "Starter C++" }, + "templates.starter.cpp-wasm": { + "translation": "Starter C++ (Wasm)" + }, "templates.starter.daisyui": { "translation": "Starter daisyUI" }, @@ -1965,6 +1968,9 @@ "templates.starter.go": { "translation": "Starter Go" }, + "templates.starter.go-wasm": { + "translation": "Starter Go (Wasm)" + }, "templates.starter.heading": { "translation": "Modelli di Partenza" }, diff --git a/src/livecodes/i18n/locales/it/translation.ts b/src/livecodes/i18n/locales/it/translation.ts index e6af58b806..e114e0149f 100644 --- a/src/livecodes/i18n/locales/it/translation.ts +++ b/src/livecodes/i18n/locales/it/translation.ts @@ -990,11 +990,13 @@ const translation: I18nTranslation = { coffeescript: 'Starter CoffeeScript', commonlisp: 'Starter Common Lisp', cpp: 'Starter C++', + 'cpp-wasm': 'Starter C++ (Wasm)', daisyui: 'Starter daisyUI', diagrams: 'Starter Diagrammi', fennel: 'Starter Fennel', gleam: 'Starter Gleam', go: 'Starter Go', + 'go-wasm': 'Starter Go (Wasm)', heading: 'Modelli di Partenza', imba: 'Starter Imba', java: 'Starter Java', diff --git a/src/livecodes/i18n/locales/ja/language-info.lokalise.json b/src/livecodes/i18n/locales/ja/language-info.lokalise.json index 66b7e00bd9..ecc5203a35 100644 --- a/src/livecodes/i18n/locales/ja/language-info.lokalise.json +++ b/src/livecodes/i18n/locales/ja/language-info.lokalise.json @@ -222,6 +222,15 @@ "go.name": { "translation": "Go" }, + "goWasm.desc": { + "translation": "WebAssembly上で動作するGoインタプリタ、Yaegiを使用" + }, + "goWasm.link": { + "translation": "囲碁公式サイト 八重垣 XをY分で学ぶ、X=囲碁 LiveCodesドキュメンテーション スターターテンプレートの読み込み " + }, + "goWasm.name": { + "translation": "Go (Wasm)" + }, "haml.desc": { "translation": "clientside-haml-js を使用したクライアントサイド JavaScript ビューテンプレート用の Haml コンパイラ。" }, diff --git a/src/livecodes/i18n/locales/ja/language-info.ts b/src/livecodes/i18n/locales/ja/language-info.ts index d593748fc6..a2c7af56b1 100644 --- a/src/livecodes/i18n/locales/ja/language-info.ts +++ b/src/livecodes/i18n/locales/ja/language-info.ts @@ -127,6 +127,11 @@ const languageInfo: I18nLangInfoTranslation = { link: '<1><2>Go ウェブサイト <3><4>Go ドキュメント <5> <6>GopherJS リポジトリ <7> <8>X分でYを学ぶ、X=Go <9><10>スターターテンプレートを読み込む', name: 'Go', }, + goWasm: { + desc: 'WebAssembly上で動作するGoインタプリタ、Yaegiを使用', + link: '<1><2>囲碁公式サイト <3><4>八重垣 <5> <6>XをY分で学ぶ、X=囲碁 <7> <8>LiveCodesドキュメンテーション <9> <10>スターターテンプレートの読み込み ', + name: 'Go (Wasm)', + }, haml: { desc: 'clientside-haml-js を使用したクライアントサイド JavaScript ビューテンプレート用の Haml コンパイラ。', link: '<1><2>Haml 公式ウェブサイト <3> <4>Haml ドキュメント <5> <6>clientside-haml-js GitHub リポジトリ <7> <8>X分でYを学ぶ、X=haml <9> <10>LiveCodes ドキュメント ', diff --git a/src/livecodes/i18n/locales/ja/translation.lokalise.json b/src/livecodes/i18n/locales/ja/translation.lokalise.json index e6b88f2abf..af16dd574e 100644 --- a/src/livecodes/i18n/locales/ja/translation.lokalise.json +++ b/src/livecodes/i18n/locales/ja/translation.lokalise.json @@ -1950,6 +1950,9 @@ "templates.starter.cpp": { "translation": "C++スターター" }, + "templates.starter.cpp-wasm": { + "translation": "C++(Wasm)スターター" + }, "templates.starter.daisyui": { "translation": "daisyUIスターター" }, @@ -1965,6 +1968,9 @@ "templates.starter.go": { "translation": "Goスターター" }, + "templates.starter.go-wasm": { + "translation": "Go(Wasm)スターター" + }, "templates.starter.heading": { "translation": "スターターテンプレート" }, diff --git a/src/livecodes/i18n/locales/ja/translation.ts b/src/livecodes/i18n/locales/ja/translation.ts index 2ad602c9ab..2b90d5ba45 100644 --- a/src/livecodes/i18n/locales/ja/translation.ts +++ b/src/livecodes/i18n/locales/ja/translation.ts @@ -989,11 +989,13 @@ const translation: I18nTranslation = { coffeescript: 'CoffeeScriptスターター', commonlisp: 'Common Lispスターター', cpp: 'C++スターター', + 'cpp-wasm': 'C++(Wasm)スターター', daisyui: 'daisyUIスターター', diagrams: 'ダイアグラムスターター', fennel: 'Fennelスターター', gleam: 'Gleamスターター', go: 'Goスターター', + 'go-wasm': 'Go(Wasm)スターター', heading: 'スターターテンプレート', imba: 'Imbaスターター', java: 'Javaスターター', diff --git a/src/livecodes/i18n/locales/pt/language-info.lokalise.json b/src/livecodes/i18n/locales/pt/language-info.lokalise.json index 41d7a05153..1d9ef54339 100644 --- a/src/livecodes/i18n/locales/pt/language-info.lokalise.json +++ b/src/livecodes/i18n/locales/pt/language-info.lokalise.json @@ -222,6 +222,15 @@ "go.name": { "translation": "Go" }, + "goWasm.desc": { + "translation": "Intérprete Go em execução no WebAssembly, usando Yaegi" + }, + "goWasm.link": { + "translation": "Sítio Web oficial do Go Yaegi Aprender X em Y minutos, em que X=Go Documentações LiveCodes Carregar modelo inicial " + }, + "goWasm.name": { + "translation": "Go (Wasm)" + }, "haml.desc": { "translation": "Compilador Haml para modelos de visualização JavaScript do lado do cliente usando clientside-haml-js." }, diff --git a/src/livecodes/i18n/locales/pt/language-info.ts b/src/livecodes/i18n/locales/pt/language-info.ts index 1170313707..465969e0f3 100644 --- a/src/livecodes/i18n/locales/pt/language-info.ts +++ b/src/livecodes/i18n/locales/pt/language-info.ts @@ -127,6 +127,11 @@ const languageInfo: I18nLangInfoTranslation = { link: '<1><2>Site do Go <3><4>Documentação do Go <5> <6>Repositório do GopherJS <7> <8>Aprenda X em Y minutos, onde X=Go <9><10>Carregar modelo inicial', name: 'Go', }, + goWasm: { + desc: 'Intérprete Go em execução no WebAssembly, usando Yaegi', + link: '<1><2>Sítio Web oficial do Go <3><4>Yaegi <5> <6>Aprender X em Y minutos, em que X=Go <7> <8>Documentações LiveCodes <9> <10>Carregar modelo inicial ', + name: 'Go (Wasm)', + }, haml: { desc: 'Compilador Haml para modelos de visualização JavaScript do lado do cliente usando clientside-haml-js.', link: '<1><2>Site oficial do Haml <3> <4>Documentação do Haml <5> <6>Repositório GitHub do clientside-haml-js <7> <8>Aprenda X em Y minutos, onde X=haml <9> <10>Documentação do LiveCodes ', diff --git a/src/livecodes/i18n/locales/pt/translation.lokalise.json b/src/livecodes/i18n/locales/pt/translation.lokalise.json index 1be85db133..851a549b21 100644 --- a/src/livecodes/i18n/locales/pt/translation.lokalise.json +++ b/src/livecodes/i18n/locales/pt/translation.lokalise.json @@ -1950,6 +1950,9 @@ "templates.starter.cpp": { "translation": "Iniciante C++" }, + "templates.starter.cpp-wasm": { + "translation": "Iniciante C++ (Wasm)" + }, "templates.starter.daisyui": { "translation": "Iniciante daisyUI" }, @@ -1965,6 +1968,9 @@ "templates.starter.go": { "translation": "Iniciante Go" }, + "templates.starter.go-wasm": { + "translation": "Iniciante Go (Wasm)" + }, "templates.starter.heading": { "translation": "Modelos Iniciantes" }, diff --git a/src/livecodes/i18n/locales/pt/translation.ts b/src/livecodes/i18n/locales/pt/translation.ts index 7260f4e3c3..ded4a0be2f 100644 --- a/src/livecodes/i18n/locales/pt/translation.ts +++ b/src/livecodes/i18n/locales/pt/translation.ts @@ -990,11 +990,13 @@ const translation: I18nTranslation = { coffeescript: 'Iniciante CoffeeScript', commonlisp: 'Iniciante Common Lisp', cpp: 'Iniciante C++', + 'cpp-wasm': 'Iniciante C++ (Wasm)', daisyui: 'Iniciante daisyUI', diagrams: 'Iniciante Diagrams', fennel: 'Iniciante Fennel', gleam: 'Iniciante Gleam', go: 'Iniciante Go', + 'go-wasm': 'Iniciante Go (Wasm)', heading: 'Modelos Iniciantes', imba: 'Iniciante Imba', java: 'Iniciante Java', diff --git a/src/livecodes/i18n/locales/ru/language-info.lokalise.json b/src/livecodes/i18n/locales/ru/language-info.lokalise.json index a3852feaa0..c4786bc7d4 100644 --- a/src/livecodes/i18n/locales/ru/language-info.lokalise.json +++ b/src/livecodes/i18n/locales/ru/language-info.lokalise.json @@ -222,6 +222,15 @@ "go.name": { "translation": "Go" }, + "goWasm.desc": { + "translation": "Интерпретатор Go, работающий на WebAssembly, с использованием Yaegi" + }, + "goWasm.link": { + "translation": "Официальный сайт Go Yaegi Узнайте X за Y минут, где X=Go Документация LiveCodes Загрузите стартовый шаблон " + }, + "goWasm.name": { + "translation": "Go (Wasm)" + }, "haml.desc": { "translation": "Компилятор Haml для клиентских шаблонов JavaScript с использованием clientside-haml-js." }, diff --git a/src/livecodes/i18n/locales/ru/language-info.ts b/src/livecodes/i18n/locales/ru/language-info.ts index 3554d0a592..c25e54afcb 100644 --- a/src/livecodes/i18n/locales/ru/language-info.ts +++ b/src/livecodes/i18n/locales/ru/language-info.ts @@ -129,6 +129,11 @@ const languageInfo: I18nLangInfoTranslation = { link: '<1><2>Сайт Go <3><4>Документация Go <5> <6>Репозиторий GopherJS <7> <8>Изучите X за Y минут, где X=Go <9><10>Загрузить стартовый шаблон', name: 'Go', }, + goWasm: { + desc: 'Интерпретатор Go, работающий на WebAssembly, с использованием Yaegi', + link: '<1><2>Официальный сайт Go <3><4>Yaegi <5> <6>Узнайте X за Y минут, где X=Go <7> <8>Документация LiveCodes <9> <10>Загрузите стартовый шаблон ', + name: 'Go (Wasm)', + }, haml: { desc: 'Компилятор Haml для клиентских шаблонов JavaScript с использованием clientside-haml-js.', link: '<1><2>Официальный сайт Haml <3> <4>Документация Haml <5> <6>GitHub-репозиторий clientside-haml-js <7> <8>Изучите X за Y минут, где X=haml <9> <10>Документация LiveCodes ', diff --git a/src/livecodes/i18n/locales/ru/translation.lokalise.json b/src/livecodes/i18n/locales/ru/translation.lokalise.json index dece03eb6f..97c0d3a820 100644 --- a/src/livecodes/i18n/locales/ru/translation.lokalise.json +++ b/src/livecodes/i18n/locales/ru/translation.lokalise.json @@ -1950,6 +1950,9 @@ "templates.starter.cpp": { "translation": "Стартер C++" }, + "templates.starter.cpp-wasm": { + "translation": "Стартер C++ (Wasm)" + }, "templates.starter.daisyui": { "translation": "Стартер daisyUI" }, @@ -1965,6 +1968,9 @@ "templates.starter.go": { "translation": "Стартер Go" }, + "templates.starter.go-wasm": { + "translation": "Стартер Go (Wasm)" + }, "templates.starter.heading": { "translation": "Стартовые шаблоны" }, diff --git a/src/livecodes/i18n/locales/ru/translation.ts b/src/livecodes/i18n/locales/ru/translation.ts index b631cf88f0..ee01115053 100644 --- a/src/livecodes/i18n/locales/ru/translation.ts +++ b/src/livecodes/i18n/locales/ru/translation.ts @@ -989,11 +989,13 @@ const translation: I18nTranslation = { coffeescript: 'Стартер CoffeeScript', commonlisp: 'Стартер Common Lisp', cpp: 'Стартер C++', + 'cpp-wasm': 'Стартер C++ (Wasm)', daisyui: 'Стартер daisyUI', diagrams: 'Стартер диаграмм', fennel: 'Стартер Fennel', gleam: 'Стартер Gleam', go: 'Стартер Go', + 'go-wasm': 'Стартер Go (Wasm)', heading: 'Стартовые шаблоны', imba: 'Стартер Imba', java: 'Стартер Java', diff --git a/src/livecodes/i18n/locales/ur/language-info.lokalise.json b/src/livecodes/i18n/locales/ur/language-info.lokalise.json index 3126f5aa1b..08222d5590 100644 --- a/src/livecodes/i18n/locales/ur/language-info.lokalise.json +++ b/src/livecodes/i18n/locales/ur/language-info.lokalise.json @@ -222,6 +222,15 @@ "go.name": { "translation": "گو" }, + "goWasm.desc": { + "translation": "Yaegi کا استعمال کرتے ہوئے WebAssembly پر چلنے والے مترجم پر جائیں۔" + }, + "goWasm.link": { + "translation": "سرکاری ویب سائٹ پر جائیں یگی Y منٹ میں X سیکھیں ، جہاں X = Go لائیو کوڈز دستاویزات لوڈ اسٹارٹر سانچہ " + }, + "goWasm.name": { + "translation": "Go (Wasm)" + }, "haml.desc": { "translation": "کلائنٹ سائیڈ جاوا اسکرپٹ ویو ٹیمپلیٹس کے لیے کلائنٹ سائیڈ-ہیمل-جے ایس کا استعمال کرتے ہوئے ہیمل کمپائلر۔" }, diff --git a/src/livecodes/i18n/locales/ur/language-info.ts b/src/livecodes/i18n/locales/ur/language-info.ts index 9dc26e5bb9..d8d9f66514 100644 --- a/src/livecodes/i18n/locales/ur/language-info.ts +++ b/src/livecodes/i18n/locales/ur/language-info.ts @@ -128,6 +128,11 @@ const languageInfo: I18nLangInfoTranslation = { link: '<1><2>گو ویب سائٹ <3><4>گو دستاویزات <5> <6>گوفر جے ایس ریپو <7> <8>X کو Y منٹ میں سیکھیں، جہاں X=گو <9><10>ابتدائی سانچہ لوڈ کریں', name: 'گو', }, + goWasm: { + desc: 'Yaegi کا استعمال کرتے ہوئے WebAssembly پر چلنے والے مترجم پر جائیں۔', + link: '<1><2>سرکاری ویب سائٹ پر جائیں <3><4>یگی <5> <6>Y منٹ میں X سیکھیں ، جہاں X = Go <7> <8>لائیو کوڈز دستاویزات <9> <10>لوڈ اسٹارٹر سانچہ ', + name: 'Go (Wasm)', + }, haml: { desc: 'کلائنٹ سائیڈ جاوا اسکرپٹ ویو ٹیمپلیٹس کے لیے کلائنٹ سائیڈ-ہیمل-جے ایس کا استعمال کرتے ہوئے ہیمل کمپائلر۔', link: '<1><2>ہیمل کی سرکاری ویب سائٹ <3> <4>ہیمل دستاویزات <5> <6>کلائنٹ سائیڈ-ہیمل-جے ایس گٹ ہب ریپو <7> <8>X کو Y منٹ میں سیکھیں، جہاں X=ہیمل <9> <10>لائیو کوڈز دستاویزات ', diff --git a/src/livecodes/i18n/locales/ur/translation.lokalise.json b/src/livecodes/i18n/locales/ur/translation.lokalise.json index f72e40283a..c3c5d43461 100644 --- a/src/livecodes/i18n/locales/ur/translation.lokalise.json +++ b/src/livecodes/i18n/locales/ur/translation.lokalise.json @@ -1950,6 +1950,9 @@ "templates.starter.cpp": { "translation": "سی++ شروعاتی" }, + "templates.starter.cpp-wasm": { + "translation": "سی++ (واسم) شروعاتی" + }, "templates.starter.daisyui": { "translation": "daisyUI شروعاتی" }, @@ -1965,6 +1968,9 @@ "templates.starter.go": { "translation": "گو شروعاتی" }, + "templates.starter.go-wasm": { + "translation": "گو (واسم) شروعاتی" + }, "templates.starter.heading": { "translation": "شروعاتی سانچے" }, diff --git a/src/livecodes/i18n/locales/ur/translation.ts b/src/livecodes/i18n/locales/ur/translation.ts index 27a451cd4f..24aa686bdd 100644 --- a/src/livecodes/i18n/locales/ur/translation.ts +++ b/src/livecodes/i18n/locales/ur/translation.ts @@ -989,11 +989,13 @@ const translation: I18nTranslation = { coffeescript: 'کافی اسکرپٹ شروعاتی', commonlisp: 'کامن لسپ شروعاتی', cpp: 'سی++ شروعاتی', + 'cpp-wasm': 'سی++ (واسم) شروعاتی', daisyui: 'daisyUI شروعاتی', diagrams: 'ڈایاگرامز شروعاتی', fennel: 'فینل شروعاتی', gleam: 'گلیم شروعاتی', go: 'گو شروعاتی', + 'go-wasm': 'گو (واسم) شروعاتی', heading: 'شروعاتی سانچے', imba: 'امبا شروعاتی', java: 'جاوا شروعاتی', diff --git a/src/livecodes/i18n/locales/zh-CN/language-info.lokalise.json b/src/livecodes/i18n/locales/zh-CN/language-info.lokalise.json index e53ce3c702..3f1399c9d7 100644 --- a/src/livecodes/i18n/locales/zh-CN/language-info.lokalise.json +++ b/src/livecodes/i18n/locales/zh-CN/language-info.lokalise.json @@ -222,6 +222,15 @@ "go.name": { "translation": "Go" }, + "goWasm.desc": { + "translation": "使用 Yaegi 在 WebAssembly 上运行 Go 解释器" + }, + "goWasm.link": { + "translation": "围棋官方网站 Yaegi 在 Y 分钟内学会 X,其中 X=Go LiveCodes 文档 加载启动模板 " + }, + "goWasm.name": { + "translation": "Go (Wasm)" + }, "haml.desc": { "translation": "使用 clientside-haml-js 的 Haml 编译器,用于编写客户端侧 JavaScript 视图模板。" }, diff --git a/src/livecodes/i18n/locales/zh-CN/language-info.ts b/src/livecodes/i18n/locales/zh-CN/language-info.ts index c06b638aa9..a01bb4731e 100644 --- a/src/livecodes/i18n/locales/zh-CN/language-info.ts +++ b/src/livecodes/i18n/locales/zh-CN/language-info.ts @@ -124,6 +124,11 @@ const languageInfo: I18nLangInfoTranslation = { link: '<1><2>Go 官方网站 <3><4>Go 文档 <5> <6>GopherJS 仓库 <7> <8>在 Y 分钟内学习 X,其中 X=Go <9><10>加载启动模板', name: 'Go', }, + goWasm: { + desc: '使用 Yaegi 在 WebAssembly 上运行 Go 解释器', + link: '<1><2>围棋官方网站 <3><4>Yaegi <5> <6>在 Y 分钟内学会 X,其中 X=Go <7> <8>LiveCodes 文档 <9> <10>加载启动模板 ', + name: 'Go (Wasm)', + }, haml: { desc: '使用 clientside-haml-js 的 Haml 编译器,用于编写客户端侧 JavaScript 视图模板。', link: '<1><2>Haml 官网 <3> <4>Haml 文档 <5> <6>clientside-haml-js GitHub 仓库 <7> <8>在 Y 分钟内学习 X,其中 X=haml <9> <10>LiveCodes 文档 ', diff --git a/src/livecodes/i18n/locales/zh-CN/translation.lokalise.json b/src/livecodes/i18n/locales/zh-CN/translation.lokalise.json index 34d128b18c..b2e410b40f 100644 --- a/src/livecodes/i18n/locales/zh-CN/translation.lokalise.json +++ b/src/livecodes/i18n/locales/zh-CN/translation.lokalise.json @@ -1950,6 +1950,9 @@ "templates.starter.cpp": { "translation": "C++ 启动模板" }, + "templates.starter.cpp-wasm": { + "translation": "C++(WASM)启动模板" + }, "templates.starter.daisyui": { "translation": "daisyUI 启动模板" }, @@ -1965,6 +1968,9 @@ "templates.starter.go": { "translation": "Go 启动模板" }, + "templates.starter.go-wasm": { + "translation": "Go(WASM)启动模板" + }, "templates.starter.heading": { "translation": "启动模板" }, diff --git a/src/livecodes/i18n/locales/zh-CN/translation.ts b/src/livecodes/i18n/locales/zh-CN/translation.ts index e5b541584e..1185d6da5d 100644 --- a/src/livecodes/i18n/locales/zh-CN/translation.ts +++ b/src/livecodes/i18n/locales/zh-CN/translation.ts @@ -987,11 +987,13 @@ const translation: I18nTranslation = { coffeescript: 'CoffeeScript 启动模板', commonlisp: 'Common Lisp 启动模板', cpp: 'C++ 启动模板', + 'cpp-wasm': 'C++(WASM)启动模板', daisyui: 'daisyUI 启动模板', diagrams: '图表启动模板', fennel: 'Fennel 启动模板', gleam: 'Gleam 启动模板', go: 'Go 启动模板', + 'go-wasm': 'Go(WASM)启动模板', heading: '启动模板', imba: 'Imba 启动模板', java: 'Java 启动模板', diff --git a/src/livecodes/languages/clojurescript/lang-clojurescript.ts b/src/livecodes/languages/clojurescript/lang-clojurescript.ts index 60199edeee..6b438e8c5e 100644 --- a/src/livecodes/languages/clojurescript/lang-clojurescript.ts +++ b/src/livecodes/languages/clojurescript/lang-clojurescript.ts @@ -23,9 +23,9 @@ export const clojurescript: LanguageSpecs = { imports: { 'cherry-cljs': cherryCljsBaseUrl + 'index.js', 'cherry-cljs/cljs.core.js': cherryCljsBaseUrl + 'cljs.core.js', - 'cherry-cljs/lib/clojure.string.js': 'lib/clojure.string.js', - 'cherry-cljs/lib/clojure.set.js': 'lib/clojure.set.js', - 'cherry-cljs/lib/clojure.walk.js': 'lib/clojure.walk.js', + 'cherry-cljs/lib/clojure.string.js': cherryCljsBaseUrl + 'lib/clojure.string.js', + 'cherry-cljs/lib/clojure.set.js': cherryCljsBaseUrl + 'lib/clojure.set.js', + 'cherry-cljs/lib/clojure.walk.js': cherryCljsBaseUrl + 'lib/clojure.walk.js', 'squint-cljs': squintCljsBaseUrl + 'index.js', 'squint-cljs/core.js': squintCljsBaseUrl + 'core.js', 'squint-cljs/string.js': squintCljsBaseUrl + 'string.js', diff --git a/src/livecodes/languages/diagrams/lang-diagrams-compiler-esm.ts b/src/livecodes/languages/diagrams/lang-diagrams-compiler-esm.ts index ff45f7120b..d27d3da78d 100644 --- a/src/livecodes/languages/diagrams/lang-diagrams-compiler-esm.ts +++ b/src/livecodes/languages/diagrams/lang-diagrams-compiler-esm.ts @@ -16,7 +16,7 @@ import { cytoscapeUrl, elkjsBaseUrl, graphreCdnUrl, - hpccJsCdnUrl, + hpccJsCdnBaseUrl, mermaidCdnUrl, nomnomlCdnUrl, pintoraUrl, @@ -180,7 +180,7 @@ const compileGnuplot = async (code: string) => { const compileMermaid = async (code: string) => { let mermaid: any; const load = async () => { - mermaid = (await import(mermaidCdnUrl)).default; + mermaid = await loadScript(mermaidCdnUrl, 'mermaid'); mermaid.initialize({ startOnLoad: false, }); @@ -201,7 +201,7 @@ const compileMermaid = async (code: string) => { const compileGraphviz = async (code: string) => { let graphviz: any; const load = async () => { - const hpccWasm = await import(hpccJsCdnUrl); + const hpccWasm = await import(hpccJsCdnBaseUrl + 'index.js'); graphviz = await hpccWasm.Graphviz.load(); }; const render = (src: string, script: HTMLScriptElement) => { diff --git a/src/livecodes/languages/go-wasm/index.ts b/src/livecodes/languages/go-wasm/index.ts new file mode 100644 index 0000000000..ef6da91230 --- /dev/null +++ b/src/livecodes/languages/go-wasm/index.ts @@ -0,0 +1 @@ +export * from './lang-go-wasm'; diff --git a/src/livecodes/languages/go-wasm/lang-go-wasm-script.ts b/src/livecodes/languages/go-wasm/lang-go-wasm-script.ts new file mode 100644 index 0000000000..42641e491f --- /dev/null +++ b/src/livecodes/languages/go-wasm/lang-go-wasm-script.ts @@ -0,0 +1,174 @@ +/* eslint-disable no-console */ +import { createWorkerFromContent } from '../../utils/utils'; +import { yaegiWasmBaseUrl } from '../../vendors'; + +const workerSrc = ` +(async () => { + importScripts('${yaegiWasmBaseUrl}wasm_exec.js'); + + const wasmUrl = '${yaegiWasmBaseUrl}yaegi-browser.wasm'; + const wasmResponse = await fetch(wasmUrl); + + const initYaegi = async () => { + try { + let instance; + const go = new Go(); + try { + const streaming = await WebAssembly.instantiateStreaming(wasmResponse.clone(), go.importObject); + instance = streaming.instance; + } catch { + const resp = await wasmResponse.clone(); + if (!resp.ok) + throw new Error('Failed to fetch yaegi-browser.wasm: ' + resp.status); + const bytes = await resp.arrayBuffer(); + const res = await WebAssembly.instantiate(bytes, go.importObject); + instance = res.instance; + } + go.run(instance); + } catch (err) { + console.error('Failed to load Yaegi:', err); + throw err; + } + }; + + addEventListener('message', async (e) => { + const runCode = async (code, input) => { + let output = null; + let error = null; + let exitCode = 0; + + const originalConsoleLog = console.log; + const originalConsoleError = console.error; + let capturedOutput = ''; + let capturedError = ''; + + try { + await initYaegi(); + + console.log = (...args) => { + capturedOutput += args.join(' ') + '\\n'; + }; + + console.error = (...args) => { + capturedError += args.join(' ') + '\\n'; + }; + + // Set up stdin if input is provided + if (input && globalThis.setStdin) { + globalThis.setStdin(input); + } + + if (self.yaegi) { + try { + const result = await self.yaegi.eval(code); + } catch (err) { + console.error('Yaegi execution error:', err); + throw err; + } + } else { + throw new Error( + 'Yaegi not found on window. Make sure yaegi-browser.wasm is loaded correctly.', + ); + } + + output = capturedOutput.trim(); + if (capturedError) { + error = capturedError.trim(); + exitCode = 1; + } + } catch (err) { + error = err.message || err.toString(); + exitCode = 1; + } finally { + // Restore console functions + console.log = originalConsoleLog; + console.error = originalConsoleError; + } + + return { input, output, error, exitCode }; + }; + + const code = e.data.code; + const input = e.data.input; + const result = code.trim() ? await runCode(code, input) : {}; + postMessage(result); + }); + + // Initialize Yaegi when worker starts + initYaegi() + .then(() => { + postMessage({ loaded: true }); + }) + .catch((err) => { + console.error('Failed to initialize Yaegi:', err); + postMessage({ error: err.message }); + }); +})(); +`; + +livecodes.goWasm = livecodes.goWasm || {}; + +livecodes.goWasm.worker = livecodes.goWasm.worker || createWorkerFromContent(workerSrc); +const worker: Worker = livecodes.goWasm.worker; + +const initialized = new Promise((resolve) => { + const onload = (e: MessageEvent) => { + console.log('Loading Yaegi WebAssembly...'); + if (e.data.loaded) { + worker.removeEventListener('message', onload); + console.log('Yaegi WebAssembly loaded successfully.'); + livecodes.goWasm.worker.loaded = true; + resolve(); + } + }; + if (!livecodes.goWasm.worker.loaded) { + worker.addEventListener('message', onload); + } else { + resolve(); + } +}); + +livecodes.goWasm.run = + livecodes.goWasm.run || + ((input: string) => + new Promise((resolve) => { + let code = ''; + livecodes.goWasm.input = input; + livecodes.goWasm.output = null; + const scripts = document.querySelectorAll('script[type="text/go-wasm"]'); + scripts.forEach((script) => (code += (script.textContent ?? '') + '\n')); + worker.onmessage = function (e: MessageEvent) { + if (e.data.loaded) return; + const result = e.data; + if (result.error != null) { + console.error(result.error); + } else if (result.output != null) { + console.log(result.output); + } + livecodes.goWasm.input = result.input; + livecodes.goWasm.output = result.output; + livecodes.goWasm.error = result.error; + livecodes.goWasm.exitCode = result.exitCode; + livecodes.goWasm.ready = true; + resolve(result); + }; + + worker.postMessage({ code, input: `${String(input ?? '')}` }); + })); + +livecodes.goWasm.loaded = new Promise((resolve) => { + const i = setInterval(() => { + if (livecodes.goWasm.ready) { + clearInterval(i); + return resolve(); + } + }, 50); +}); + +window.addEventListener('load', async () => { + livecodes.goWasm.ready = false; + parent.postMessage({ type: 'loading', payload: true }, '*'); + await initialized; + await livecodes.goWasm.run(livecodes.goWasm.input); + parent.postMessage({ type: 'loading', payload: false }, '*'); +}); diff --git a/src/livecodes/languages/go-wasm/lang-go-wasm.ts b/src/livecodes/languages/go-wasm/lang-go-wasm.ts new file mode 100644 index 0000000000..8f8fcb0291 --- /dev/null +++ b/src/livecodes/languages/go-wasm/lang-go-wasm.ts @@ -0,0 +1,19 @@ +import type { LanguageSpecs } from '../../models'; +import { go } from '../go/lang-go'; + +export const goWasm: LanguageSpecs = { + name: 'go-wasm', + title: 'Go (Wasm)', + formatter: go.formatter, + compiler: { + factory: () => async (code) => code, + scripts: ({ baseUrl }) => [baseUrl + '{{hash:lang-go-wasm-script.js}}'], + liveReload: true, + scriptType: 'text/go-wasm', + compiledCodeLanguage: 'go', + }, + extensions: ['wasm.go', 'go-wasm', 'gowasm'], + editor: 'script', + editorLanguage: 'go', + largeDownload: true, +}; diff --git a/src/livecodes/languages/languages.ts b/src/livecodes/languages/languages.ts index 9f318652af..54e3479030 100644 --- a/src/livecodes/languages/languages.ts +++ b/src/livecodes/languages/languages.ts @@ -23,6 +23,7 @@ import { fennel } from './fennel'; import { flow } from './flow'; import { gleam } from './gleam'; import { go } from './go'; +import { goWasm } from './go-wasm'; import { haml } from './haml'; import { handlebars } from './handlebars'; import { html } from './html'; @@ -142,6 +143,7 @@ export const languages: LanguageSpecs[] = [ ruby, rubyWasm, go, + goWasm, php, phpWasm, cpp, diff --git a/src/livecodes/languages/postgresql/lang-postgresql-compiler-esm.ts b/src/livecodes/languages/postgresql/lang-postgresql-compiler-esm.ts index aacc74f089..baa85b387b 100644 --- a/src/livecodes/languages/postgresql/lang-postgresql-compiler-esm.ts +++ b/src/livecodes/languages/postgresql/lang-postgresql-compiler-esm.ts @@ -1,6 +1,6 @@ import type { CompilerFunction } from '../../models'; import { getLanguageCustomSettings, safeName } from '../../utils/utils'; -import { pgliteUrl } from '../../vendors'; +import { pgliteBaseUrl } from '../../vendors'; declare global { interface Window { @@ -11,7 +11,7 @@ declare global { export const pgSqlCompiler: CompilerFunction = async (code, { config }) => { if (!code.trim()) return '{ data: [] }'; - window.PGlite = window.PGlite || (await import(pgliteUrl)).PGlite; + window.PGlite = window.PGlite || (await import(pgliteBaseUrl + 'index.js')).PGlite; const options = getLanguageCustomSettings('pgsql', config); const { dbName, scriptURLs, ...pgliteOptions } = options; diff --git a/src/livecodes/languages/python-wasm/lang-python-wasm-script.ts b/src/livecodes/languages/python-wasm/lang-python-wasm-script.ts index e0ec66b066..b4a378516b 100644 --- a/src/livecodes/languages/python-wasm/lang-python-wasm-script.ts +++ b/src/livecodes/languages/python-wasm/lang-python-wasm-script.ts @@ -1,5 +1,5 @@ /* eslint-disable no-underscore-dangle */ -import { fontAwesomeUrl, pyodideBaseUrl } from '../../vendors'; +import { pyodideBaseUrl } from '../../vendors'; declare const loadPyodide: any; @@ -50,12 +50,6 @@ window.addEventListener('load', async () => { } async function prepareEnv() { - // needed for matplotlib icons - const stylesheet = document.createElement('link'); - stylesheet.rel = 'stylesheet'; - stylesheet.href = fontAwesomeUrl; - document.head.append(stylesheet); - await pyodideReady; const patchInput = ` from js import prompt diff --git a/src/livecodes/languages/rescript/lang-rescript-compiler-esm.ts b/src/livecodes/languages/rescript/lang-rescript-compiler-esm.ts index c12bf5243d..53170e41de 100644 --- a/src/livecodes/languages/rescript/lang-rescript-compiler-esm.ts +++ b/src/livecodes/languages/rescript/lang-rescript-compiler-esm.ts @@ -6,7 +6,10 @@ import { reasonReactUrl, reasonStdLibBaseUrl, requireUrl, - rescriptCdnBaseUrl, + rescriptCdnUrl1, + rescriptCdnUrl2, + rescriptCdnUrl3, + rescriptCdnUrl4, rescriptStdLibBaseUrl, } from '../../vendors'; @@ -57,12 +60,7 @@ const loadCompiler = async (language: Language) => { ); } else { window.require( - [ - rescriptCdnBaseUrl + 'compiler.js', - rescriptCdnBaseUrl + 'compiler-builtins/cmij.js', - rescriptCdnBaseUrl + '%40rescript/react/cmij.js', - rescriptCdnBaseUrl + '%40rescript/core/cmij.js', - ], + [rescriptCdnUrl1, rescriptCdnUrl2, rescriptCdnUrl3, rescriptCdnUrl4], () => { window.rescript_ocaml_compiler = window.rescript_compiler; window.rescript_compiler = undefined; diff --git a/src/livecodes/languages/rescript/lang-rescript-formatter.ts b/src/livecodes/languages/rescript/lang-rescript-formatter.ts index 6851d6cb7d..4036a7d642 100644 --- a/src/livecodes/languages/rescript/lang-rescript-formatter.ts +++ b/src/livecodes/languages/rescript/lang-rescript-formatter.ts @@ -1,12 +1,12 @@ import type { LanguageFormatter } from '../../models'; import { getAbsoluteUrl } from '../../utils'; -import { rescriptCdnBaseUrl } from '../../vendors'; +import { rescriptCdnUrl1 } from '../../vendors'; declare const importScripts: any; const createRescriptFormatter: LanguageFormatter['factory'] = (baseUrl, language) => { if (!(self as any).rescript_compiler) { - importScripts(getAbsoluteUrl(rescriptCdnBaseUrl + 'compiler.js', baseUrl)); + importScripts(getAbsoluteUrl(rescriptCdnUrl1, baseUrl)); } const compiler = (self as any).rescript_compiler.make(); compiler.setModuleSystem('es6'); diff --git a/src/livecodes/languages/tailwindcss/processor-tailwindcss-compiler.ts b/src/livecodes/languages/tailwindcss/processor-tailwindcss-compiler.ts index 5c3a3b4575..d9bf67bf17 100644 --- a/src/livecodes/languages/tailwindcss/processor-tailwindcss-compiler.ts +++ b/src/livecodes/languages/tailwindcss/processor-tailwindcss-compiler.ts @@ -6,6 +6,7 @@ import { modulesService } from '../../services'; import { getLanguageCustomSettings } from '../../utils/utils'; import { tailwindcss3Url, tailwindcssBaseUrl, vendorsBaseUrl } from '../../vendors'; import { lightningcssFeatures } from '../lightningcss/processor-lightningcss-compiler'; +import { addCodeInStyleBlocks } from './utils'; declare const self: any; @@ -186,21 +187,21 @@ self.createTailwindcssCompiler = (): CompilerFunction => { }, }); - return tailwind.generateStylesFromContent(code, [ - ``, - ]); + const html = ``; + return tailwind.generateStylesFromContent(addCodeInStyleBlocks(code, html), [html]); }; const tailwind4: CompilerFunction = async (code, { config, options }) => { - const prepareCode = (css: string) => { + const prepareCode = (css: string, html: string) => { let result = replaceStyleImports(css, [/tailwindcss/g]); if (!result.includes('@import')) { result = `@import "tailwindcss";${result}`; } - return result; + return addCodeInStyleBlocks(result, html); }; + const html = ``; - const css = prepareCode(code); + const css = prepareCode(code, html); try { const compiler = await self.tailwindcss.compile(css, { base: '/', diff --git a/src/livecodes/languages/tailwindcss/utils.ts b/src/livecodes/languages/tailwindcss/utils.ts new file mode 100644 index 0000000000..ec39c53713 --- /dev/null +++ b/src/livecodes/languages/tailwindcss/utils.ts @@ -0,0 +1,13 @@ +export const addCodeInStyleBlocks = (css: string, html: string) => { + // from compiler/compile-blocks.ts#compileBlocks + const getBlockPattern = (el: 'style', langAttr = 'lang') => + `(<${el}\\s*)(?:([\\s\\S]*?)${langAttr}\\s*=\\s*["']([A-Za-z0-9 _]*)["'])?((?:[^>]*)>)([\\s\\S]*?)(<\\/${el}>)`; + const pattern = getBlockPattern('style'); + for (const arr of [...html.matchAll(new RegExp(pattern, 'g'))]) { + const content = arr[5]; + if (content?.trim()) { + css += `\n${content}`; + } + } + return css; +}; diff --git a/src/livecodes/languages/utils.ts b/src/livecodes/languages/utils.ts index 903ca3aa17..beafe93f45 100644 --- a/src/livecodes/languages/utils.ts +++ b/src/livecodes/languages/utils.ts @@ -1,6 +1,6 @@ import type { Compiler, Config, CustomSettings, Language, Processor } from '../models'; import { getLanguageCustomSettings } from '../utils/utils'; -import { highlightjsUrl } from '../vendors'; +import { vendorsBaseUrl } from '../vendors'; export const getLanguageByAlias = (alias: string = ''): Language | undefined => { if (!alias) return; @@ -97,7 +97,8 @@ export const getCustomSettings = ( export const detectLanguage = async (code: string, languages: Language[]) => { (window as any).HighlightJS = - (window as any).HighlightJS || (await import(highlightjsUrl)).default; + (window as any).HighlightJS || + (await import(vendorsBaseUrl + 'highlight.js/highlight.js')).default; const result = (window as any).HighlightJS.highlightAuto(code, languages); return { language: result.language as Language, diff --git a/src/livecodes/main.ts b/src/livecodes/main.ts index b65d9c8f4a..d99136b19a 100644 --- a/src/livecodes/main.ts +++ b/src/livecodes/main.ts @@ -5,7 +5,7 @@ import appHTML from './html/app.html?raw'; import type { API, CDN, Config, CustomEvents, EmbedOptions } from './models'; import { modulesService } from './services/modules'; import { isInIframe } from './utils/utils'; -import { codeMirrorBaseUrl, esModuleShimsPath } from './vendors'; +import { codeMirrorBasePath, esModuleShimsPath } from './vendors'; export type { API, Config }; @@ -77,7 +77,7 @@ export const livecodes = (container: string, config: Partial = {}): Prom const loadApp = async () => { const appCDN = await modulesService.checkCDNs(esModuleShimsPath, params.get('appCDN') as CDN); - + const codeMirrorBaseUrl = modulesService.getUrl(codeMirrorBasePath, appCDN as CDN); const supportsImportMaps = HTMLScriptElement.supports ? HTMLScriptElement.supports('importmap') : false; @@ -104,6 +104,17 @@ export const livecodes = (container: string, config: Partial = {}): Prom import * as mod from '${baseUrl}{{hash:codemirror.js}}'; window['${baseUrl}{{hash:codemirror.js}}'] = mod; + `, + ) + .replace( + /{{polyfillScript}}/g, + process.env.LOCAL_MODULES === 'true' + ? '' + : ` + `, ) .replace(/{{codemirrorCoreUrl}}/g, `${codeMirrorBaseUrl}codemirror-core.js`) diff --git a/src/livecodes/services/modules.ts b/src/livecodes/services/modules.ts index 5ec3f1d25a..54f5e57a74 100644 --- a/src/livecodes/services/modules.ts +++ b/src/livecodes/services/modules.ts @@ -1,6 +1,7 @@ import type { CDN } from '../models'; declare const globalThis: { appCDN: CDN }; +const localModules = process.env.LOCAL_MODULES === 'true'; const moduleCDNs: CDN[] = [ 'esm.sh', @@ -60,14 +61,16 @@ export const modulesService = { }, getUrl: (path: string, cdn?: CDN) => - path.startsWith('http') || path.startsWith('data:') - ? path - : getCdnUrl(path, false, cdn || getAppCDN()) || path, + path.startsWith('data:') ? path : getCdnUrl(path, false, cdn || getAppCDN()) || path, cdnLists: { npm: npmCDNs, module: moduleCDNs, gh: ghCDNs }, checkCDNs: async (testModule: string, preferredCDN?: CDN) => { - const cdns: CDN[] = [preferredCDN, ...modulesService.cdnLists.npm].filter(Boolean) as CDN[]; + const modulesBaseUrl = new URL('./modules/', location.href).href as CDN; + const localCDN = localModules ? modulesBaseUrl : undefined; + const cdns = [preferredCDN, localCDN, ...modulesService.cdnLists.npm].filter( + (x) => x != null, + ) as CDN[]; for (const cdn of cdns) { try { const res = await fetch(modulesService.getUrl(testModule, cdn), { @@ -94,6 +97,10 @@ export const getAppCDN = (): CDN => { }; const getCdnUrl = (modName: string, isModule: boolean, defaultCDN?: CDN) => { + if (localModules && !isModule) { + return getLocalUrl(modName, defaultCDN); + } + if (modName.startsWith('data:')) return modName; const post = isModule && modName.startsWith('unpkg:') ? '?module' : ''; if (modName.startsWith('gh:')) { modName = modName.replace('gh', ghCDNs[0]); @@ -107,9 +114,23 @@ const getCdnUrl = (modName: string, isModule: boolean, defaultCDN?: CDN) => { return modName.replace(pattern, template) + post; } } + if (modName.startsWith('http')) return modName; return null; }; +const getLocalUrl = (modName: string, modulesBaseUrl = '/modules/') => { + modName = modName + .replace('https://unpkg.com/', '') + .replace('unpkg:', '') + .replaceAll('https://', '') + .replaceAll(':', '_') + .replaceAll('?', '_'); + if (modName.includes('pyodide')) { + modName = modName.replace('cdn.jsdelivr.net/', ''); + } + return `${modulesBaseUrl}${modName}`; +}; + // based on https://github.com/neoascetic/rawgithack/blob/master/web/rawgithack.js const TEMPLATES: Array<[RegExp, string]> = [ [/^(esm\.sh:)(.+)/i, 'https://esm.sh/$2'], diff --git a/src/livecodes/styles/inc-menu.scss b/src/livecodes/styles/inc-menu.scss index 98ecebe646..4b92adf7c2 100644 --- a/src/livecodes/styles/inc-menu.scss +++ b/src/livecodes/styles/inc-menu.scss @@ -570,7 +570,7 @@ i.arrow { width: var(--s16); &[for='theme-color-custom'] { - background: conic-gradient(in hsl longer hue, red 0 0); + background: conic-gradient(in hsl longer hue, red 0 100%); filter: contrast(0.5); } diff --git a/src/livecodes/templates/starter/cpp-wasm-starter.ts b/src/livecodes/templates/starter/cpp-wasm-starter.ts index 776e0e1cf8..8e54515005 100644 --- a/src/livecodes/templates/starter/cpp-wasm-starter.ts +++ b/src/livecodes/templates/starter/cpp-wasm-starter.ts @@ -3,7 +3,7 @@ import type { Template } from '../../models'; export const cppWasmStarter: Template = { name: 'cpp-wasm', aliases: ['clang'], - title: 'C++ (Wasm) Starter', + title: window.deps.translateString('templates.starter.cpp-wasm', 'C++ (Wasm) Starter'), thumbnail: 'assets/templates/cpp.svg', activeEditor: 'script', markup: { diff --git a/src/livecodes/templates/starter/go-wasm-starter.ts b/src/livecodes/templates/starter/go-wasm-starter.ts new file mode 100644 index 0000000000..1952a33370 --- /dev/null +++ b/src/livecodes/templates/starter/go-wasm-starter.ts @@ -0,0 +1,177 @@ +import type { Template } from '../../models'; + +export const goWasmStarter: Template = { + name: 'go-wasm', + title: window.deps.translateString('templates.starter.go-wasm', 'Go (Wasm) Starter'), + thumbnail: 'assets/templates/go.svg', + activeEditor: 'script', + markup: { + language: 'html', + content: ` +
    +

    Go (Wasm)

    + + +
    +

    Interactive Counter

    +

    Current count: 0

    + +
    + +
    +

    Stdin Input Demo

    +

    Enter your name:

    + + +

    +
    + + +
    + + +`.trimStart(), + }, + style: { + language: 'css', + content: ` +.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(), + }, + script: { + language: 'go-wasm', + content: ` +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +func main() { + // Read input from stdin + scanner := bufio.NewScanner(os.Stdin) + + if scanner.Scan() { + input := strings.TrimSpace(scanner.Text()) + + if count, err := strconv.Atoi(input); err == nil { + newCount := count + 1 + fmt.Println(newCount) + return + } + + fmt.Printf("Hello, %s!\\n", input) + } else { + fmt.Println("Hello from Go WebAssembly!") + } +} +`.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/livecodes/types/bundle-types.ts b/src/livecodes/types/bundle-types.ts index 89da0f9284..bf03eaaa38 100644 --- a/src/livecodes/types/bundle-types.ts +++ b/src/livecodes/types/bundle-types.ts @@ -1,6 +1,6 @@ // based on dts-bundle -import { pathBrowserifyUrl } from '../vendors'; +import { vendorsBaseUrl } from '../vendors'; // const dtsExp = /\.d\.ts$/; const bomOptExp = /^\uFEFF?/; @@ -62,7 +62,7 @@ export interface BundleResult { } export async function bundle(options: Options): Promise { - const path = await import(pathBrowserifyUrl); + const path = (await import(vendorsBaseUrl + 'path-browserify/path-browserify.js')).default; assert(typeof options === 'object' && options, 'options must be an object'); // option parsing & validation diff --git a/src/livecodes/utils/utils.ts b/src/livecodes/utils/utils.ts index b22e84b73a..5ae0cc8929 100644 --- a/src/livecodes/utils/utils.ts +++ b/src/livecodes/utils/utils.ts @@ -623,21 +623,37 @@ export const isFocusable = /* @__PURE__ */ (item: any | null): boolean => { * whose values are different from the destination object. */ export const compareObjects = /* @__PURE__ */ ( - srcObj: Record, - dstObj: Record, + srcObj: Partial>, + dstObj: Partial>, ) => { const diff: string[] = []; for (const key of Object.keys(srcObj)) { - if (typeof srcObj[key] === 'function') { + const srcObjProp = srcObj[key]; + const dstObjProp = dstObj[key]; + if (typeof srcObjProp === 'function') { continue; } else if (!(key in dstObj)) { diff.push(key); - } else if (srcObj[key] !== null && typeof srcObj[key] === 'object') { - const objDiff = compareObjects(srcObj[key] as any, dstObj[key] as any).map( - (k) => `${key}.${k}`, - ); - diff.push(...objDiff); - } else if (srcObj[key] !== dstObj[key]) { + } else if (srcObjProp !== null && typeof srcObjProp === 'object') { + if (!dstObjProp || typeof dstObjProp !== 'object') { + diff.push(key); + } else if (Array.isArray(srcObjProp)) { + if (!Array.isArray(dstObjProp)) { + diff.push(key); + } else if (srcObjProp.length !== dstObjProp.length) { + diff.push(key); + } else { + for (let i = 0; i < srcObjProp.length; i++) { + if (srcObjProp[i] !== dstObjProp[i]) { + diff.push(`${key}[${i}]`); + } + } + } + } else { + const objDiff = compareObjects(srcObjProp, dstObjProp).map((k) => `${key}.${k}`); + diff.push(...objDiff); + } + } else if (srcObjProp !== dstObjProp) { diff.push(key); } } diff --git a/src/livecodes/vendors.ts b/src/livecodes/vendors.ts index 3ee6afdcfc..8f21222fc7 100644 --- a/src/livecodes/vendors.ts +++ b/src/livecodes/vendors.ts @@ -1,9 +1,17 @@ import { modulesService } from './services/modules'; -const { getUrl, getModuleUrl } = modulesService; +// - only use `getUrl` (not `getModuleUrl` or plain URLs) - except `es-module-shims` and `codeMirrorBasePath` +// - only use `gh:` or `unpkg: prefixes if required +// - always add full version and file extension +// - minimize usage of baseUrls if possible +// - if es module imports others, use baseUrl instead +// - excluding `vendorsBaseUrl`, the file is sorted alphabetically +// see scripts/download-modules.js + +const { getUrl } = modulesService; export const vendorsBaseUrl = // 'http://127.0.0.1:8081/'; - /* @__PURE__ */ getUrl('@live-codes/browser-compilers@0.22.3/dist/'); + /* @__PURE__ */ getUrl('@live-codes/browser-compilers@0.22.4/dist/'); export const acornUrl = /* @__PURE__ */ getUrl('acorn@8.12.1/dist/acorn.js'); @@ -41,7 +49,7 @@ export const browserJestUrl = /* @__PURE__ */ getUrl( export const brythonBaseUrl = /* @__PURE__ */ getUrl('brython@3.12.4/'); -export const chaiUrl = /* @__PURE__ */ getModuleUrl('chai@5.1.2'); +export const chaiUrl = /* @__PURE__ */ getUrl('chai@5.2.1/chai.js'); export const cherryCljsBaseUrl = /* @__PURE__ */ getUrl('cherry-cljs@0.2.19/'); @@ -85,7 +93,7 @@ export const codeiumProviderUrl = /* @__PURE__ */ getUrl( '@live-codes/monaco-codeium-provider@0.2.2/dist/index.js', ); -export const codeMirrorBaseUrl = /* @__PURE__ */ getUrl('@live-codes/codemirror@0.3.2/build/'); +export const codeMirrorBasePath = '@live-codes/codemirror@0.3.2/build/'; export const coffeeScriptUrl = /* @__PURE__ */ getUrl( 'coffeescript@2.7.0/lib/coffeescript-browser-compiler-legacy/coffeescript.js', @@ -99,6 +107,8 @@ 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.2/src/'); + export const csstreeUrl = /* @__PURE__ */ getUrl('css-tree@2.3.1/dist/csstree.js'); export const cytoscapeSvgUrl = /* @__PURE__ */ getUrl('cytoscape-svg@0.4.0/cytoscape-svg.js'); @@ -109,7 +119,7 @@ export const ddietrCmThemesBaseUrl = /* @__PURE__ */ getUrl( '@ddietr/codemirror-themes@1.4.2/dist/theme/', ); -export const doppioJvmBaseUrl = 'https://unpkg.com/@seth0x41/doppio@1.0.0/'; +export const doppioJvmBaseUrl = /* @__PURE__ */ getUrl('unpkg:@seth0x41/doppio@1.0.0/'); export const dotUrl = /* @__PURE__ */ getUrl('dot@1.1.3/doT.js'); @@ -127,109 +137,111 @@ export const fflateUrl = /* @__PURE__ */ getUrl('fflate@0.8.1/esm/browser.js'); export const flexSearchUrl = /* @__PURE__ */ getUrl('flexsearch@0.7.21/dist/flexsearch.bundle.js'); -export const fontAnonymousProUrl = /* @__PURE__ */ getUrl( - '@fontsource/anonymous-pro@4.5.9/index.css', -); +export const fontAnonymousProBaseUrl = /* @__PURE__ */ getUrl('@fontsource/anonymous-pro@4.5.9/'); -export const fontAstigmataUrl = /* @__PURE__ */ getUrl( - 'gh:hatemhosny/astigmata-font@6d0ee00a07fb1932902f0b81a504d075d47bd52f/index.css', +export const fontAstigmataBaseUrl = /* @__PURE__ */ getUrl( + 'gh:hatemhosny/astigmata-font@6d0ee00a07fb1932902f0b81a504d075d47bd52f/', ); -export const fontAwesomeUrl = /* @__PURE__ */ getUrl('font-awesome@4.7.0/css/font-awesome.min.css'); +export const fontAwesomeBaseUrl = /* @__PURE__ */ getUrl('font-awesome@4.7.0/'); -export const fontCascadiaCodeUrl = /* @__PURE__ */ getUrl( - '@fontsource/cascadia-code@4.2.1/index.css', -); +export const fontCascadiaCodeBaseUrl = /* @__PURE__ */ getUrl('@fontsource/cascadia-code@4.2.1/'); export const fontCodeNewRomanUrl = /* @__PURE__ */ getUrl( - 'https://fonts.cdnfonts.com/css/code-new-roman-2', + 'https://fonts.cdnfonts.com/css/code-new-roman-2?style.css', ); -export const fontComicMonoUrl = /* @__PURE__ */ getUrl('comic-mono@0.0.1/index.css'); +export const fontComicMonoBaseUrl = /* @__PURE__ */ getUrl('comic-mono@0.0.1/'); -export const fontCourierPrimeUrl = /* @__PURE__ */ getUrl( - '@fontsource/courier-prime@4.5.9/index.css', -); +export const fontCourierPrimeBaseUrl = /* @__PURE__ */ getUrl('@fontsource/courier-prime@4.5.9/'); export const fontDECTerminalModernUrl = /* @__PURE__ */ getUrl( - 'https://fonts.cdnfonts.com/css/dec-terminal-modern', + 'https://fonts.cdnfonts.com/css/dec-terminal-modern?style.css', ); -export const fontDejaVuMonoUrl = /* @__PURE__ */ getUrl('@fontsource/dejavu-mono@4.5.4/index.css'); +export const fontDejaVuMonoBaseUrl = /* @__PURE__ */ getUrl('@fontsource/dejavu-mono@4.5.4/'); -export const fontFantasqueUrl = /* @__PURE__ */ getUrl( - '@typopro/web-fantasque-sans-mono@3.7.5/TypoPRO-FantasqueSansMono.css', +export const fontFantasqueBaseUrl = /* @__PURE__ */ getUrl( + '@typopro/web-fantasque-sans-mono@3.7.5/', ); -export const fontFiraCodeUrl = /* @__PURE__ */ getUrl('firacode@6.2.0/distr/fira_code.css'); +export const fontFiraCodeBaseUrl = /* @__PURE__ */ getUrl('firacode@6.2.0/distr/'); -export const fontFixedsysUrl = /* @__PURE__ */ getUrl('https://fonts.cdnfonts.com/css/fixedsys-62'); +export const fontFixedsysUrl = /* @__PURE__ */ getUrl( + 'https://fonts.cdnfonts.com/css/fixedsys-62?style.css', +); -export const fontHackUrl = /* @__PURE__ */ getUrl('hack-font@3.3.0/build/web/hack.css'); +export const fontHackBaseUrl = /* @__PURE__ */ getUrl('hack-font@3.3.0/build/web/'); -export const fontHermitUrl = /* @__PURE__ */ getUrl('typeface-hermit@0.0.44/index.css'); +export const fontHermitBaseUrl = /* @__PURE__ */ getUrl('typeface-hermit@0.0.44/'); export const fontIBMPlexMonoUrl = /* @__PURE__ */ getUrl( - 'https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&display=swap', + 'https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&display=swap&style.css', ); export const fontInconsolataUrl = /* @__PURE__ */ getUrl( - 'https://fonts.googleapis.com/css2?family=Inconsolata&display=swap', + 'https://fonts.googleapis.com/css2?family=Inconsolata&display=swap&style.css', ); export const fontInterUrl = /* @__PURE__ */ getUrl( - 'https://fonts.googleapis.com/css?family=Inter:300,400,500', + 'https://fonts.googleapis.com/css?family=Inter:300,400,500&style.css', ); -export const fontIosevkaUrl = /* @__PURE__ */ getUrl('@fontsource/iosevka@4.5.4/index.css'); +export const fontIosevkaBaseUrl = /* @__PURE__ */ getUrl('@fontsource/iosevka@4.5.4/'); -export const fontJetbrainsMonoUrl = /* @__PURE__ */ getUrl( - '@fontsource/jetbrains-mono@4.5.11/index.css', +export const fontJetbrainsMonoBaseUrl = /* @__PURE__ */ getUrl( + '@fontsource/jetbrains-mono@4.5.11/', ); export const fontMaterialIconsUrl = /* @__PURE__ */ getUrl( - 'https://fonts.googleapis.com/css?family=Material+Icons&display=swap', + 'https://fonts.googleapis.com/css?family=Material+Icons&display=swap&style.css', ); -export const fontMenloUrl = /* @__PURE__ */ getUrl('https://fonts.cdnfonts.com/css/menlo'); +export const fontMenloUrl = /* @__PURE__ */ getUrl( + 'https://fonts.cdnfonts.com/css/menlo?style.css', +); export const fontMonaspaceBaseUrl = /* @__PURE__ */ getUrl('monaspace-font@0.0.2/'); -export const fontMonofurUrl = /* @__PURE__ */ getUrl('https://fonts.cdnfonts.com/css/monofur'); +export const fontMonofurUrl = /* @__PURE__ */ getUrl( + 'https://fonts.cdnfonts.com/css/monofur?style.css', +); -export const fontMonoidUrl = /* @__PURE__ */ getUrl('@typopro/web-monoid@3.7.5/TypoPRO-Monoid.css'); +export const fontMonoidBaseUrl = /* @__PURE__ */ getUrl('@typopro/web-monoid@3.7.5/'); export const fontNotoUrl = /* @__PURE__ */ getUrl( - 'https://fonts.googleapis.com/css2?family=Noto+Sans+Mono&display=swap', + 'https://fonts.googleapis.com/css2?family=Noto+Sans+Mono&display=swap&style.css', ); export const fontNovaMonoUrl = /* @__PURE__ */ getUrl( - 'https://fonts.googleapis.com/css2?family=Nova+Mono&display=swap', + 'https://fonts.googleapis.com/css2?family=Nova+Mono&display=swap&style.css', ); -export const fontOpenDyslexicUrl = /* @__PURE__ */ getUrl( - '@fontsource/opendyslexic@4.5.4/index.css', -); +export const fontOpenDyslexicBaseUrl = /* @__PURE__ */ getUrl('@fontsource/opendyslexic@4.5.4/'); export const fontProFontWindowsUrl = /* @__PURE__ */ getUrl( - 'https://fonts.cdnfonts.com/css/profontwindows', + 'https://fonts.cdnfonts.com/css/profontwindows?style.css', ); -export const fontRobotoMonoUrl = /* @__PURE__ */ getUrl('@fontsource/roboto-mono@4.5.8/index.css'); +export const fontRobotoMonoBaseUrl = /* @__PURE__ */ getUrl('@fontsource/roboto-mono@4.5.8/'); -export const fontSFMonoUrl = /* @__PURE__ */ getUrl('https://fonts.cdnfonts.com/css/sf-mono'); +export const fontSFMonoUrl = /* @__PURE__ */ getUrl( + 'https://fonts.cdnfonts.com/css/sf-mono?style.css', +); -export const fontSourceCodeProUrl = /* @__PURE__ */ getUrl( - '@fontsource/source-code-pro@4.5.12/index.css', +export const fontSourceCodeProBaseUrl = /* @__PURE__ */ getUrl( + '@fontsource/source-code-pro@4.5.12/', ); -export const fontSpaceMonoUrl = /* @__PURE__ */ getUrl('@fontsource/space-mono@4.5.10/index.css'); +export const fontSpaceMonoBaseUrl = /* @__PURE__ */ getUrl('@fontsource/space-mono@4.5.10/'); -export const fontSudoVarUrl = /* @__PURE__ */ getUrl('https://fonts.cdnfonts.com/css/sudo-var'); +export const fontSudoVarUrl = /* @__PURE__ */ getUrl( + 'https://fonts.cdnfonts.com/css/sudo-var?style.css', +); -export const fontUbuntuMonoUrl = /* @__PURE__ */ getUrl('@fontsource/ubuntu-mono@4.5.11/index.css'); +export const fontUbuntuMonoBaseUrl = /* @__PURE__ */ getUrl('@fontsource/ubuntu-mono@4.5.11/'); -export const fontVictorMonoUrl = /* @__PURE__ */ getUrl('victormono@1.5.4/dist/index.css'); +export const fontVictorMonoBaseUrl = /* @__PURE__ */ getUrl('victormono@1.5.4/dist/'); export const fscreenUrl = /* @__PURE__ */ getUrl('fscreen@1.2.0/dist/fscreen.esm.js'); @@ -245,9 +257,7 @@ export const graphreCdnUrl = /* @__PURE__ */ getUrl('graphre@0.1.3/dist/graphre. export const handlebarsBaseUrl = /* @__PURE__ */ getUrl('handlebars@4.7.8/dist/'); -export const highlightjsUrl = /* @__PURE__ */ getModuleUrl('highlight.js@11.11.1'); - -export const hpccJsCdnUrl = /* @__PURE__ */ getUrl('@hpcc-js/wasm@2.13.0/dist/index.js'); +export const hpccJsCdnBaseUrl = /* @__PURE__ */ getUrl('@hpcc-js/wasm@2.13.0/dist/'); export const htmlToImageUrl = /* @__PURE__ */ getUrl('html-to-image@1.11.11/dist/html-to-image.js'); @@ -283,11 +293,11 @@ export const lunaObjViewerStylesUrl = /* @__PURE__ */ getUrl( 'luna-object-viewer@0.2.4/luna-object-viewer.css', ); -export const malinaBaseUrl = /* @__PURE__ */ getUrl(`malinajs@0.7.19/`); +export const malinaBaseUrl = /* @__PURE__ */ getUrl('malinajs@0.7.19/'); export const markedUrl = /* @__PURE__ */ getUrl('marked@13.0.2/marked.min.js'); -export const mermaidCdnUrl = /* @__PURE__ */ getUrl('mermaid@10.2.2/dist/mermaid.esm.mjs'); +export const mermaidCdnUrl = /* @__PURE__ */ getUrl('mermaid@10.2.2/dist/mermaid.min.js'); export const metaPngUrl = /* @__PURE__ */ getUrl('meta-png@1.0.6/dist/meta-png.umd.js'); @@ -301,9 +311,7 @@ export const monacoThemesBaseUrl = /* @__PURE__ */ getUrl('monaco-themes@0.4.4/t export const monacoVimUrl = /* @__PURE__ */ getUrl('monaco-vim@0.4.1/dist/monaco-vim.js'); -export const monacoVolarUrl = /* @__PURE__ */ getUrl( - '@live-codes/monaco-volar@0.1.0/dist/index.js', -); +export const monacoVolarBaseUrl = /* @__PURE__ */ getUrl('@live-codes/monaco-volar@0.1.0/dist/'); export const mustacheUrl = /* @__PURE__ */ getUrl('mustache@4.2.0/mustache.js'); @@ -315,13 +323,11 @@ export const normalizeCssUrl = /* @__PURE__ */ getUrl('normalize.css@8.0.1/norma export const nunjucksBaseUrl = /* @__PURE__ */ getUrl('nunjucks@3.2.4/browser/'); -export const opalBaseUrl = /* @__PURE__ */ getUrl('https://cdn.opalrb.com/opal/1.8.2/'); +export const opalBaseUrl = /* @__PURE__ */ getUrl('gh:opal/opal-cdn@v1.8.2/opal/1.8.2/'); export const parinferUrl = /* @__PURE__ */ getUrl('parinfer@3.13.1/parinfer.js'); -export const pathBrowserifyUrl = /* @__PURE__ */ getModuleUrl('path-browserify@1.0.1'); - -export const pgliteUrl = /* @__PURE__ */ getUrl('@electric-sql/pglite@0.1.5/dist/index.js'); +export const pgliteBaseUrl = /* @__PURE__ */ getUrl('@electric-sql/pglite@0.1.5/dist/'); export const pintoraUrl = /* @__PURE__ */ getUrl( '@pintora/standalone@0.6.2/lib/pintora-standalone.umd.js', @@ -352,7 +358,7 @@ export const prismThemesLaserWaveUrl = /* @__PURE__ */ getUrl( ); export const pyodideBaseUrl = /* @__PURE__ */ getUrl( - 'https://cdn.jsdelivr.net/pyodide/v0.25.1/full/', + 'https://cdn.jsdelivr.net/pyodide/v0.28.0/full/', ); export const qrcodeUrl = /* @__PURE__ */ getUrl('easyqrcodejs@4.6.1/dist/easy.qrcode.min.js'); @@ -381,7 +387,18 @@ export const reasonReactUrl = /* @__PURE__ */ getUrl( export const reasonStdLibBaseUrl = /* @__PURE__ */ getUrl('@rescript/std@9.1.3/lib/es6/'); -export const rescriptCdnBaseUrl = /* @__PURE__ */ getUrl('https://cdn.rescript-lang.org/v11.1.2/'); +export const rescriptCdnUrl1 = /* @__PURE__ */ getUrl( + 'https://cdn.rescript-lang.org/v11.1.2/compiler.js', +); +export const rescriptCdnUrl2 = /* @__PURE__ */ getUrl( + 'https://cdn.rescript-lang.org/v11.1.2/compiler-builtins/cmij.js', +); +export const rescriptCdnUrl3 = /* @__PURE__ */ getUrl( + 'https://cdn.rescript-lang.org/v11.1.2/%40rescript/react/cmij.js', +); +export const rescriptCdnUrl4 = /* @__PURE__ */ getUrl( + 'https://cdn.rescript-lang.org/v11.1.2/%40rescript/core/cmij.js', +); export const rescriptStdLibBaseUrl = /* @__PURE__ */ getUrl('@rescript/std@11.1.2/lib/es6/'); @@ -435,19 +452,19 @@ export const tesseractUrl = /* @__PURE__ */ getUrl('tesseract.js@6.0.1/dist/tess export const twigUrl = /* @__PURE__ */ getUrl('twig@1.17.1/twig.min.js'); -export const typescriptUrl = /* @__PURE__ */ getUrl(`typescript@5.6.2/lib/typescript.js`); +export const typescriptUrl = /* @__PURE__ */ getUrl('typescript@5.6.2/lib/typescript.js'); export const typescriptVfsUrl = /* @__PURE__ */ getUrl('@typescript/vfs@1.5.3/dist/vfs.esm.js'); export const uniterUrl = /* @__PURE__ */ getUrl('uniter@2.18.0/dist/uniter.js'); -export const vegaCdnUrl = /* @__PURE__ */ getUrl('vega@5.25.0/build/vega.js'); +export const vegaCdnUrl = /* @__PURE__ */ getUrl('vega@5.25.0/build/vega.min.js'); -export const vegaLiteCdnUrl = /* @__PURE__ */ getUrl('vega-lite@5.9.3/build/vega-lite.js'); +export const vegaLiteCdnUrl = /* @__PURE__ */ getUrl('vega-lite@5.9.3/build/vega-lite.min.js'); -export const vue3CdnUrl = /* @__PURE__ */ getUrl('vue@3'); +export const vue3CdnUrl = /* @__PURE__ */ getUrl('vue@3.5.17/dist/vue.global.prod.js'); -export const vue2CdnUrl = /* @__PURE__ */ getUrl('vue@2'); +export const vue2CdnUrl = /* @__PURE__ */ getUrl('vue@2.7.16/dist/vue.min.js'); export const vueRuntimeUrl = /* @__PURE__ */ getUrl('vue@3/dist/vue.runtime.esm-browser.prod.js'); diff --git a/src/sdk/models.ts b/src/sdk/models.ts index 50695a9bdd..adebd1a1ae 100644 --- a/src/sdk/models.ts +++ b/src/sdk/models.ts @@ -968,6 +968,8 @@ export type Language = | 'riotjs' | 'malina' | 'malinajs' + | 'ripple' + | 'ripplejs' | 'xht' | 'coffeescript' | 'coffee' @@ -998,6 +1000,9 @@ export type Language = | 'rubywasm' | 'go' | 'golang' + | 'go-wasm' + | 'wasm.go' + | 'gowasm' | 'php' | 'php-wasm' | 'phpwasm' @@ -1350,6 +1355,7 @@ export interface Compiler { | 'text/commonlisp' | 'text/tcl' | 'text/prolog' + | 'text/go-wasm' | 'application/json' | 'application/lua' | 'text/fennel' @@ -1418,6 +1424,7 @@ export type TemplateName = | 'ruby' | 'ruby-wasm' | 'go' + | 'go-wasm' | 'php' | 'php-wasm' | 'cpp' diff --git a/src/sdk/vue.ts b/src/sdk/vue.ts index 312b7f98ce..18f767a3b9 100644 --- a/src/sdk/vue.ts +++ b/src/sdk/vue.ts @@ -125,7 +125,7 @@ const LiveCodes: LiveCodesComponent = { 'div', { ref: containerRef, - 'data-height': height, + 'data-height': height.value, }, ctx.slots.default?.() || '', ); diff --git a/vendor-licenses.md b/vendor-licenses.md index c3049f5b7c..a22b29a64e 100644 --- a/vendor-licenses.md +++ b/vendor-licenses.md @@ -315,3 +315,7 @@ WebR: [License](https://github.com/r-wasm/webr/blob/e47ab9854c9306c410f302579c66 Whirl: [MIT License](https://github.com/jh3y/whirl/tree/8de79b76a13200ccbd8b0b75b4f79978ef1ee890) Windi CSS: [MIT License](https://github.com/windicss/windicss/blob/bcd50d877e62630f191602ddeabd9f677cc6d90c/LICENSE) + +Yaegi: [Apache License 2.0](https://github.com/traefik/yaegi/blob/d93266d013f393a20122ef0dd3f579d411a066be/LICENSE) + +yaegi-wasm: [Apache License 2.0](https://github.com/Muhammad-Ayman/yaegi-wasm/blob/528a6bbac7464dc8911e50e0d617512f3e247e8c/LICENSE)