modernweb-dev / rocket

The modern web setup for static sites with a sprinkle of JavaScript
https://rocket.modern-web.dev
MIT License
368 stars 51 forks source link

[next] markdown code block use cases (script, render, story, preview, ...) #315

Open daKmoR opened 2 years ago

daKmoR commented 2 years ago

in markdown we currently have this

```js server
const foo = 'bar';
const foo = 'bar';

but we also have more

## Markdown

````md
## Overview

You will be able to...

Which will result in:

```js xxx
import { html } from 'lit';
const users = [
  { firstName: 'John', lastName: 'Doe' },
  { firstName: 'Jane', lastName: 'Doe' },
];
export const listOfUsers = () => html`
  <ul>
    ${users.map(user => html`<li>${user.firstName} ${user.lastName}</li>`)}
  </ul>
`;
export const listOfUsersWithFrame = () => html`
  <ul>
    ${users.map(user => html`<li>${user.firstName} ${user.lastName}</li>`)}
  </ul>
`;

which shows/executes the code at the place of the code block on the client
e.g. the above example would become

## HTML

```html
<h2>Overview</h2>
<p>You will be able to...</p>
<p>Which will result in:</p>

<mdjs-story mdjs-story-name="listOfUsers">
  <ul>
    <li>John Doe</li>
    <li>Jane Doe</li>
  </ul>
</mdjs-story>

<mdjs-preview mdjs-story-name="listOfUsersWithFrame">
  <pre><code>[...]</code></pre>
  <div slot="story">
    <ul>
      <li>John Doe</li><li>Jane Doe</li>
    </ul>
  </div>
</mdjs-preview>

Rendered

Screenshot 2022-03-07 at 09 24 18

Question

now the question is... would that make sense server-side as well? I guess... and what should the key be šŸ¤”

should it retain the story? or a complete "new" set?

suggestion from the top of my head...

hmmm inject, inject-frame seems not very intuitive... anyone got better ideas? šŸ¤”

daKmoR commented 2 years ago
daKmoR commented 2 years ago

this get's quite complicated... maybe use a "full" configuration? šŸ¤”

```js client
console.log('just execute');
export const foo = () => html`<p>render the p</p>`;
export const bar = () => html`<p>render the p AND the highlighted code block</p>`;
export const baz = () => html`<p>render the p AND the highlighted code block OPENED within a FRAME</p>`;
bashmish commented 2 years ago

follow-up to the discussion https://github.com/modernweb-dev/rocket/discussions/288

my use cases extend the proposal above with the following:

in addition to that I also want to

the syntax story({ option1: true, option2: 'value' }) has a principal difference: it allows using arbitrary configuration value types with ease, while unique keys leave you only with booleans

it might solve the following cases:

and last, but not least: object syntax will be easier to extend in the future, using real JS

pseudo code

---
defaultStoryConfig: { openCode: true, renderMethod: 'iframe' }
---

```js story({ ...defaultStoryConfig, minHeight: '200px' })
export const myStory = () => html`...my-story-code...`;

since we have `story` and `script`, I think it might be good to keep them both
the difference still makes sense: `story` is for rendering and `script` is for executing only
but the names can be improved, because not every rendering is a story, see the example below:

````markdown
## Setup

Setup data for demos:

```js script({ showCode: true })
import { html } from `lit`;
const users = [
  { firstName: 'John', lastName: 'Doe' },
  { firstName: 'Jane', lastName: 'Doe' },
];

Setup styles for demos:

<style>
/* some basic styles, e.g. for ul and li */
</style>

Demos

Demo in Light DOM (+ code block open by default)

export const listOfUsers = () => html`
  <ul>
    ${users.map(user => html`<li>${user.firstName} ${user.lastName}</li>`)}
  </ul>
`;

Demo in Shadow DOM

export const listOfUsers = () => html`
  <style>
  /* some isolated styles */
  </style>
  <ul>
    ${users.map(user => html`<li>${user.firstName} ${user.lastName}</li>`)}
  </ul>
`;

Demo in Iframe

// isolated JS env
import 'path/to/my-user.define.js'
export const listOfUsers = () => {
  const localUsers = [...];
  return html`
    <style>
    /* some isolated styles */
    </style>
    <ul>
      ${localUsers.map(user => html`<li><my-user user=${user}></my-user></li>`)}
    </ul>
  `;
}


I'd improve the names like this:
- `story` => `render`
- `script` => `execute`
- `frame` => `wrapper` (to prevent confusion with an iframe)

I'm just scraping the surface here, but I'll be happy to work on a test suite with many input/output examples for my use cases. Do you already have or are you planning to set smth up? I can either extend it or start making it myself and let you extend with your cases later.
daKmoR commented 2 years ago

all your examples are meant to run client side right?

so it would be client render and client execute?

Comparing client VS server of render

## Client Rendering

```js client render
export const foo = () => html`<p>content</p>`;

ā¤µļø

ā¤µļø (after mdjs-render is registered and initialized)

# shadow-dom

content

// VS

Server Only Rendering

export const foo = () => html`<p>content</p>`;

ā¤µļø

content


So `js client render` executes the render on the client and requires an initialized web component to render. `js server render` executes the render on the server and does not need any js.

hmmmm....

or do we even want hydration? šŸ¤”

e.g. similar to [hydration](https://github.com/modernweb-dev/rocket/discussions/302) of web components...
(`render-mode` is the current attribute for it... again better name would be nice)
export const foo = () => html`<p>content</p>`;

ā¤µļø

content



Benefit would be NO js needed to display it... and it could become interactive on click, on visible, on idle, ...
daKmoR commented 2 years ago

puuhhh that is getting more and more complex šŸ˜…

I think the workflow should be 1) update to v10 of unified 2) "finish" partial hydration for web components 3) look into this API again

so this might be a little bit šŸ˜…

as I do not want to block you... here are two ideas a) only do small adjustments to the API for example by "just" adding js story with-code and release it b) do (1) and (a) and release as a breaking change => e.g. server/hydration/add js options would happen in a next breaking change

bashmish commented 2 years ago

all your examples are meant to run client side right?

so it would be client render and client execute?

not really partially I do not understand your original proposal, but I feel like it's just another dimension to what we are discussing here, so can be solved with the same API which translates to another configuration option

also, why do you want to be explicit about it? I mean your end goal is to render, so if you can prerender on the server, then do it as a perf optimisation, if you can't, leave it to the client, if smth doesn't go right automatically, then forcing client/server via an option can give a way to solve it

```js server render
export const foo = () => html`<p>content</p>`;

ā¤µļø

content



this example makes me think we are talking about 2 different "server" render:
1. render only the Light DOM defined in the story template and leave the rest of work to the client
2. full SSR: entire tree with Shadow DOM and all children/other Shadow DOMs produced as a result of it (I'm not an expert in Shadow DOM SSR, so not sure I fully understand how it works), and this is proposed here https://github.com/modernweb-dev/rocket/issues/308

the 1 is pretty simple to do on the server, even if `<p>` is a WC, just rendering LightDOM is easy
while 2 is a totally different story and relying on the automatic behavior is probably not a good idea, so it's indeed important to keep it in mind when designing the API for new MDJS
bashmish commented 2 years ago

puuhhh that is getting more and more complex šŸ˜…

I think the workflow should be

  1. update to v10 of unified
  2. "finish" partial hydration for web components
  3. look into this API again

so this might be a little bit šŸ˜…

as I do not want to block you... here are two ideas a) only do small adjustments to the API for example by "just" adding js story with-code and release it b) do (1) and (a) and release as a breaking change => e.g. server/hydration/add js options would happen in a next breaking change

thanks for suggesting a way to unblock me :) I agree it's good to start small and solve the low hanging fruits first, but from my perspective the most valuable low hanging fruits are the ones which would solve my 2 issues here:

https://backlight.dev/edit/4BeMe20hqOWTkdUL2NuJ?branch=mdjs-issues%4044v7XI9EE5hzBwU7oxi8dbGjKz03&p=doc

image

given the current syntax, I'd introduce 2 new keys as previously discussed in https://github.com/modernweb-dev/rocket/discussions/288, except that after all good input we had here I'd name them differently, currently I like script-with-code and story-with-code, but we can change it when PR is technically ready, wdyt?

daKmoR commented 2 years ago

currently I like script-with-code and story-with-code, but we can change it when PR is technically ready, wdyt?

sounds good to me šŸ‘ looking forward to a PR šŸ¤—