Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

Marko × LiveCodes — proof of concept

A single HTML page that compiles and runs Marko templates entirely in the browser. No build step, no bundler, no server-side compilation — the page pulls the Marko compiler, translator and DOM runtime from a CDN and does everything client-side.

This is a standalone spike ahead of adding Marko as a language to LiveCodes.

Run it

ES modules and import maps need a real origin, so serve the folder statically (any static host works — there is still no backend involved):

npx serve .
# or
python -m http.server 5173

Then open http://localhost:5173/. Edit the template on the left; the preview on the right updates on Run (or ⌘/Ctrl+Enter). Compile errors are shown as Marko code frames.

How it works

Everything happens in index.html:

  1. process.env.BUNDLE = '1' — @marko/compiler/modules.js checks this (or the existence of document) to switch off Node module resolution. Setting it explicitly makes browser mode deterministic.
  2. Import map → CDN — @marko/compiler@5.42.5 (Marko 6's compiler package) and marko@6.3.52 come from esm.sh.
  3. translator: marko/translator — Marko 6's core tags (<let>, <if>, <for>, …) are defined programmatically by the translator, so they resolve without touching the file system. Passing the module here is required because the string default "marko/translator" cannot be resolved without Node.
  4. fileSystem — a virtual FS that throws ENOENT and returns [] from readdirSync, standing in for a project with no files.
  5. Compile with { output: 'dom', modules: 'esm' } → an ES module that imports from marko/dom.
  6. Load & mount — the compiled code becomes a Blob module. The import map resolves its bare marko/dom specifier, and template.mount({}, host) renders it. Updates are reactive and batched asynchronously.

Multiple files

Yes. The PoC ships a two-file project (template.marko importing button.marko) with a tab per file. Two things make it work:

  • The virtual FS serves every file in the project, so import Button from "./button.marko" resolves at compile time. The compiled entry keeps that relative specifier (import ... from "./button.marko").
  • A blob module cannot resolve a relative .marko specifier, so buildModule walks the import graph depth-first, creates a blob URL per template, and rewrites each relative specifier to the child's blob URL before the parent module is created. Circular imports are detected and reported instead of looping.

Each <Button/> instance keeps its own state, so component encapsulation holds.

Verified

  • Counter template renders and increments on click.
  • <style={...}> binding and conditional text update reactively.
  • A two-file project renders: template.marko imports button.marko and mounts two independent instances.
  • Compile errors surface as readable Marko code frames (e.g. Missing ending "div" tag, or an unresolvable import).

Notes for the LiveCodes integration

  • LiveCodes' existing pattern fits: a lang-marko-compiler.ts built to an IIFE worker, a LanguageSpecs entry with a compiler.url/factory, and an imports map that resolves the compiled module's specifiers (marko/dom, marko/debug/dom, …) to vendors.ts URLs.
  • The compiler package references Node built-ins (path, assert, fs) but ships a browser babel build (@marko/compiler/internal/babel → dist/babel.web.js), so it is meant to be bundled for the browser — same approach as the Svelte/Vue workers.
  • Versions pinned here: @marko/compiler@5.42.5, marko@6.3.52.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages