Skip to content
Open
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
31 changes: 22 additions & 9 deletions src/_hyperscript.js
Original file line number Diff line number Diff line change
Expand Up @@ -7628,31 +7628,44 @@
function browserInit() {
/** @type {HTMLScriptElement[]} */
var scripts = Array.from(globalScope.document.querySelectorAll("script[type='text/hyperscript'][src]"))
Promise.all(

// Fetch external .hs files immediately so network latency overlaps
// with DOMContentLoaded. Parsing is deferred until after ready() so
// that plugins loaded via synchronous <script> tags (e.g. hdb.js)
// have a chance to register their commands before behavior files
// are evaluated. See https://github.com/bigskysoftware/_hyperscript/issues/533
var fetchedScripts = Promise.all(
scripts.map(function (script) {
return fetch(script.src)
.then(function (res) {
return res.text();
});
})
)
.then(script_values => script_values.forEach(sc => _hyperscript(sc)))
.then(() => ready(function () {
mergeMetaConfig();
runtime_.processNode(document.documentElement);
);

document.dispatchEvent(new Event("hyperscript:ready"));
ready(function () {
mergeMetaConfig();

// Register htmx:load listener immediately so dynamic swaps that
// occur while external .hs files are still being fetched are not missed.
globalScope.document.addEventListener("htmx:load", function (/** @type {CustomEvent} */ evt) {
runtime_.processNode(evt.detail.elt);
});
}));

fetchedScripts.then(function (script_values) {
script_values.forEach(function (sc) { _hyperscript(sc); });

runtime_.processNode(document.documentElement);

document.dispatchEvent(new Event("hyperscript:ready"));
});
});

function ready(fn) {
if (document.readyState !== "loading") {
setTimeout(fn);
} else {
document.addEventListener("DOMContentLoaded", fn);
document.addEventListener("DOMContentLoaded", fn, { once: true });
}
}

Expand Down
43 changes: 43 additions & 0 deletions test/core/pluginLoading.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
describe("plugin loading order", function () {

it("a command registered via addCommand is available to subsequently parsed scripts", function () {
var executed = false;

_hyperscript.addCommand("__testplugincmd", function (parser, runtime, tokens) {
if (!tokens.matchToken("__testplugincmd")) return;
return {
op: function (ctx) {
executed = true;
return runtime.findNext(this, ctx);
}
};
});

// Parse and execute a script that uses the plugin command directly.
_hyperscript("__testplugincmd");
executed.should.equal(true);
});

it("a feature registered via addFeature is available to subsequently parsed scripts", function () {
var installed = false;

_hyperscript.addFeature("__testpluginfeature", function (parser, runtime, tokens) {
if (!tokens.matchToken("__testpluginfeature")) return;
var name = tokens.requireTokenType("IDENTIFIER").value;
return {
install: function (target, source) {
installed = true;
}
};
});

_hyperscript("__testpluginfeature myFeature");
installed.should.equal(true);
});

it("parsing a script with an unregistered command throws a parse error", function () {
var error = getParseErrorFor("on click __nonexistentcmd");
error.should.not.equal("");
});

});
1 change: 1 addition & 0 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ <h2>Mocha Test Suite</h2>
<script src="core/runtimeErrors.js"></script>
<script src="core/security.js"></script>
<script src="core/scoping.js"></script>
<script src="core/pluginLoading.js"></script>

<!-- features -->
<script src="features/on.js"></script>
Expand Down