summaryrefslogtreecommitdiff
path: root/packages/integrations/lit
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/lit')
-rw-r--r--packages/integrations/lit/.gitignore1
-rw-r--r--packages/integrations/lit/client-shim.js10
-rw-r--r--packages/integrations/lit/client-shim.min.js79
-rw-r--r--packages/integrations/lit/hydration-support.js1
-rw-r--r--packages/integrations/lit/package.json38
-rw-r--r--packages/integrations/lit/server-shim.js5
-rw-r--r--packages/integrations/lit/server.js72
-rw-r--r--packages/integrations/lit/src/index.ts42
-rw-r--r--packages/integrations/lit/tsconfig.json10
9 files changed, 258 insertions, 0 deletions
diff --git a/packages/integrations/lit/.gitignore b/packages/integrations/lit/.gitignore
new file mode 100644
index 000000000..40b878db5
--- /dev/null
+++ b/packages/integrations/lit/.gitignore
@@ -0,0 +1 @@
+node_modules/ \ No newline at end of file
diff --git a/packages/integrations/lit/client-shim.js b/packages/integrations/lit/client-shim.js
new file mode 100644
index 000000000..cab3fe4d9
--- /dev/null
+++ b/packages/integrations/lit/client-shim.js
@@ -0,0 +1,10 @@
+async function polyfill() {
+ const { hydrateShadowRoots } = await import('@webcomponents/template-shadowroot/template-shadowroot.js');
+ hydrateShadowRoots(document.body);
+}
+
+const polyfillCheckEl = new DOMParser().parseFromString(`<p><template shadowroot="open"></template></p>`, 'text/html', { includeShadowRoots: true }).querySelector('p');
+
+if (!polyfillCheckEl || !polyfillCheckEl.shadowRoot) {
+ polyfill();
+}
diff --git a/packages/integrations/lit/client-shim.min.js b/packages/integrations/lit/client-shim.min.js
new file mode 100644
index 000000000..0c6a452d8
--- /dev/null
+++ b/packages/integrations/lit/client-shim.min.js
@@ -0,0 +1,79 @@
+/** @license Copyright 2020 Google LLC (BSD-3-Clause) */
+/** Bundled JS generated from "@astrojs/lit/client-shim.js" */
+var N = Object.defineProperty;
+var i = (t, n) => () => (t && (n = t((t = 0))), n);
+var b = (t, n) => {
+ for (var a in n) N(t, a, { get: n[a], enumerable: !0 });
+};
+function s() {
+ if (d === void 0) {
+ let t = document.createElement('div');
+ (t.innerHTML = '<div><template shadowroot="open"></template></div>'), (d = !!t.firstElementChild.shadowRoot);
+ }
+ return d;
+}
+var d,
+ m = i(() => {});
+var p,
+ c,
+ f,
+ u = i(() => {
+ (p = (t) => t.parentElement === null), (c = (t) => t.tagName === 'TEMPLATE'), (f = (t) => t.nodeType === Node.ELEMENT_NODE);
+ });
+var h,
+ E = i(() => {
+ m();
+ u();
+ h = (t) => {
+ var n;
+ if (s()) return;
+ let a = [],
+ e = t.firstElementChild;
+ for (; e !== t && e !== null; )
+ if (c(e)) a.push(e), (e = e.content);
+ else if (e.firstElementChild !== null) e = e.firstElementChild;
+ else if (f(e) && e.nextElementSibling !== null) e = e.nextElementSibling;
+ else {
+ let o;
+ for (; e !== t && e !== null; )
+ if (p(e)) {
+ o = a.pop();
+ let r = o.parentElement,
+ l = o.getAttribute('shadowroot');
+ if (((e = o), l === 'open' || l === 'closed')) {
+ let y = o.hasAttribute('shadowrootdelegatesfocus');
+ try {
+ r.attachShadow({ mode: l, delegatesFocus: y }).append(o.content);
+ } catch {}
+ } else o = void 0;
+ } else {
+ let r = e.nextElementSibling;
+ if (r != null) {
+ (e = r), o !== void 0 && o.parentElement.removeChild(o);
+ break;
+ }
+ let l = (n = e.parentElement) === null || n === void 0 ? void 0 : n.nextElementSibling;
+ if (l != null) {
+ (e = l), o !== void 0 && o.parentElement.removeChild(o);
+ break;
+ }
+ (e = e.parentElement), o !== void 0 && (o.parentElement.removeChild(o), (o = void 0));
+ }
+ }
+ };
+ });
+var w = i(() => {
+ E();
+});
+var v = {};
+b(v, { hasNativeDeclarativeShadowRoots: () => s, hydrateShadowRoots: () => h });
+var S = i(() => {
+ m();
+ w();
+});
+async function g() {
+ let { hydrateShadowRoots: t } = await Promise.resolve().then(() => (S(), v));
+ t(document.body);
+}
+var x = new DOMParser().parseFromString('<p><template shadowroot="open"></template></p>', 'text/html', { includeShadowRoots: !0 }).querySelector('p');
+(!x || !x.shadowRoot) && g();
diff --git a/packages/integrations/lit/hydration-support.js b/packages/integrations/lit/hydration-support.js
new file mode 100644
index 000000000..0c21646fb
--- /dev/null
+++ b/packages/integrations/lit/hydration-support.js
@@ -0,0 +1 @@
+import 'lit/experimental-hydrate-support.js';
diff --git a/packages/integrations/lit/package.json b/packages/integrations/lit/package.json
new file mode 100644
index 000000000..2e76308f1
--- /dev/null
+++ b/packages/integrations/lit/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "@astrojs/lit",
+ "version": "0.0.1",
+ "description": "Use Lit components within Astro",
+ "type": "module",
+ "types": "./dist/index.d.ts",
+ "author": "withastro",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/withastro/astro.git",
+ "directory": "packages/integrations/lit"
+ },
+ "bugs": "https://github.com/withastro/astro/issues",
+ "homepage": "https://astro.build",
+ "exports": {
+ ".": "./dist/index.js",
+ "./server.js": "./server.js",
+ "./client-shim.js": "./client-shim.js",
+ "./hydration-support.js": "./hydration-support.js",
+ "./package.json": "./package.json"
+ },
+ "scripts": {
+ "build": "astro-scripts build \"src/**/*.ts\" && tsc",
+ "dev": "astro-scripts dev \"src/**/*.ts\""
+ },
+ "dependencies": {
+ "@lit-labs/ssr": "^2.0.2"
+ },
+ "devDependencies": {
+ "astro": "workspace:*",
+ "astro-scripts": "workspace:*"
+ },
+ "peerDependencies": {
+ "@webcomponents/template-shadowroot": "^0.1.0",
+ "lit": "^2.1.3"
+ }
+}
diff --git a/packages/integrations/lit/server-shim.js b/packages/integrations/lit/server-shim.js
new file mode 100644
index 000000000..054679592
--- /dev/null
+++ b/packages/integrations/lit/server-shim.js
@@ -0,0 +1,5 @@
+import { installWindowOnGlobal } from '@lit-labs/ssr/lib/dom-shim.js';
+installWindowOnGlobal();
+
+window.global = window;
+document.getElementsByTagName = () => [];
diff --git a/packages/integrations/lit/server.js b/packages/integrations/lit/server.js
new file mode 100644
index 000000000..1622ef619
--- /dev/null
+++ b/packages/integrations/lit/server.js
@@ -0,0 +1,72 @@
+import './server-shim.js';
+import '@lit-labs/ssr/lib/render-lit-html.js';
+import { LitElementRenderer } from '@lit-labs/ssr/lib/lit-element-renderer.js';
+
+function isCustomElementTag(name) {
+ return typeof name === 'string' && /-/.test(name);
+}
+
+function getCustomElementConstructor(name) {
+ if (typeof customElements !== 'undefined' && isCustomElementTag(name)) {
+ return customElements.get(name) || null;
+ }
+ return null;
+}
+
+async function isLitElement(Component) {
+ const Ctr = getCustomElementConstructor(Component);
+ return !!(Ctr && Ctr._$litElement$);
+}
+
+async function check(Component, _props, _children) {
+ // Lit doesn't support getting a tagName from a Constructor at this time.
+ // So this must be a string at the moment.
+ return !!(await isLitElement(Component));
+}
+
+function* render(tagName, attrs, children) {
+ const instance = new LitElementRenderer(tagName);
+
+ // LitElementRenderer creates a new element instance, so copy over.
+ const Ctr = getCustomElementConstructor(tagName);
+ for (let [name, value] of Object.entries(attrs)) {
+ // check if this is a reactive property
+ if (name in Ctr.prototype) {
+ instance.setProperty(name, value);
+ } else {
+ instance.setAttribute(name, value);
+ }
+ }
+
+ instance.connectedCallback();
+
+ yield `<${tagName}`;
+ yield* instance.renderAttributes();
+ yield `>`;
+ const shadowContents = instance.renderShadow({});
+ if (shadowContents !== undefined) {
+ yield '<template shadowroot="open">';
+ yield* shadowContents;
+ yield '</template>';
+ }
+ yield children || ''; // don’t print “undefined” as string
+ yield `</${tagName}>`;
+}
+
+async function renderToStaticMarkup(Component, props, children) {
+ let tagName = Component;
+
+ let out = '';
+ for (let chunk of render(tagName, props, children)) {
+ out += chunk;
+ }
+
+ return {
+ html: out,
+ };
+}
+
+export default {
+ check,
+ renderToStaticMarkup,
+};
diff --git a/packages/integrations/lit/src/index.ts b/packages/integrations/lit/src/index.ts
new file mode 100644
index 000000000..bf256eb84
--- /dev/null
+++ b/packages/integrations/lit/src/index.ts
@@ -0,0 +1,42 @@
+import { readFileSync } from 'node:fs';
+import type { AstroIntegration } from 'astro';
+
+function getViteConfiguration() {
+ return {
+ optimizeDeps: {
+ include: [
+ '@astrojs/lit/client-shim.js',
+ '@astrojs/lit/hydration-support.js',
+ '@webcomponents/template-shadowroot/template-shadowroot.js',
+ 'lit/experimental-hydrate-support.js',
+ ],
+ exclude: ['@astrojs/lit/server.js'],
+ },
+ ssr: {
+ external: ['lit-element/lit-element.js', '@lit-labs/ssr/lib/install-global-dom-shim.js', '@lit-labs/ssr/lib/render-lit-html.js', '@lit-labs/ssr/lib/lit-element-renderer.js'],
+ },
+ };
+}
+
+export default function (): AstroIntegration {
+ return {
+ name: '@astrojs/lit',
+ hooks: {
+ 'astro:config:setup': ({ updateConfig, addRenderer, injectScript }) => {
+ // Inject the necessary polyfills on every page (inlined for speed).
+ injectScript('head-inline', readFileSync(new URL('../client-shim.min.js', import.meta.url), { encoding: 'utf-8' }));
+ // Inject the hydration code, before a component is hydrated.
+ injectScript('before-hydration', `import '@astrojs/lit/hydration-support.js';`);
+ // Add the lit renderer so that Astro can understand lit components.
+ addRenderer({
+ name: '@astrojs/lit',
+ serverEntrypoint: '@astrojs/lit/server.js',
+ });
+ // Update the vite configuration.
+ updateConfig({
+ vite: getViteConfiguration(),
+ });
+ },
+ },
+ };
+}
diff --git a/packages/integrations/lit/tsconfig.json b/packages/integrations/lit/tsconfig.json
new file mode 100644
index 000000000..44baf375c
--- /dev/null
+++ b/packages/integrations/lit/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "../../../tsconfig.base.json",
+ "include": ["src"],
+ "compilerOptions": {
+ "allowJs": true,
+ "module": "ES2020",
+ "outDir": "./dist",
+ "target": "ES2020"
+ }
+}