Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

demo/restore products page and upgrade to Lit v3 #9

Closed
wants to merge 11 commits into from
1,049 changes: 902 additions & 147 deletions package-lock.json

Large diffs are not rendered by default.

13 changes: 10 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,23 @@
"clean": "rimraf public .greenwood .vercel",
"dev": "greenwood develop",
"build": "greenwood build",
"serve": "greenwood build && greenwood serve",
"start": "npm run serve"
"serve": "npm run clean && npm run build && greenwood serve",
"start": "npm run serve",
"postinstall": "patch-package"
},
"overrides": {
"@lit-labs/ssr": "^3.2.0",
"lit": "^3.1.0"
},
"dependencies": {
"lit": "^2.8.0"
"@lit-labs/ssr-client": "^1.1.6",
"lit": "^3.1.0"
},
"devDependencies": {
"@greenwood/cli": "~0.29.0",
"@greenwood/plugin-adapter-vercel": "~0.29.0",
"@greenwood/plugin-renderer-lit": "~0.29.0",
"patch-package": "^8.0.0",
"rimraf": "^5.0.0"
}
}
408 changes: 408 additions & 0 deletions patches/@greenwood+cli+0.29.0.patch

Large diffs are not rendered by default.

177 changes: 177 additions & 0 deletions patches/@greenwood+plugin-renderer-lit+0.29.0.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
diff --git a/node_modules/@greenwood/plugin-renderer-lit/src/execute-route-module.js b/node_modules/@greenwood/plugin-renderer-lit/src/execute-route-module.js
index 5e73f75..2c307fe 100644
--- a/node_modules/@greenwood/plugin-renderer-lit/src/execute-route-module.js
+++ b/node_modules/@greenwood/plugin-renderer-lit/src/execute-route-module.js
@@ -1,30 +1,16 @@
-// this needs to come first
-import { render } from '@lit-labs/ssr/lib/render-with-global-dom-shim.js';
-import { Buffer } from 'buffer';
+import { render } from '@lit-labs/ssr';
+import { collectResult } from '@lit-labs/ssr/lib/render-result.js'
import { html } from 'lit';
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
-import { Readable } from 'stream';
-
-async function streamToString (stream) {
- const chunks = [];
-
- for await (let chunk of stream) {
- chunks.push(Buffer.from(chunk));
- }
-
- return Buffer.concat(chunks).toString('utf-8');
-}
-
-async function getTemplateResultString(template) {
- return await streamToString(Readable.from(render(template)));
-}

async function executeRouteModule({ moduleUrl, compilation, page, prerender, htmlContents, scripts }) {
const data = {
template: null,
body: null,
frontmatter: null,
- html: null
+ html: null,
+ hydrate: false,
+ pageData: {},
};

// prerender static content
@@ -35,28 +21,36 @@ async function executeRouteModule({ moduleUrl, compilation, page, prerender, htm

const templateResult = html`${unsafeHTML(htmlContents)}`;

- data.html = await getTemplateResultString(templateResult);
+ data.html = await collectResult(render(templateResult));
} else {
const module = await import(moduleUrl).then(module => module);
- const { getTemplate = null, getBody = null, getFrontmatter = null } = module;
+ const { getTemplate = null, getBody = null, getFrontmatter = null, hydration = false, loader, isolation } = module;
+
+ // TODO cant we get these from just pulling from the file during the graph phase?
+ // e.g., hydration, prerender, isolation, etc
+ if (hydration) {
+ data.hydrate = true;
+ }

- if (module.default && module.tagName) {
- const { tagName } = module;
- const templateResult = html`
- ${unsafeHTML(`<${tagName}></${tagName}>`)}
- `;
+ if (isolation) {
+ data.isolation = true;
+ }
+
+ if (loader) {
+ data.pageData = await loader(); // request, compilation, etc can go here
+ // console.log(data.pageData);
+ }

- data.body = await getTemplateResultString(templateResult);
- } else if (getBody) {
- const templateResult = await getBody(compilation, page);
+ if (getBody) {
+ const templateResult = await getBody(compilation, page, data.pageData);

- data.body = await getTemplateResultString(templateResult);
+ data.body = await collectResult(render(templateResult));
}

if (getTemplate) {
const templateResult = await getTemplate(compilation, page);

- data.template = await getTemplateResultString(templateResult);
+ data.template = await collectResult(render(templateResult));
}

if (getFrontmatter) {
diff --git a/node_modules/@greenwood/plugin-renderer-lit/src/index.js b/node_modules/@greenwood/plugin-renderer-lit/src/index.js
index e4e190b..66c15a2 100755
--- a/node_modules/@greenwood/plugin-renderer-lit/src/index.js
+++ b/node_modules/@greenwood/plugin-renderer-lit/src/index.js
@@ -1,14 +1,80 @@
+
+// import { checkResourceExists } from '../../lib/resource-utils.js';
+// import fs from 'fs/promises';
+import { ResourceInterface } from '@greenwood/cli/src/lib/resource-interface.js';
+
+class LitHydrationResource extends ResourceInterface {
+ constructor(compilation, options) {
+ super(compilation, options);
+ // this.extensions = ['html'];
+ // this.contentType = 'text/html';
+ // this.libPath = '@greenwood/router/router.js';
+ }
+
+ // assumes Greenwood's standard-html plugin has tracked this metadata
+ // during resource serve lifecycle
+ async shouldIntercept(url, request, response) {
+ const { pathname } = url;
+ const matchingRoute = this.compilation.graph.find((node) => node.route === pathname) || {};
+ const { hydrate, pageData } = matchingRoute;
+
+ return hydrate && pageData;
+ }
+
+ async intercept(url, request, response) {
+ // console.log('SHOULD intercept', { url });
+ let body = await response.text();
+
+ // TODO would be nice not to have to do this, but
+ // this hydrate lib is not showing up in greenwood build / serve
+ const type = process.env.__GWD_COMMAND__ === 'develop'
+ ? 'module-shim'
+ : 'module'
+
+ // TODO have to manually set module-shim?
+ body = body.replace('<head>', `
+ <head>
+ <!-- this needs to come first before any userland code -->
+ <script type="${type}" src="/node_modules/@lit-labs/ssr-client/lit-element-hydrate-support.js"></script>
+ `);
+
+
+ // TODO full hydration implementation?
+ // <script type="module" defer>
+ // // https://lit.dev/docs/ssr/client-usage/
+ // import { render } from 'lit';
+ // import { hydrate } from '@lit-labs/ssr-client'; // this will need to be in users package.json and / or import map
+ // import { getBody } from '../src/pages/products.js';
+
+ // globalThis.document.addEventListener('DOMContentLoaded', () => {
+ // const hydrationData = JSON.parse(document.getElementById('__GWD_HYDRATION_DATA__')?.textContent || '{"__noData__": true}')
+ // console.log('lets get hydrated!', { hydrationData });
+
+ // if(!hydrationData.__noData__) {
+ // hydrate(getBody({}, {}, hydrationData), window.document.body);
+ // }
+ // });
+ // </script>
+
+ return new Response(body);
+ }
+}
+
const greenwoodPluginRendererLit = (options = {}) => {
- return {
+ return [{
type: 'renderer',
- name: 'plugin-renderer-lit',
+ name: 'plugin-renderer-lit:renderer',
provider: () => {
return {
executeModuleUrl: new URL('./execute-route-module.js', import.meta.url),
prerender: options.prerender
};
}
- };
+ }, {
+ type: 'resource',
+ name: 'plugin-renderer-lit:resource',
+ provider: (compilation, options) => new LitHydrationResource(compilation, options)
+ }];
};

export {
8 changes: 5 additions & 3 deletions src/api/fragment.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { render } from '@lit-labs/ssr/lib/render-with-global-dom-shim.js';
import { render } from '@lit-labs/ssr';
import { collectResult } from '@lit-labs/ssr/lib/render-result.js'
import { html } from 'lit';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { getProducts } from '../services/products.js';
import { renderFromHTML } from '../services/render-to-string.js';
import '../components/card.js';

export const isolation = true;

export async function handler(request) {
const params = new URLSearchParams(request.url.slice(request.url.indexOf('?')));
const limit = params.has('limit') ? parseInt(params.get('limit'), 10) : 5;
const offset = params.has('offset') ? parseInt(params.get('offset'), 10) : 0;
const products = (await getProducts()).slice(offset, offset + limit);
const body = await renderFromHTML(render(html`
const body = await collectResult(render(html`
${
unsafeHTML(products.map((item, idx) => {
const { title, thumbnail } = item;
Expand Down
8 changes: 5 additions & 3 deletions src/api/search.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { render } from '@lit-labs/ssr/lib/render-with-global-dom-shim.js';
import { render } from '@lit-labs/ssr';
import { collectResult } from '@lit-labs/ssr/lib/render-result.js'
import { html } from 'lit';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { getProducts } from '../services/products.js';
import { renderFromHTML } from '../services/render-to-string.js';
import '../components/card.js';

export const isolation = true;

export async function handler(request) {
const formData = await request.formData();
const term = formData.has('term') ? formData.get('term') : '';
Expand All @@ -17,7 +19,7 @@ export async function handler(request) {
if (products.length === 0) {
body = 'No results found.';
} else {
body = await renderFromHTML(render(html`
body = await collectResult(render(html`
${
unsafeHTML(products.map((item, idx) => {
const { title, thumbnail } = item;
Expand Down
1 change: 1 addition & 0 deletions src/components/card.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export class Card extends LitElement {

render() {
const { title, thumbnail } = this;
// console.log('render start', { title, thumbnail })
if(!title && !thumbnail) {
return;
}
Expand Down
69 changes: 69 additions & 0 deletions src/pages/products.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// import '@lit-labs/ssr/lib/install-global-dom-shim.js';
// import { html } from '@lit-labs/ssr';
import { LitElement, html } from 'lit';
import '../components/card.js';
import { getProducts } from '../services/products.js';

export const hydration = true;
export const isolation = true;

export async function loader(request = null) {
const products = await getProducts();

return { products };
}

export function getBody(compilation, page, data) {
const { products } = data;

return html`
${
products.map((product, idx) => {
const { title, thumbnail } = product;
return html`
<app-card
title="${idx + 1}) ${title}"
thumbnail="${thumbnail}"
></app-card>
`;
})
}
`;
}

// export default class ProductsPage extends LitElement {

// constructor() {
// super();
// this.products = [{ title: 'TODO', thumbnail: 'https://www.greenwoodjs.io/assets/greenwood-logo-og.png' }];
// }

// // async connectedCallback() {
// // super.connectedCallback();

// // this.products = await getProducts();
// // }

// render() {
// const { products } = this;
// console.log('RENDER', products);

// return html`
// ${
// products.map((product, idx) => {
// const { title, thumbnail } = product;
// return html`
// <app-card
// title="${idx + 1}) ${title}"
// thumbnail="${thumbnail}"
// ></app-card>
// `;
// })
// }
// `;
// }
// }

// // for now these are needed for the Lit specific implementations
// customElements.define('products-page', ProductsPage);
// export const tagName = 'products-page';
13 changes: 0 additions & 13 deletions src/services/render-to-string.js

This file was deleted.

6 changes: 4 additions & 2 deletions src/templates/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="/favicon.ico"/>
<link rel="icon" href="/favicon.ico"/>

<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro">
<link rel="stylesheet" href="../styles/main.css">
<script type="module" src="../components/card.js"></script>
Expand All @@ -30,7 +29,10 @@ <h1>Demos</h1>
<a href="https://github.com/ProjectEvergreen/greenwood-demo-adapter-vercel" title="Link to demonstration repo">Greenwood demonstration repo</a>
showcasing various features deployed to and running on Vercel serverless functions.
</p>
<page-outlet></page-outlet>

<section class="products-cards-container">
<page-outlet></page-outlet>
</section>
</main>

<footer>
Expand Down