├── .gitignore
├── .npmignore
├── .npmrc
├── LICENSE
├── README.md
├── cjs
├── async.js
├── index.js
└── package.json
├── esm
├── async.js
└── index.js
├── package.json
└── test
├── async.js
├── context-async.js
├── context.js
├── index.js
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .nyc_output
3 | node_modules/
4 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .nyc_output
3 | .travis.yml
4 | node_modules/
5 | rollup/
6 | test/
7 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | ISC License
2 |
3 | Copyright (c) 2020-present, Andrea Giammarchi, @WebReflection
4 |
5 | Permission to use, copy, modify, and/or distribute this software for any
6 | purpose with or without fee is hereby granted, provided that the above
7 | copyright notice and this permission notice appear in all copies.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 | OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 | PERFORMANCE OF THIS SOFTWARE.
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🦄 µ land-ssr
2 |
3 | 
4 |
5 | **Social Media Photo by [Ben Klea](https://unsplash.com/@benkleaphoto) on [Unsplash](https://unsplash.com/)**
6 |
7 | *micro* land SSR, is [µland](https://github.com/WebReflection/uland#readme) API for SSR / DOM-less environments.
8 |
9 | It allows using same components code on the client and the server, producing *html* or *svg* streams.
10 |
11 | This module also exports `uland-ssr/async`, which is based on `uland-nofx/async`.
12 |
13 |
14 | ### 📣 Community Announcement
15 |
16 | Please ask questions in the [dedicated forum](https://webreflection.boards.net/) to help the community around this project grow ♥
17 |
18 | ---
19 |
20 | ## API
21 |
22 | ```js
23 | import {Component, render, html, useState} from 'uland-ssr';
24 |
25 | const Counter = Component((initialState) => {
26 | const [count, setCount] = useState(initialState);
27 | return html`
28 | setCount(count + 1)}>
29 | Count: ${count}
30 | `;
31 | });
32 |
33 | // basic example, creates the expected output
34 | // minus online events (would need re-hydration)
35 | render(String, () => html`
36 |
37 | A bounce of counters.
38 | ${Counter(0)} ${Counter(1)}
39 |
40 | `);
41 | ```
42 |
--------------------------------------------------------------------------------
/cjs/async.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {isArray} = require('uarray');
3 | const {
4 | html: $html,
5 | svg: $svg,
6 | render: $render
7 | } = require('uhtml-ssr/async');
8 |
9 | const html = async (template, ...values) => {
10 | await unrollValues(values);
11 | return $html(template, ...values);
12 | };
13 | html.for = createFor($html);
14 |
15 | const svg = async (template, ...values) => {
16 | await unrollValues(values);
17 | return $svg(template, ...values);
18 | };
19 | svg.for = createFor($svg);
20 |
21 | const render = async (where, what) => {
22 | const value = await (typeof what === 'function' ? what() : what);
23 | return $render(
24 | where,
25 | await (value instanceof Hook ? unroll(value) : value)
26 | );
27 | };
28 |
29 | exports.Component = Component;
30 | exports.render = render;
31 | exports.html = html;
32 | exports.svg = svg;
33 |
34 | (m => {
35 | exports.createContext = m.createContext;
36 | exports.useContext = m.useContext;
37 | exports.useCallback = m.useCallback;
38 | exports.useMemo = m.useMemo;
39 | exports.useEffect = m.useEffect;
40 | exports.useLayoutEffect = m.useLayoutEffect;
41 | exports.useReducer = m.useReducer;
42 | exports.useState = m.useState;
43 | exports.useRef = m.useRef;
44 | })(require('uhooks-nofx'));
45 |
46 | const unroll = ({f, c, a}) => f.apply(c, a);
47 |
48 | const unrollValues = async (values) => {
49 | const {length} = values;
50 | for (let i = 0; i < length; i++) {
51 | const hook = await values[i];
52 | if (hook instanceof Hook)
53 | values[i] = await unroll(hook);
54 | else if (isArray(hook))
55 | await unrollValues(hook);
56 | }
57 | };
58 |
59 | function Component(f) {
60 | return function () {
61 | return new Hook(f, this, arguments);
62 | };
63 | }
64 |
65 | function Hook(f, c, a) {
66 | this.f = f;
67 | this.c = c;
68 | this.a = a;
69 | }
70 |
71 | function createFor(uhtml) {
72 | return (e, id) => {
73 | return async (template, ...values) => {
74 | await unrollValues(values);
75 | return uhtml.for(e, id)(template, ...values);
76 | };
77 | };
78 | }
79 |
--------------------------------------------------------------------------------
/cjs/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {isArray} = require('uarray');
3 | const {
4 | html: $html,
5 | svg: $svg,
6 | render: $render
7 | } = require('uhtml-ssr');
8 |
9 | const html = /*async*/ (template, ...values) => {
10 | /*await*/ unrollValues(values);
11 | return $html(template, ...values);
12 | };
13 | html.for = createFor($html);
14 |
15 | const svg = /*async*/ (template, ...values) => {
16 | /*await*/ unrollValues(values);
17 | return $svg(template, ...values);
18 | };
19 | svg.for = createFor($svg);
20 |
21 | const render = /*async*/ (where, what) => {
22 | const value = /*await*/ (typeof what === 'function' ? what() : what);
23 | return $render(
24 | where,
25 | /*await*/ (value instanceof Hook ? unroll(value) : value)
26 | );
27 | };
28 |
29 | exports.Component = Component;
30 | exports.render = render;
31 | exports.html = html;
32 | exports.svg = svg;
33 |
34 | (m => {
35 | exports.createContext = m.createContext;
36 | exports.useContext = m.useContext;
37 | exports.useCallback = m.useCallback;
38 | exports.useMemo = m.useMemo;
39 | exports.useEffect = m.useEffect;
40 | exports.useLayoutEffect = m.useLayoutEffect;
41 | exports.useReducer = m.useReducer;
42 | exports.useState = m.useState;
43 | exports.useRef = m.useRef;
44 | })(require('uhooks-nofx'));
45 |
46 | const unroll = ({f, c, a}) => f.apply(c, a);
47 |
48 | const unrollValues = /*async*/ (values) => {
49 | const {length} = values;
50 | for (let i = 0; i < length; i++) {
51 | const hook = /*await*/ values[i];
52 | if (hook instanceof Hook)
53 | values[i] = /*await*/ unroll(hook);
54 | else if (isArray(hook))
55 | /*await*/ unrollValues(hook);
56 | }
57 | };
58 |
59 | function Component(f) {
60 | return function () {
61 | return new Hook(f, this, arguments);
62 | };
63 | }
64 |
65 | function Hook(f, c, a) {
66 | this.f = f;
67 | this.c = c;
68 | this.a = a;
69 | }
70 |
71 | function createFor(uhtml) {
72 | return (e, id) => {
73 | return /*async*/ (template, ...values) => {
74 | /*await*/ unrollValues(values);
75 | return uhtml.for(e, id)(template, ...values);
76 | };
77 | };
78 | }
79 |
--------------------------------------------------------------------------------
/cjs/package.json:
--------------------------------------------------------------------------------
1 | {"type":"commonjs"}
--------------------------------------------------------------------------------
/esm/async.js:
--------------------------------------------------------------------------------
1 | import {isArray} from 'uarray';
2 | import {
3 | html as $html,
4 | svg as $svg,
5 | render as $render
6 | } from 'uhtml-ssr/async';
7 |
8 | const html = async (template, ...values) => {
9 | await unrollValues(values);
10 | return $html(template, ...values);
11 | };
12 | html.for = createFor($html);
13 |
14 | const svg = async (template, ...values) => {
15 | await unrollValues(values);
16 | return $svg(template, ...values);
17 | };
18 | svg.for = createFor($svg);
19 |
20 | const render = async (where, what) => {
21 | const value = await (typeof what === 'function' ? what() : what);
22 | return $render(
23 | where,
24 | await (value instanceof Hook ? unroll(value) : value)
25 | );
26 | };
27 |
28 | export {Component, render, html, svg};
29 |
30 | export {
31 | createContext, useContext,
32 | useCallback, useMemo,
33 | useEffect, useLayoutEffect,
34 | useReducer, useState, useRef
35 | } from 'uhooks-nofx';
36 |
37 | const unroll = ({f, c, a}) => f.apply(c, a);
38 |
39 | const unrollValues = async (values) => {
40 | const {length} = values;
41 | for (let i = 0; i < length; i++) {
42 | const hook = await values[i];
43 | if (hook instanceof Hook)
44 | values[i] = await unroll(hook);
45 | else if (isArray(hook))
46 | await unrollValues(hook);
47 | }
48 | };
49 |
50 | function Component(f) {
51 | return function () {
52 | return new Hook(f, this, arguments);
53 | };
54 | }
55 |
56 | function Hook(f, c, a) {
57 | this.f = f;
58 | this.c = c;
59 | this.a = a;
60 | }
61 |
62 | function createFor(uhtml) {
63 | return (e, id) => {
64 | return async (template, ...values) => {
65 | await unrollValues(values);
66 | return uhtml.for(e, id)(template, ...values);
67 | };
68 | };
69 | }
70 |
--------------------------------------------------------------------------------
/esm/index.js:
--------------------------------------------------------------------------------
1 | import {isArray} from 'uarray';
2 | import {
3 | html as $html,
4 | svg as $svg,
5 | render as $render
6 | } from 'uhtml-ssr';
7 |
8 | const html = /*async*/ (template, ...values) => {
9 | /*await*/ unrollValues(values);
10 | return $html(template, ...values);
11 | };
12 | html.for = createFor($html);
13 |
14 | const svg = /*async*/ (template, ...values) => {
15 | /*await*/ unrollValues(values);
16 | return $svg(template, ...values);
17 | };
18 | svg.for = createFor($svg);
19 |
20 | const render = /*async*/ (where, what) => {
21 | const value = /*await*/ (typeof what === 'function' ? what() : what);
22 | return $render(
23 | where,
24 | /*await*/ (value instanceof Hook ? unroll(value) : value)
25 | );
26 | };
27 |
28 | export {Component, render, html, svg};
29 |
30 | export {
31 | createContext, useContext,
32 | useCallback, useMemo,
33 | useEffect, useLayoutEffect,
34 | useReducer, useState, useRef
35 | } from 'uhooks-nofx';
36 |
37 | const unroll = ({f, c, a}) => f.apply(c, a);
38 |
39 | const unrollValues = /*async*/ (values) => {
40 | const {length} = values;
41 | for (let i = 0; i < length; i++) {
42 | const hook = /*await*/ values[i];
43 | if (hook instanceof Hook)
44 | values[i] = /*await*/ unroll(hook);
45 | else if (isArray(hook))
46 | /*await*/ unrollValues(hook);
47 | }
48 | };
49 |
50 | function Component(f) {
51 | return function () {
52 | return new Hook(f, this, arguments);
53 | };
54 | }
55 |
56 | function Hook(f, c, a) {
57 | this.f = f;
58 | this.c = c;
59 | this.a = a;
60 | }
61 |
62 | function createFor(uhtml) {
63 | return (e, id) => {
64 | return /*async*/ (template, ...values) => {
65 | /*await*/ unrollValues(values);
66 | return uhtml.for(e, id)(template, ...values);
67 | };
68 | };
69 | }
70 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "uland-ssr",
3 | "version": "0.5.0",
4 | "description": "Same uland API for SSR / DOM-less environments",
5 | "main": "./cjs/index.js",
6 | "scripts": {
7 | "build": "npm run async && npm run cjs && npm run test",
8 | "cjs": "ascjs esm cjs",
9 | "async": "cp esm/index.js esm/async.js && sed -i.bck 's/uhtml-ssr/uhtml-ssr\\/async/; s/\\/\\*async\\*\\//async/; s/\\/\\*await\\*\\//await/' esm/async.js && rm -rf esm/async.js.bck",
10 | "test": "node test/index.js && node test/async.js"
11 | },
12 | "keywords": [
13 | "uland",
14 | "ssr",
15 | "hooks"
16 | ],
17 | "author": "Andrea Giammarchi",
18 | "license": "ISC",
19 | "devDependencies": {
20 | "ascjs": "^5.0.1"
21 | },
22 | "module": "./esm/index.js",
23 | "type": "module",
24 | "exports": {
25 | ".": {
26 | "import": "./esm/index.js",
27 | "default": "./cjs/index.js"
28 | },
29 | "./async": {
30 | "import": "./esm/async.js",
31 | "default": "./cjs/async.js"
32 | },
33 | "./package.json": "./package.json"
34 | },
35 | "dependencies": {
36 | "uarray": "^1.0.0",
37 | "uhooks-nofx": "^0.2.0",
38 | "uhtml-ssr": "^0.5.0"
39 | },
40 | "repository": {
41 | "type": "git",
42 | "url": "git+https://github.com/WebReflection/uland-ssr.git"
43 | },
44 | "bugs": {
45 | "url": "https://github.com/WebReflection/uland-ssr/issues"
46 | },
47 | "homepage": "https://github.com/WebReflection/uland-ssr#readme"
48 | }
49 |
--------------------------------------------------------------------------------
/test/async.js:
--------------------------------------------------------------------------------
1 | const {Component, render, html} = require('../cjs/async.js');
2 |
3 | const Item = Component(value => html`
4 | ${value}
5 | `);
6 |
7 | const AsyncList = Component(async items => html`
8 |
9 | ${(await items).map(value => Item(value))}
10 |
11 | `);
12 |
13 | render({write: console.log}, AsyncList(Promise.all([1, 2, 3])));
14 |
--------------------------------------------------------------------------------
/test/context-async.js:
--------------------------------------------------------------------------------
1 | const {
2 | Component,
3 | createContext,
4 | html,
5 | render,
6 | useContext,
7 | } = require("../cjs/async");
8 |
9 | const FooContext = createContext();
10 |
11 | const FooComponent = Component(() => {
12 | const value = useContext(FooContext);
13 | return html`${value}
`;
14 | });
15 |
16 | async function doRender(fooValue) {
17 | FooContext.provide(fooValue);
18 |
19 | // Realistically, a delay would happen inside rendering of complex components.
20 | await new Promise((resolve) => setTimeout(resolve, 1));
21 |
22 | const renderedHtml = await render(
23 | String,
24 | () =>
25 | html`
26 |
27 | ${FooComponent()}
28 |
29 | `,
30 | );
31 |
32 | console.log(renderedHtml);
33 | }
34 |
35 | doRender("123");
36 | doRender("456");
37 |
--------------------------------------------------------------------------------
/test/context.js:
--------------------------------------------------------------------------------
1 | const {
2 | Component,
3 | createContext,
4 | html,
5 | render,
6 | useContext,
7 | } = require("../cjs");
8 |
9 | const FooContext = createContext();
10 |
11 | const Inner = Component(() => {
12 | const value = useContext(FooContext);
13 | return html`${value}
`;
14 | });
15 |
16 | const Outer = Component(({ fooValue, children }) => {
17 | FooContext.provide(fooValue);
18 | return html`${children}
`;
19 | });
20 |
21 | const renderedHtml = render(
22 | String,
23 | () =>
24 | html`
25 |
26 | ${Outer({ fooValue: "123", children: Inner() })}
27 |
28 | `,
29 | );
30 |
31 | console.log(renderedHtml);
32 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | const {Component, render, html, useState} = require('../cjs');
2 |
3 | const Counter = Component((initialState) => {
4 | const [count, setCount] = useState(initialState);
5 | return html`
6 | setCount(count + 1)}>
7 | Count: ${count}
8 | `;
9 | });
10 |
11 | console.log(
12 | render(String, () => html`
13 |
14 | A bounce of counters.
15 | ${Counter(0)} ${Counter(1)}
16 |
17 | `)
18 | );
19 |
--------------------------------------------------------------------------------
/test/package.json:
--------------------------------------------------------------------------------
1 | {"type":"commonjs"}
--------------------------------------------------------------------------------