diff --git a/.gitignore b/.gitignore index 95c878d..d98e728 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ benchmarks/*.brs isolate-* v8*.log *.cpuprofile +roku_modules diff --git a/.vscode/launch.json b/.vscode/launch.json index 678af4a..d682926 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -22,6 +22,14 @@ "${workspaceFolder}/**", "!**/node_modules/**" ] + }, + { + "name": "Debug test-project", + "type": "brightscript", + "request": "launch", + "host": "${promptForHost}", + "rootDir": "${workspaceFolder}/out/dist", + "preLaunchTask": "build-test-app" } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 68d01a9..c2886cd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,8 +7,7 @@ "**/.DS_Store": true, "**/node_modules": true, "**/.nyc_output": true, - "**/coverage": true, - "**/dist": true + "**/coverage": true }, "search.exclude": { "**/node_modules": true, @@ -20,5 +19,6 @@ "typescript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": true, "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false, "files.trimTrailingWhitespace": true, - "typescript.tsdk": "node_modules\\typescript\\lib" + "typescript.tsdk": "node_modules\\typescript\\lib", + "brightscript.bsdk": "1.0.0-alpha.39" } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index dc6a73a..cb8641a 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -9,6 +9,15 @@ "kind": "test", "isDefault": true } + }, + { + "label": "build-test-app", + "type": "shell", + "command": "cd test-project && npx bsc", + "group": { + "kind": "test", + "isDefault": true + } } ] } diff --git a/lib/bsconfig.json b/lib/bsconfig.json index 0967ef4..a29bdce 100644 --- a/lib/bsconfig.json +++ b/lib/bsconfig.json @@ -1 +1,8 @@ -{} +{ + "files": [ + "manifest", + "source/**/*.*", + "components/**/*.*", + "images/**/*.*" + ] +} diff --git a/lib/components/Reftracker.bs b/lib/components/Reftracker.bs new file mode 100644 index 0000000..b3c6ff2 --- /dev/null +++ b/lib/components/Reftracker.bs @@ -0,0 +1,94 @@ +import "pkg:/source/roku_modules/promises/promises.brs" +import "pkg:/source/reftrackerLib.bs" + +' typecast m as ReftrackerM +interface ReftrackerM + nodesByKeypath as ifAssociativeArray + keypathsByNodeReftrackerId as ifAssociativeArray + allNodes as roSGNode[] + nodeQueue as NodeQueueItem[] + top as roSGNodeReftracker +end interface + +function init() + 'build several parallel lookups for nodes to simplify discoverability later + + ' a lookup of nodes by their keypath + m.nodesByKeypath = {} + 'a lookup of all keypaths for a node indexed by the node's reftrackerId + m.keypathsByNodeReftrackerId = {} + 'a flat list of all nodes discovered in this run + m.allNodes = [] + + m.top.runId = reftracker.internal.getRandomUUID() +end function + +function discover() + 'seed the list of nodes with all roots (should be a good starting point) + for each root in m.top.getRoots() as roSGnode[] + registerNodeRef(`root:<${root.subtype()}>`, root) + end for + + 'process the nodes one-by-one + promises.onThen(processNextNode(), function(result) + print "done processing nodes" + end function) +end function + +'Register a reference to a node so we can process it later. This +function registerNodeRef(keypath as string, node as roSGNode) + reftrackerId = reftracker.internal.getReftrackerId(node) + + 'store a reference to the node by its keypath + m.nodesByKeypath[keypath] = node + + isNewNode = m.keypathsByNodeReftrackerId[reftrackerId] = invalid + + if isNewNode + 'build a new array to store all the keypaths for this node + m.keypathsByNodeReftrackerId[reftrackerId] = [] + 'if this is the first time we've seen this node, store it in our list of all nodes + m.allNodes.push(node) + + 'register this node for future evaluation + m.nodeQueue.push({ + keypath: keypath, + node: node + }) + end if + m.keypathsByNodeReftrackerId[reftrackerId].push(keypath) +end function + +function processNextNode() + 'if we have no more nodes, we are done! + if m.nodeQueue.count() = 0 + return promises.resolve(invalid) + end if + + nodeQueueItem = m.nodeQueue.pop() + + return promises.chain(promises.resolve(true), nodeQueueItem).then(function(result, queueItem as NodeQueueItem) + 'if this node supports reftracker functionality, process the node's internal `m` + if (nodeQueueItem.node as dynamic).reftrackerEnabled then + return nodeQueueItem.node@.reftracker_internal_execute({ command: "discover", reftracker: m.top }) + end if + end function).then(function(result, nodeQueueItem as NodeQueueItem) + 'add all public fields to a list of stuff to work on + for each field in nodeQueueItem.node.getFields() + value = nodeQueueItem.node[field] + reftracker.internal.registerWorkItem(m.top, `${nodeQueueItem.keypath}.${field}`, value) + end for + + 'now process this data (it will run async and process in chunks until all are finished) + return reftracker.internal.processWorkItems(m.top) + + end function).then(function(result, nodeQueueItem as NodeQueueItem) + + return processNextNode() + end function).toPromise() +end function + +interface NodeQueueItem + keypath as string + node as roSGNode +end interface diff --git a/lib/components/Reftracker.xml b/lib/components/Reftracker.xml new file mode 100644 index 0000000..6f4a387 --- /dev/null +++ b/lib/components/Reftracker.xml @@ -0,0 +1,8 @@ + + + + + + +