Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ erl_crash.dump
examples/*/*/.github
examples/*/*/manifest.toml
examples/*/*/priv/static

node_modules/
13 changes: 4 additions & 9 deletions src/lustre/internals/constants.ffi.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,13 @@ import Dict from "../../../gleam_stdlib/dict.mjs";
import { new$ as set_new } from "../../../gleam_stdlib/gleam/set.mjs";

const EMPTY_DICT = /* @__PURE__ */ Dict.new();

export function empty_dict() {
return EMPTY_DICT;
}

const EMPTY_SET = /* @__PURE__*/ set_new();

export function empty_set() {
return EMPTY_SET;
}
export const empty_dict = () => EMPTY_DICT;
export const empty_set = () => EMPTY_SET;

export const document = () => globalThis?.document;

export const document = /* @__PURE__ */ globalThis?.document;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a function now?

Copy link
Contributor Author

@yoshi-monster yoshi-monster May 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

previously this value was captured when the module graph was loaded - which is before any (non-top-level) code has had a chance to execute.

This means that in the test environment, where by default we do not have a global window object, our own document was always undefined. We also want to reset the document in between tests, so that's another reason why we can't have a stable reference here.

Initially I pulled out this constant as a server runtime size optimisation, since it avoids repeating the string document all over the bundle

export const NAMESPACE_HTML = "http://www.w3.org/1999/xhtml";
export const ELEMENT_NODE = 1;
export const TEXT_NODE = 3;
Expand Down
6 changes: 3 additions & 3 deletions src/lustre/runtime/client/runtime.ffi.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { document } from "../../internals/constants.ffi.mjs";

//

export const is_browser = () => !!document;
export const is_browser = () => !!document();

export const is_registered = (name) => is_browser() && customElements.get(name);

Expand Down Expand Up @@ -245,7 +245,7 @@ const copiedStyleSheets = new WeakMap();

export async function adoptStylesheets(shadowRoot) {
const pendingParentStylesheets = [];
for (const node of document.querySelectorAll("link[rel=stylesheet], style")) {
for (const node of document().querySelectorAll("link[rel=stylesheet], style")) {
if (node.sheet) continue;

pendingParentStylesheets.push(
Expand All @@ -268,7 +268,7 @@ export async function adoptStylesheets(shadowRoot) {

const pending = [];

for (const sheet of document.styleSheets) {
for (const sheet of document().styleSheets) {
try {
shadowRoot.adoptedStyleSheets.push(sheet);
} catch {
Expand Down
19 changes: 14 additions & 5 deletions src/lustre/vdom/reconciler.ffi.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,16 @@ export class Reconciler {
#dispatch = () => {};

#useServerEvents = false;
#exposeKeys = false;

constructor(root, dispatch, { useServerEvents = false } = {}) {
constructor(root, dispatch, {
useServerEvents = false,
exposeKeys = false
} = {}) {
this.#root = root;
this.#dispatch = dispatch;
this.#useServerEvents = useServerEvents;
this.#exposeKeys = exposeKeys;
}

mount(vdom) {
Expand Down Expand Up @@ -290,7 +295,11 @@ export class Reconciler {
}
}

#createAttributes(node, { attributes }) {
#createAttributes(node, { key, attributes }) {
if (this.#exposeKeys && key) {
node.setAttribute('data-lustre-key', key)
}

iterate(attributes, (attribute) => this.#createAttribute(node, attribute));
}

Expand Down Expand Up @@ -459,18 +468,18 @@ const insertBefore = (parent, node, referenceNode) =>
parent.insertBefore(node, referenceNode ?? null);

const createChildElement = (parent, { key, tag, namespace }) => {
const node = document.createElementNS(namespace || NAMESPACE_HTML, tag);
const node = document().createElementNS(namespace || NAMESPACE_HTML, tag);
initialiseMetadata(parent, node, key);
return node;
};

const createChildText = (parent, { key, content }) => {
const node = document.createTextNode(content ?? "");
const node = document().createTextNode(content ?? "");
initialiseMetadata(parent, node, key);
return node;
};

const createDocumentFragment = () => document.createDocumentFragment();
const createDocumentFragment = () => document().createDocumentFragment();
const childAt = (node, at) => node.childNodes[at | 0];

// METADATA --------------------------------------------------------------------
Expand Down
6 changes: 3 additions & 3 deletions src/lustre/vdom/virtualise.ffi.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const virtualise = (root) => {
}

const emptyTextNode = (parent) => {
const node = document.createTextNode("");
const node = document().createTextNode("");
initialiseMetadata(parent, node);
return node;
}
Expand Down Expand Up @@ -70,7 +70,7 @@ const virtualiseNode = (parent, node) => {

case TEXT_NODE:
initialiseMetadata(parent, node);
return text(node.data);
return node.data ? text(node.data) : null;

case DOCUMENT_FRAGMENT_NODE: // shadowRoot
initialiseMetadata(parent, node);
Expand Down Expand Up @@ -114,7 +114,7 @@ const virtualiseInputEvents = (tag, node) => {
// User apps may be using semi-controlled inputs where they listen to blur
// events to save the value rather than using the input event. To account for
// those, we dispatch a blur event if the input is not currently focused.
if (document.activeElement !== node) {
if (document().activeElement !== node) {
node.dispatchEvent(new Event("blur", { bubbles: true }));
}
});
Expand Down
Loading