Skip to content

Commit

Permalink
[core/ssr] Add Node builds that don't explode (#3156)
Browse files Browse the repository at this point in the history
### Background

Before this PR, importing Lit with Node causes errors such as `window is not defined`. The only way to load Lit was to first ensure that the `dom-shim.js` module provided by the `@lit-labs/ssr` package was loaded, which defines `window` plus APIs like `HTMLElement` and `customElements`.

This meant that using Lit in frameworks such as Next did not work out-of-the-box _even when only client-side rendering is needed_.

### This change

After this PR, Lit can be imported from Node without errors, and custom elements can be defined as no-ops.

This doesn't remove the need for loading the DOM shim shim when SSR is actually needed, but it does give us compatibility at least for client-side rendering in frameworks like Next, without the user needing to do anything special.

This works by adding a Node-specific Rollup build and a `node` export condition to `lit-html` and `@lit/reactive-element`. Neither `lit-element` nor `lit` need a Node build, because only the underlying 2 libraries currently need Node-specific behavior.

This only defines `customElements` as new globals. It does not define `window`, `HTMLElement`, or any other APIs. This way, we won't affect the behavior of libraries that detect whether they are in Node vs the browser by checking for a `window` global.

For testing, I've added a `node-imports.ts` file to each package, and added a new `node:test` script which executes that module with Node. This confirms that Node doesn't crash on import.

Fixes #3079

### Size

This actually slightly drops the size of the main `lit-html.js` and `reactive-element.js` production build files:

| File                | Old size (raw/gzip/brotli)      | New size (raw/gzip/brotli) |
| ------------------- | -------------- | -------------- |
| lit-html.js         | 8.68 / 3.45 / 3.13 | 8.02 / 3.38 / 3.07 |
| reactive-element.js | 5.97 / 2.17 / 1.93 | 5.95 / 2.17 / 1.92 |
  • Loading branch information
aomarks committed Aug 4, 2022
1 parent 325db3c commit ae6f680
Show file tree
Hide file tree
Showing 22 changed files with 390 additions and 42 deletions.
7 changes: 7 additions & 0 deletions .changeset/tall-mirrors-notice.md
@@ -0,0 +1,7 @@
---
'lit': minor
'lit-html': minor
'@lit/reactive-element': minor
---

Lit and its underlying libraries can now be imported directly from Node without crashing, without the need to load the @lit-labs/ssr dom-shim library. Note that actually rendering from a Node context still requires the @lit-labs/ssr dom-shim, and the appropriate integration between @lit-labs/ssr and your framework/tool.
2 changes: 2 additions & 0 deletions .eslintignore
Expand Up @@ -62,6 +62,7 @@ packages/lit-element/polyfill-support.*
packages/lit-element/private-ssr-support.*

packages/lit-html/development/
packages/lit-html/node/
packages/lit-html/version-stability-build/
packages/lit-html/directives/
packages/lit-html/node_modules/
Expand Down Expand Up @@ -118,6 +119,7 @@ packages/localize-tools/testdata/*/output/

packages/reactive-element/decorators/
packages/reactive-element/development/
packages/reactive-element/node/
packages/reactive-element/test/
packages/reactive-element/css-tag.*
packages/reactive-element/decorators.*
Expand Down
2 changes: 2 additions & 0 deletions .prettierignore
Expand Up @@ -62,6 +62,7 @@ packages/lit-element/polyfill-support.*
packages/lit-element/private-ssr-support.*

packages/lit-html/development/
packages/lit-html/node/
packages/lit-html/version-stability-build/
packages/lit-html/directives/
packages/lit-html/node_modules/
Expand Down Expand Up @@ -104,6 +105,7 @@ packages/localize-tools/testdata/*/output/

packages/reactive-element/decorators/
packages/reactive-element/development/
packages/reactive-element/node/
packages/reactive-element/test/
packages/reactive-element/css-tag.*
packages/reactive-element/decorators.*
Expand Down
13 changes: 12 additions & 1 deletion packages/lit-element/package.json
Expand Up @@ -104,7 +104,8 @@
"prepublishOnly": "npm run check-version",
"test": "wireit",
"test:dev": "wireit",
"test:prod": "wireit"
"test:prod": "wireit",
"test:node": "wireit"
},
"wireit": {
"build": {
Expand Down Expand Up @@ -198,6 +199,7 @@
"dependencies": [
"test:dev",
"test:prod",
"test:node",
"check-version"
]
},
Expand All @@ -219,6 +221,15 @@
],
"files": [],
"output": []
},
"test:node": {
"command": "node development/test/node-imports.js",
"dependencies": [
"build:ts",
"build:rollup"
],
"files": [],
"output": []
}
},
"files": [
Expand Down
39 changes: 39 additions & 0 deletions packages/lit-element/src/test/node-imports.ts
@@ -0,0 +1,39 @@
/**
* @license
* Copyright 2022 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/

// This file will be loaded by Node from the node:test script to verify that all
// exports of this package can be imported without crashing in Node.

import 'lit-element';
import 'lit-element/experimental-hydrate-support.js';
import 'lit-element/private-ssr-support.js';
import 'lit-element/decorators.js';
import 'lit-element/decorators/custom-element.js';
import 'lit-element/decorators/event-options.js';
import 'lit-element/decorators/state.js';
import 'lit-element/decorators/property.js';
import 'lit-element/decorators/query.js';
import 'lit-element/decorators/query-all.js';
import 'lit-element/decorators/query-assigned-elements.js';
import 'lit-element/decorators/query-assigned-nodes.js';
import 'lit-element/decorators/query-async.js';

import {LitElement, html} from 'lit-element';
import {customElement} from 'lit-element/decorators.js';

@customElement('my-element')
export class MyElement extends LitElement {
override render() {
return html`Hello World`;
}
}

export class MyOtherElement extends LitElement {
override render() {
return html`Hello World`;
}
}
customElements.define('my-other-element', MyOtherElement);
1 change: 1 addition & 0 deletions packages/lit-html/.gitignore
@@ -1,4 +1,5 @@
/development/
/node/
/version-stability-build/
/directives/
/node_modules/
Expand Down
47 changes: 44 additions & 3 deletions packages/lit-html/package.json
Expand Up @@ -15,141 +15,169 @@
"exports": {
".": {
"types": "./development/lit-html.d.ts",
"node": "./node/lit-html.js",
"development": "./development/lit-html.js",
"default": "./lit-html.js"
},
"./async-directive.js": {
"types": "./development/async-directive.d.ts",
"node": "./node/async-directive.js",
"development": "./development/async-directive.js",
"default": "./async-directive.js"
},
"./directive-helpers.js": {
"types": "./development/directive-helpers.d.ts",
"node": "./node/directive-helpers.js",
"development": "./development/directive-helpers.js",
"default": "./directive-helpers.js"
},
"./directive.js": {
"types": "./development/directive.d.ts",
"node": "./node/directive.js",
"development": "./development/directive.js",
"default": "./directive.js"
},
"./directives/async-append.js": {
"types": "./development/directives/async-append.d.ts",
"node": "./node/directives/async-append.js",
"development": "./development/directives/async-append.js",
"default": "./directives/async-append.js"
},
"./directives/async-replace.js": {
"types": "./development/directives/async-replace.d.ts",
"node": "./node/directives/async-replace.js",
"development": "./development/directives/async-replace.js",
"default": "./directives/async-replace.js"
},
"./directives/cache.js": {
"types": "./development/directives/cache.d.ts",
"node": "./node/directives/cache.js",
"development": "./development/directives/cache.js",
"default": "./directives/cache.js"
},
"./directives/choose.js": {
"types": "./development/directives/choose.d.ts",
"node": "./node/directives/choose.js",
"development": "./development/directives/choose.js",
"default": "./directives/choose.js"
},
"./directives/class-map.js": {
"types": "./development/directives/class-map.d.ts",
"node": "./node/directives/class-map.js",
"development": "./development/directives/class-map.js",
"default": "./directives/class-map.js"
},
"./directives/guard.js": {
"types": "./development/directives/guard.d.ts",
"node": "./node/directives/guard.js",
"development": "./development/directives/guard.js",
"default": "./directives/guard.js"
},
"./directives/if-defined.js": {
"types": "./development/directives/if-defined.d.ts",
"node": "./node/directives/if-defined.js",
"development": "./development/directives/if-defined.js",
"default": "./directives/if-defined.js"
},
"./directives/join.js": {
"types": "./development/directives/join.d.ts",
"node": "./node/directives/join.js",
"development": "./development/directives/join.js",
"default": "./directives/join.js"
},
"./directives/keyed.js": {
"types": "./development/directives/keyed.d.ts",
"node": "./node/directives/keyed.js",
"development": "./development/directives/keyed.js",
"default": "./directives/keyed.js"
},
"./directives/live.js": {
"types": "./development/directives/live.d.ts",
"node": "./node/directives/live.js",
"development": "./development/directives/live.js",
"default": "./directives/live.js"
},
"./directives/map.js": {
"types": "./development/directives/map.d.ts",
"node": "./node/directives/map.js",
"development": "./development/directives/map.js",
"default": "./directives/map.js"
},
"./directives/range.js": {
"types": "./development/directives/range.d.ts",
"node": "./node/directives/range.js",
"development": "./development/directives/range.js",
"default": "./directives/range.js"
},
"./directives/ref.js": {
"types": "./development/directives/ref.d.ts",
"node": "./node/directives/ref.js",
"development": "./development/directives/ref.js",
"default": "./directives/ref.js"
},
"./directives/repeat.js": {
"types": "./development/directives/repeat.d.ts",
"node": "./node/directives/repeat.js",
"development": "./development/directives/repeat.js",
"default": "./directives/repeat.js"
},
"./directives/style-map.js": {
"types": "./development/directives/style-map.d.ts",
"node": "./node/directives/style-map.js",
"development": "./development/directives/style-map.js",
"default": "./directives/style-map.js"
},
"./directives/template-content.js": {
"types": "./development/directives/template-content.d.ts",
"node": "./node/directives/template-content.js",
"development": "./development/directives/template-content.js",
"default": "./directives/template-content.js"
},
"./directives/unsafe-html.js": {
"types": "./development/directives/unsafe-html.d.ts",
"node": "./node/directives/unsafe-html.js",
"development": "./development/directives/unsafe-html.js",
"default": "./directives/unsafe-html.js"
},
"./directives/unsafe-svg.js": {
"types": "./development/directives/unsafe-svg.d.ts",
"node": "./node/directives/unsafe-svg.js",
"development": "./development/directives/unsafe-svg.js",
"default": "./directives/unsafe-svg.js"
},
"./directives/until.js": {
"types": "./development/directives/until.d.ts",
"node": "./node/directives/until.js",
"development": "./development/directives/until.js",
"default": "./directives/until.js"
},
"./directives/when.js": {
"types": "./development/directives/when.d.ts",
"node": "./node/directives/when.js",
"development": "./development/directives/when.js",
"default": "./directives/when.js"
},
"./experimental-hydrate.js": {
"types": "./development/experimental-hydrate.d.ts",
"node": "./node/experimental-hydrate.js",
"development": "./development/experimental-hydrate.js",
"default": "./experimental-hydrate.js"
},
"./polyfill-support.js": {
"types": "./development/polyfill-support.d.ts",
"node": "./node/polyfill-support.js",
"development": "./development/polyfill-support.js",
"default": "./polyfill-support.js"
},
"./private-ssr-support.js": {
"types": "./development/private-ssr-support.d.ts",
"node": "./node/private-ssr-support.js",
"development": "./development/private-ssr-support.js",
"default": "./private-ssr-support.js"
},
"./static.js": {
"types": "./development/static.d.ts",
"node": "./node/static.js",
"development": "./development/static.js",
"default": "./static.js"
}
Expand All @@ -165,7 +193,8 @@
"prepublishOnly": "npm run check-version",
"test": "wireit",
"test:dev": "wireit",
"test:prod": "wireit"
"test:prod": "wireit",
"test:node": "wireit"
},
"files": [
"/async-directive.{d.ts,d.ts.map,js,js.map}",
Expand All @@ -178,7 +207,8 @@
"/static.{d.ts,d.ts.map,js,js.map}",
"/development/",
"!/development/test/",
"/directives/"
"/directives/",
"/node/"
],
"wireit": {
"build": {
Expand Down Expand Up @@ -240,7 +270,8 @@
"test/*_test.html",
"development/test/*_test.html",
"test/polyfill-support/*_test.html",
"development/test/polyfill-support/*_test.html"
"development/test/polyfill-support/*_test.html",
"node/"
]
},
"build:version-stability-test": {
Expand Down Expand Up @@ -281,6 +312,7 @@
"dependencies": [
"test:dev",
"test:prod",
"test:node",
"check-version"
]
},
Expand All @@ -303,6 +335,15 @@
],
"files": [],
"output": []
},
"test:node": {
"command": "node development/test/node-imports.js",
"dependencies": [
"build:ts",
"build:rollup"
],
"files": [],
"output": []
}
},
"dependencies": {
Expand Down
1 change: 1 addition & 0 deletions packages/lit-html/rollup.config.js
Expand Up @@ -45,6 +45,7 @@ export const defaultConfig = (options = {}) =>
file: 'polyfill-support',
},
],
includeNodeBuild: true,
...options,
});

Expand Down

0 comments on commit ae6f680

Please sign in to comment.