mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-23 10:07:35 +00:00
Introduce asyncDepthFirstFlatMap
This commit is contained in:
parent
d12bbcadd1
commit
76e472acac
2 changed files with 84 additions and 18 deletions
|
|
@ -23,11 +23,43 @@ export async function readUint8array(zip, filepath) {
|
|||
return file.async("uint8array");
|
||||
}
|
||||
|
||||
// Transform an xml tree structure by mapping over each node breadth first
|
||||
function flatten(arr) {
|
||||
const newArr = [];
|
||||
arr.forEach(item => {
|
||||
if (Array.isArray(item)) {
|
||||
newArr.push(...item);
|
||||
} else {
|
||||
newArr.push(item);
|
||||
}
|
||||
});
|
||||
return newArr;
|
||||
}
|
||||
|
||||
// Note: Bad things will happen if the root node you pass returns an array. In
|
||||
// our use case, the root node never returns an array.
|
||||
// Note: Because children are processed in parallel, the order in which nodes
|
||||
// are processed can be a bit confusing. The only guarantee we make is that
|
||||
// children will be processed before their parent.
|
||||
export async function asyncDepthFirstFlatMap(node, mapper) {
|
||||
const { children } = node;
|
||||
if (children == null) {
|
||||
return mapper(node);
|
||||
}
|
||||
|
||||
const promisedChildren = children.map(child =>
|
||||
asyncDepthFirstFlatMap(child, mapper)
|
||||
);
|
||||
const mappedChildren = await Promise.all(promisedChildren);
|
||||
const flatChildren = flatten(mappedChildren);
|
||||
|
||||
return mapper({ ...node, children: flatChildren });
|
||||
}
|
||||
|
||||
// Transform an tree structure by mapping over each node breadth first
|
||||
// Parents are mapped before their children
|
||||
// Children are mapped in parallel
|
||||
export async function asyncTreeMap(xml, mapper) {
|
||||
const mapped = await mapper(xml);
|
||||
export async function asyncTreeMap(node, mapper) {
|
||||
const mapped = await mapper(node);
|
||||
if (mapped.children == null) {
|
||||
return mapped;
|
||||
}
|
||||
|
|
@ -41,20 +73,20 @@ export async function asyncTreeMap(xml, mapper) {
|
|||
|
||||
export async function inlineIncludes(xml, zip) {
|
||||
return asyncTreeMap(xml, async node => {
|
||||
if (node.name === "include") {
|
||||
// TODO: Normalize file names so that they hit the same cache
|
||||
// TODO: Ensure this node does not already have children for some reason
|
||||
const includedFile = await readXml(zip, node.attributes.file);
|
||||
if (includedFile == null) {
|
||||
console.warn(
|
||||
`Tried to include a file that could not be found: ${
|
||||
node.attributes.file
|
||||
}`
|
||||
);
|
||||
return node;
|
||||
}
|
||||
return { ...node, children: includedFile.children };
|
||||
if (node.name !== "include") {
|
||||
return node;
|
||||
}
|
||||
return node;
|
||||
// TODO: Normalize file names so that they hit the same cache
|
||||
// TODO: Ensure this node does not already have children for some reason
|
||||
const includedFile = await readXml(zip, node.attributes.file);
|
||||
if (includedFile == null) {
|
||||
console.warn(
|
||||
`Tried to include a file that could not be found: ${
|
||||
node.attributes.file
|
||||
}`
|
||||
);
|
||||
return node;
|
||||
}
|
||||
return { ...node, children: includedFile.children };
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ describe("readXml", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("asyncTreeMao", () => {
|
||||
describe("asyncTreeMap", () => {
|
||||
it("runs parents before children", async () => {
|
||||
const callNodeNames = new Set();
|
||||
const mapper = node => {
|
||||
|
|
@ -65,3 +65,37 @@ describe("inlineIncludes", () => {
|
|||
expect(resolvedXml).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("asyncDepthFirstFlatMap", () => {
|
||||
test("encounters children first", async () => {
|
||||
const encounterd = [];
|
||||
const mapper = async node => {
|
||||
encounterd.push(node.name);
|
||||
if (node.replaceWithChildren) {
|
||||
return node.children;
|
||||
}
|
||||
return { ...node, name: node.name.toLowerCase() };
|
||||
};
|
||||
|
||||
const start = {
|
||||
name: "A",
|
||||
children: [
|
||||
{ name: "B" },
|
||||
{
|
||||
name: "C",
|
||||
children: [
|
||||
{ name: "E" },
|
||||
{ name: "F", replaceWithChildren: true, children: [{ name: "G" }] },
|
||||
],
|
||||
replaceWithChildren: true,
|
||||
},
|
||||
{ name: "D" },
|
||||
],
|
||||
};
|
||||
expect(await Utils.asyncDepthFirstFlatMap(start, mapper)).toEqual({
|
||||
name: "a",
|
||||
children: [{ name: "b" }, { name: "e" }, { name: "g" }, { name: "d" }],
|
||||
});
|
||||
expect(encounterd).toEqual(["B", "E", "G", "D", "F", "C", "A"]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue