diff --git a/src/snippet.js b/src/snippet.js index b285dab..a7d550e 100644 --- a/src/snippet.js +++ b/src/snippet.js @@ -315,24 +315,32 @@ class CodapiSnippet extends HTMLElement { // and builds the complete code from the first snippet // to the last (which is the current one). function gatherCode(curSnip) { - let code = curSnip.code; - let ids = curSnip.dependsOn ? curSnip.dependsOn.split(" ") : []; - // print separators between snippets to tail output later - const sep = curSnip.hasAttribute("output-tail") ? codegen.hr(curSnip.syntax) : ""; - // first dependency should be the last one to be prepended - for (const id of ids.reverse()) { - const snip = document.getElementById(id); + const visited = new Set(); + const result = []; + + const recurse = (snip, id) => { if (!snip) { throw new Error(`#${id} dependency not found`); } - code = snip.code + `\n${sep}\n` + code; + + if (visited.has(id)) { + return; + } + visited.add(id); + if (snip.dependsOn) { - const moreIDs = snip.dependsOn.split(" ").filter((i) => !ids.includes(i)); - // first dependency should be the last one to be prepended - ids.push(...moreIDs.reverse()); + snip.dependsOn.split(" ").forEach((depID) => { + recurse(document.getElementById(depID), depID); + }); } - } - return code; + + result.push(snip.code); + }; + + recurse(curSnip, curSnip.id); + // print separators between snippets to tail output later + const sep = curSnip.hasAttribute("output-tail") ? codegen.hr(curSnip.syntax) : ""; + return sep ? result.join(`\n${sep}\n`) : result.join("\n"); } // delay executes a function after a timeout, diff --git a/tests/snippet.js b/tests/snippet.js index ccaa6eb..3f839e2 100644 --- a/tests/snippet.js +++ b/tests/snippet.js @@ -72,6 +72,7 @@ async function runTests() { await testDependsOn(); await testDependsOrder1(); await testDependsOrder2(); + await testDependsOrder3(); t.summary(); return t.errorCount; @@ -1080,6 +1081,41 @@ async function testDependsOrder2() { }); } +async function testDependsOrder3() { + return new Promise((resolve, reject) => { + t.log("testDependsOrder3..."); + const html = ` +
console.log("i")
+ + +
console.log("1")
+ + +
console.log("2")
+ + +
console.log("3")
+ + +
console.log("m")
+ + + `; + const ui = createSnippet(html); + ui.snip.addEventListener("result", (event) => { + const result = event.detail; + t.assert("result.ok", result.ok); + t.assert("result.stdout", result.stdout.trim() == "i\n1\n2\n3\nm"); + t.assert("result.stderr", result.stderr == ""); + t.assert("status done", ui.status.innerHTML.includes("Done")); + t.assert("output", ui.output.out.innerText.trim() == "i\n1\n2\n3\nm"); + resolve(); + }); + ui.toolbar.run.click(); + t.assert("status running", ui.status.innerHTML.includes("Running")); + }); +} + function createSnippet(html) { document.querySelector("#app").innerHTML = html; const editor = document.querySelector("#app pre:last-of-type code");