diff options
author | 2022-03-18 15:35:45 -0700 | |
---|---|---|
committer | 2022-03-18 15:35:45 -0700 | |
commit | 6386c14d00d1d820804f0ee5b1424e73c049fe83 (patch) | |
tree | 3015e834e1d84100fd0871f6a55479bed61c0c14 /packages/integrations/lit | |
parent | 0f376a7c52d3a22ff32b33e0afc34dd306ed70c4 (diff) | |
download | astro-6386c14d00d1d820804f0ee5b1424e73c049fe83.tar.gz astro-6386c14d00d1d820804f0ee5b1424e73c049fe83.tar.zst astro-6386c14d00d1d820804f0ee5b1424e73c049fe83.zip |
Astro Integration System (#2820)
* update examples
* add initial integrations
* update tests
* update astro
* update ci
* get final tests working
* update injectelement todo
* update ben code review
* respond to final code review feedback
Diffstat (limited to 'packages/integrations/lit')
-rw-r--r-- | packages/integrations/lit/.gitignore | 1 | ||||
-rw-r--r-- | packages/integrations/lit/client-shim.js | 10 | ||||
-rw-r--r-- | packages/integrations/lit/client-shim.min.js | 79 | ||||
-rw-r--r-- | packages/integrations/lit/hydration-support.js | 1 | ||||
-rw-r--r-- | packages/integrations/lit/package.json | 38 | ||||
-rw-r--r-- | packages/integrations/lit/server-shim.js | 5 | ||||
-rw-r--r-- | packages/integrations/lit/server.js | 72 | ||||
-rw-r--r-- | packages/integrations/lit/src/index.ts | 42 | ||||
-rw-r--r-- | packages/integrations/lit/tsconfig.json | 10 |
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" + } +} |