Skip to content

luwes/wesc

Repository files navigation

WeSC

We are the Superlative Components!

Goals

  • HTML first (The Rule of Least Power)
  • Stay close to web standards
  • Define and create a superlative component authoring experience
  • Server language agnostic

A streaming web component bundler written in Rust using the lol-html parser.

The idea is to create a single-file HTML component format and builder that builds the HTML result super fast (streaming, low memory) and is server language agnostic.

Features

  • Streaming HTML bundler
  • Web component definition
  • Default and named slots with fallback content
  • Declarative Shadow DOM
  • CSS bundling
  • JS bundling

Example

wesc ./index.html

Syntax

index.html

<!doctype html>
<html>
  <head>
    <link rel="definition" name="w-card" href="./components/card.html">
  </head>
  <body>
    <w-card>
      <h3 slot="title">Title</h3>
      Description
    </w-card>
  </body>
</html>

components/card.html

<template>
<!-- or <template shadowrootmode="open"> -->
  <style>
    @scope {
      h3 {
        color: red;
      }
    }
  </style>
  <div>
    <h3><slot name="title">Add a slotted title</slot></h3>
    <p><slot>Add default slotted content</slot></p>
  </div>
</template>

<style>
  w-card {
    display: block;
  }
</style>

<!-- TODO: bundle to a global scripts.js -->
<script>
  class WCard extends HTMLElement {
    connectedCallback() {
      console.log('w-card connected');
    }
  }
  customElements.define('w-card', WCard);
</script>

WeSC DOM - Custom element server-side rendering

Custom elements are a crucial part of reaching these goals. The first problem WeSC is aiming to solve is rendering DSD (declarative shadow DOM) in a simple and approachable way for most use cases.

Examples

Have a look at the examples to see if your use case is handled and feel free to open an issue if not.

Simple Node usage

npm install wesc

index.js

import { promises as fs } from 'fs';
import { renderToString } from 'wesc/dom/server';

// Import web component library
import 'media-chrome';

// Process full page
let html = await fs.readFile('./app.html');

let out = await renderToString(html);

await fs.writeFile('./index.html', out);

app.html

<!-- ... -->
<media-controller>
  <video
    playsinline
    slot="media"
    src="https://stream.mux.com/A3VXy02VoUinw01pwyomEO3bHnG4P32xzV7u1j1FSzjNg/high.mp4"
  ></video>
  <media-poster-image
    slot="poster"
    src="https://image.mux.com/A3VXy02VoUinw01pwyomEO3bHnG4P32xzV7u1j1FSzjNg/thumbnail.jpg"
    placeholdersrc="data:image/jpeg;base64,/9j/2wBDABQODxIPDRQSEBIXFRQYHjIhHhwcHj0sLiQySUBMS0dARkVQWnNiUFVtVkVGZIhlbXd7gYKBTmCNl4x9lnN+gXz/2wBDARUXFx4aHjshITt8U0ZTfHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHz/wAARCAAUADADASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECBAP/xAAdEAEBAAEEAwAAAAAAAAAAAAAAARECAxITFCFR/8QAGQEAAwADAAAAAAAAAAAAAAAAAAEDAgQF/8QAGBEBAQEBAQAAAAAAAAAAAAAAAAETERL/2gAMAwEAAhEDEQA/ANeC4ldyI1b2EtIzzrrIqYZLvl5FGkGdbfQzGPvo76WsPxXLlfqbaA5va2iVJADgPELACsD/2Q=="
  ></media-poster-image>
  <media-loading-indicator slot="centered-chrome" noautohide></media-loading-indicator>
  <media-control-bar>
    <media-play-button></media-play-button>
    <media-mute-button></media-mute-button>
    <media-volume-range></media-volume-range>
    <media-time-display></media-time-display>
    <media-time-range></media-time-range>
    <media-duration-display></media-duration-display>
    <media-playback-rate-button></media-playback-rate-button>
    <media-fullscreen-button></media-fullscreen-button>
  </media-control-bar>
</media-controller>
<!-- ... -->

HTML output

view-source:https://wesc-node.netlify.app

Related