@alloy-js/core 0.24.0-dev.2 → 0.24.0-dev.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/dev/src/components/AccessExpression.test.js +1 -1
- package/dist/dev/src/components/AccessExpression.test.js.map +1 -1
- package/dist/dev/src/components/Output.js +3 -2
- package/dist/dev/src/components/Output.js.map +1 -1
- package/dist/dev/src/components/SourceFile.js.map +1 -1
- package/dist/dev/src/content-slot.test.js +1 -1
- package/dist/dev/src/content-slot.test.js.map +1 -1
- package/dist/dev/src/context.js +30 -3
- package/dist/dev/src/context.js.map +1 -1
- package/dist/dev/src/debug/diagnostics.test.js +1 -1
- package/dist/dev/src/debug/diagnostics.test.js.map +1 -1
- package/dist/dev/src/debug/effects.test.js +1 -1
- package/dist/dev/src/debug/effects.test.js.map +1 -1
- package/dist/dev/src/debug/file-streaming.js +103 -0
- package/dist/dev/src/debug/file-streaming.js.map +1 -0
- package/dist/dev/src/debug/files.test.js +4 -5
- package/dist/dev/src/debug/files.test.js.map +1 -1
- package/dist/dev/src/debug/index.js +4 -6
- package/dist/dev/src/debug/index.js.map +1 -1
- package/dist/dev/src/debug/message-format.test.js +50 -52
- package/dist/dev/src/debug/message-format.test.js.map +1 -1
- package/dist/dev/src/debug/render-tree-orphans.test.js +13 -23
- package/dist/dev/src/debug/render-tree-orphans.test.js.map +1 -1
- package/dist/dev/src/debug/render.js +529 -352
- package/dist/dev/src/debug/render.js.map +1 -1
- package/dist/dev/src/debug/render.test.js +171 -92
- package/dist/dev/src/debug/render.test.js.map +1 -1
- package/dist/dev/src/debug/trace-writer.js +127 -15
- package/dist/dev/src/debug/trace-writer.js.map +1 -1
- package/dist/dev/src/debug/trace.js +0 -36
- package/dist/dev/src/debug/trace.js.map +1 -1
- package/dist/dev/src/devtools/devtools-server.js +55 -32
- package/dist/dev/src/devtools/devtools-server.js.map +1 -1
- package/dist/dev/src/devtools-entry.browser.js.map +1 -1
- package/dist/dev/src/devtools-entry.js.map +1 -1
- package/dist/dev/src/diagnostics.js +19 -1
- package/dist/dev/src/diagnostics.js.map +1 -1
- package/dist/dev/src/index.js +5 -2
- package/dist/dev/src/index.js.map +1 -1
- package/dist/dev/src/jsx-runtime.js +14 -8
- package/dist/dev/src/jsx-runtime.js.map +1 -1
- package/dist/dev/src/output-types.js +2 -0
- package/dist/dev/src/output-types.js.map +1 -0
- package/dist/dev/src/reactivity.js +155 -13
- package/dist/dev/src/reactivity.js.map +1 -1
- package/dist/dev/src/render/get-string-width.js +61 -0
- package/dist/dev/src/render/get-string-width.js.map +1 -0
- package/dist/dev/src/render/index.js +2 -0
- package/dist/dev/src/render/index.js.map +1 -0
- package/dist/dev/src/render/node-context.js +7 -0
- package/dist/dev/src/render/node-context.js.map +1 -0
- package/dist/dev/src/render/node.js +386 -0
- package/dist/dev/src/render/node.js.map +1 -0
- package/dist/dev/src/render/printer-support.js +180 -0
- package/dist/dev/src/render/printer-support.js.map +1 -0
- package/dist/dev/src/render/printer.js +797 -0
- package/dist/dev/src/render/printer.js.map +1 -0
- package/dist/dev/src/render-error.js +79 -0
- package/dist/dev/src/render-error.js.map +1 -0
- package/dist/dev/src/render-output.js +209 -0
- package/dist/dev/src/render-output.js.map +1 -0
- package/dist/dev/src/runtime/create-intrinsic.js +53 -0
- package/dist/dev/src/runtime/create-intrinsic.js.map +1 -0
- package/dist/dev/src/runtime/fragment.js +21 -0
- package/dist/dev/src/runtime/fragment.js.map +1 -0
- package/dist/dev/src/runtime/index.js +13 -0
- package/dist/dev/src/runtime/index.js.map +1 -0
- package/dist/dev/src/runtime/insert.js +453 -0
- package/dist/dev/src/runtime/insert.js.map +1 -0
- package/dist/dev/src/runtime/intrinsic.js +1 -11
- package/dist/dev/src/runtime/intrinsic.js.map +1 -1
- package/dist/dev/src/scheduler.js +38 -14
- package/dist/dev/src/scheduler.js.map +1 -1
- package/dist/dev/src/stc.js +2 -0
- package/dist/dev/src/stc.js.map +1 -1
- package/dist/dev/src/sti.js +1 -1
- package/dist/dev/src/sti.js.map +1 -1
- package/dist/dev/src/symbols/symbol-slot.test.js +1 -1
- package/dist/dev/src/symbols/symbol-slot.test.js.map +1 -1
- package/dist/dev/src/test-render.js +78 -0
- package/dist/dev/src/test-render.js.map +1 -0
- package/dist/dev/src/utils.js +47 -35
- package/dist/dev/src/utils.js.map +1 -1
- package/dist/dev/test/babel-e2e.test.js +218 -0
- package/dist/dev/test/babel-e2e.test.js.map +1 -0
- package/dist/dev/test/components/block.test.js +1 -1
- package/dist/dev/test/components/block.test.js.map +1 -1
- package/dist/dev/test/components/copy-file.test.js +7 -7
- package/dist/dev/test/components/copy-file.test.js.map +1 -1
- package/dist/dev/test/components/update-file.test.js +1 -1
- package/dist/dev/test/components/update-file.test.js.map +1 -1
- package/dist/dev/test/components/wrap.test.js +1 -1
- package/dist/dev/test/components/wrap.test.js.map +1 -1
- package/dist/dev/test/control-flow/match.test.js +1 -1
- package/dist/dev/test/control-flow/match.test.js.map +1 -1
- package/dist/dev/test/control-flow/show.test.js +1 -1
- package/dist/dev/test/control-flow/show.test.js.map +1 -1
- package/dist/dev/test/lazy-isempty.test.js +6 -6
- package/dist/dev/test/lazy-isempty.test.js.map +1 -1
- package/dist/dev/test/node.test.js +80 -0
- package/dist/dev/test/node.test.js.map +1 -0
- package/dist/dev/test/output-e2e.test.js +194 -0
- package/dist/dev/test/output-e2e.test.js.map +1 -0
- package/dist/dev/test/reactivity/circular-reactives.test.js +1 -1
- package/dist/dev/test/reactivity/circular-reactives.test.js.map +1 -1
- package/dist/dev/test/reactivity/cleanup.test.js +1 -1
- package/dist/dev/test/reactivity/cleanup.test.js.map +1 -1
- package/dist/dev/test/rendering/memoization.test.js +6 -1
- package/dist/dev/test/rendering/memoization.test.js.map +1 -1
- package/dist/dev/test/rendering/render-output-diagnostics.test.js +102 -0
- package/dist/dev/test/rendering/render-output-diagnostics.test.js.map +1 -0
- package/dist/dev/test/runtime.test.js +385 -0
- package/dist/dev/test/runtime.test.js.map +1 -0
- package/dist/dev/test/tree-test-utils.js +16 -0
- package/dist/dev/test/tree-test-utils.js.map +1 -0
- package/dist/dev/test/utils.test.js +1 -1
- package/dist/dev/test/utils.test.js.map +1 -1
- package/dist/dev/testing/devtools-utils.js +1 -1
- package/dist/dev/testing/devtools-utils.js.map +1 -1
- package/dist/dev/testing/extend-expect.js +7 -33
- package/dist/dev/testing/extend-expect.js.map +1 -1
- package/dist/dev/testing/render.js +7 -17
- package/dist/dev/testing/render.js.map +1 -1
- package/dist/devtools/index.html +17 -17
- package/dist/src/components/AccessExpression.test.js +1 -1
- package/dist/src/components/AccessExpression.test.js.map +1 -1
- package/dist/src/components/Output.d.ts +1 -1
- package/dist/src/components/Output.d.ts.map +1 -1
- package/dist/src/components/Output.js +2 -1
- package/dist/src/components/Output.js.map +1 -1
- package/dist/src/components/ReferenceOrContent.d.ts +1 -1
- package/dist/src/components/ReferenceOrContent.d.ts.map +1 -1
- package/dist/src/components/SourceFile.d.ts +1 -1
- package/dist/src/components/SourceFile.d.ts.map +1 -1
- package/dist/src/components/SourceFile.js.map +1 -1
- package/dist/src/content-slot.test.js +1 -1
- package/dist/src/content-slot.test.js.map +1 -1
- package/dist/src/context/format-options.d.ts +1 -1
- package/dist/src/context/format-options.d.ts.map +1 -1
- package/dist/src/context.d.ts +9 -1
- package/dist/src/context.d.ts.map +1 -1
- package/dist/src/context.js +30 -3
- package/dist/src/context.js.map +1 -1
- package/dist/src/debug/diagnostics.test.js +1 -1
- package/dist/src/debug/diagnostics.test.js.map +1 -1
- package/dist/src/debug/effects.test.js +1 -1
- package/dist/src/debug/effects.test.js.map +1 -1
- package/dist/src/debug/file-streaming.d.ts +22 -0
- package/dist/src/debug/file-streaming.d.ts.map +1 -0
- package/dist/src/debug/file-streaming.js +103 -0
- package/dist/src/debug/file-streaming.js.map +1 -0
- package/dist/src/debug/files.test.js +4 -5
- package/dist/src/debug/files.test.js.map +1 -1
- package/dist/src/debug/index.d.ts +5 -7
- package/dist/src/debug/index.d.ts.map +1 -1
- package/dist/src/debug/index.js +4 -6
- package/dist/src/debug/index.js.map +1 -1
- package/dist/src/debug/message-format.test.js +16 -18
- package/dist/src/debug/message-format.test.js.map +1 -1
- package/dist/src/debug/render-tree-orphans.test.js +8 -18
- package/dist/src/debug/render-tree-orphans.test.js.map +1 -1
- package/dist/src/debug/render.d.ts +71 -21
- package/dist/src/debug/render.d.ts.map +1 -1
- package/dist/src/debug/render.js +529 -352
- package/dist/src/debug/render.js.map +1 -1
- package/dist/src/debug/render.test.js +137 -74
- package/dist/src/debug/render.test.js.map +1 -1
- package/dist/src/debug/trace-writer.d.ts +6 -1
- package/dist/src/debug/trace-writer.d.ts.map +1 -1
- package/dist/src/debug/trace-writer.js +127 -15
- package/dist/src/debug/trace-writer.js.map +1 -1
- package/dist/src/debug/trace.d.ts +0 -36
- package/dist/src/debug/trace.d.ts.map +1 -1
- package/dist/src/debug/trace.js +0 -36
- package/dist/src/debug/trace.js.map +1 -1
- package/dist/src/devtools/devtools-protocol.d.ts +34 -1
- package/dist/src/devtools/devtools-protocol.d.ts.map +1 -1
- package/dist/src/devtools/devtools-server.d.ts.map +1 -1
- package/dist/src/devtools/devtools-server.js +55 -32
- package/dist/src/devtools/devtools-server.js.map +1 -1
- package/dist/src/devtools-entry.browser.d.ts +1 -1
- package/dist/src/devtools-entry.browser.d.ts.map +1 -1
- package/dist/src/devtools-entry.browser.js.map +1 -1
- package/dist/src/devtools-entry.d.ts +1 -1
- package/dist/src/devtools-entry.d.ts.map +1 -1
- package/dist/src/devtools-entry.js.map +1 -1
- package/dist/src/diagnostics.d.ts +4 -0
- package/dist/src/diagnostics.d.ts.map +1 -1
- package/dist/src/diagnostics.js +19 -1
- package/dist/src/diagnostics.js.map +1 -1
- package/dist/src/index.d.ts +5 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/jsx-runtime.d.ts +13 -4
- package/dist/src/jsx-runtime.d.ts.map +1 -1
- package/dist/src/jsx-runtime.js +14 -8
- package/dist/src/jsx-runtime.js.map +1 -1
- package/dist/src/output-types.d.ts +40 -0
- package/dist/src/output-types.d.ts.map +1 -0
- package/dist/src/output-types.js +2 -0
- package/dist/src/output-types.js.map +1 -0
- package/dist/src/reactivity.d.ts +49 -18
- package/dist/src/reactivity.d.ts.map +1 -1
- package/dist/src/reactivity.js +155 -13
- package/dist/src/reactivity.js.map +1 -1
- package/dist/src/render/get-string-width.d.ts +19 -0
- package/dist/src/render/get-string-width.d.ts.map +1 -0
- package/dist/src/render/get-string-width.js +61 -0
- package/dist/src/render/get-string-width.js.map +1 -0
- package/dist/src/render/index.d.ts +2 -0
- package/dist/src/render/index.d.ts.map +1 -0
- package/dist/src/render/index.js +2 -0
- package/dist/src/render/index.js.map +1 -0
- package/dist/src/render/node-context.d.ts +5 -0
- package/dist/src/render/node-context.d.ts.map +1 -0
- package/dist/src/render/node-context.js +7 -0
- package/dist/src/render/node-context.js.map +1 -0
- package/dist/src/render/node.d.ts +146 -0
- package/dist/src/render/node.d.ts.map +1 -0
- package/dist/src/render/node.js +386 -0
- package/dist/src/render/node.js.map +1 -0
- package/dist/src/render/printer-support.d.ts +50 -0
- package/dist/src/render/printer-support.d.ts.map +1 -0
- package/dist/src/render/printer-support.js +180 -0
- package/dist/src/render/printer-support.js.map +1 -0
- package/dist/src/render/printer.d.ts +35 -0
- package/dist/src/render/printer.d.ts.map +1 -0
- package/dist/src/render/printer.js +797 -0
- package/dist/src/render/printer.js.map +1 -0
- package/dist/src/render-error.d.ts +4 -0
- package/dist/src/render-error.d.ts.map +1 -0
- package/dist/src/render-error.js +79 -0
- package/dist/src/render-error.js.map +1 -0
- package/dist/src/render-output.d.ts +42 -0
- package/dist/src/render-output.d.ts.map +1 -0
- package/dist/src/render-output.js +209 -0
- package/dist/src/render-output.js.map +1 -0
- package/dist/src/runtime/component.d.ts +2 -2
- package/dist/src/runtime/component.d.ts.map +1 -1
- package/dist/src/runtime/create-intrinsic.d.ts +28 -0
- package/dist/src/runtime/create-intrinsic.d.ts.map +1 -0
- package/dist/src/runtime/create-intrinsic.js +53 -0
- package/dist/src/runtime/create-intrinsic.js.map +1 -0
- package/dist/src/runtime/fragment.d.ts +16 -0
- package/dist/src/runtime/fragment.d.ts.map +1 -0
- package/dist/src/runtime/fragment.js +21 -0
- package/dist/src/runtime/fragment.js.map +1 -0
- package/dist/src/runtime/index.d.ts +12 -0
- package/dist/src/runtime/index.d.ts.map +1 -0
- package/dist/src/runtime/index.js +13 -0
- package/dist/src/runtime/index.js.map +1 -0
- package/dist/src/runtime/insert.d.ts +29 -0
- package/dist/src/runtime/insert.d.ts.map +1 -0
- package/dist/src/runtime/insert.js +453 -0
- package/dist/src/runtime/insert.js.map +1 -0
- package/dist/src/runtime/intrinsic.d.ts +12 -29
- package/dist/src/runtime/intrinsic.d.ts.map +1 -1
- package/dist/src/runtime/intrinsic.js +1 -11
- package/dist/src/runtime/intrinsic.js.map +1 -1
- package/dist/src/scheduler.d.ts.map +1 -1
- package/dist/src/scheduler.js +38 -14
- package/dist/src/scheduler.js.map +1 -1
- package/dist/src/stc.d.ts.map +1 -1
- package/dist/src/stc.js +2 -0
- package/dist/src/stc.js.map +1 -1
- package/dist/src/sti.d.ts +7 -6
- package/dist/src/sti.d.ts.map +1 -1
- package/dist/src/sti.js +1 -1
- package/dist/src/sti.js.map +1 -1
- package/dist/src/symbols/symbol-slot.test.js +1 -1
- package/dist/src/symbols/symbol-slot.test.js.map +1 -1
- package/dist/src/test-render.d.ts +31 -0
- package/dist/src/test-render.d.ts.map +1 -0
- package/dist/src/test-render.js +78 -0
- package/dist/src/test-render.js.map +1 -0
- package/dist/src/utils.d.ts +1 -1
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +40 -28
- package/dist/src/utils.js.map +1 -1
- package/dist/src/write-output.d.ts +1 -1
- package/dist/src/write-output.d.ts.map +1 -1
- package/dist/test/babel-e2e.test.d.ts +13 -0
- package/dist/test/babel-e2e.test.d.ts.map +1 -0
- package/dist/test/babel-e2e.test.js +218 -0
- package/dist/test/babel-e2e.test.js.map +1 -0
- package/dist/test/components/block.test.js +1 -1
- package/dist/test/components/block.test.js.map +1 -1
- package/dist/test/components/copy-file.test.d.ts.map +1 -1
- package/dist/test/components/copy-file.test.js +1 -1
- package/dist/test/components/copy-file.test.js.map +1 -1
- package/dist/test/components/update-file.test.js +1 -1
- package/dist/test/components/update-file.test.js.map +1 -1
- package/dist/test/components/wrap.test.js +1 -1
- package/dist/test/components/wrap.test.js.map +1 -1
- package/dist/test/control-flow/match.test.js +1 -1
- package/dist/test/control-flow/match.test.js.map +1 -1
- package/dist/test/control-flow/show.test.js +1 -1
- package/dist/test/control-flow/show.test.js.map +1 -1
- package/dist/test/lazy-isempty.test.js +6 -6
- package/dist/test/lazy-isempty.test.js.map +1 -1
- package/dist/test/node.test.d.ts +2 -0
- package/dist/test/node.test.d.ts.map +1 -0
- package/dist/test/node.test.js +80 -0
- package/dist/test/node.test.js.map +1 -0
- package/dist/test/output-e2e.test.d.ts +13 -0
- package/dist/test/output-e2e.test.d.ts.map +1 -0
- package/dist/test/output-e2e.test.js +194 -0
- package/dist/test/output-e2e.test.js.map +1 -0
- package/dist/test/reactivity/circular-reactives.test.js +1 -1
- package/dist/test/reactivity/circular-reactives.test.js.map +1 -1
- package/dist/test/reactivity/cleanup.test.js +1 -1
- package/dist/test/reactivity/cleanup.test.js.map +1 -1
- package/dist/test/rendering/memoization.test.js +6 -1
- package/dist/test/rendering/memoization.test.js.map +1 -1
- package/dist/test/rendering/render-output-diagnostics.test.d.ts +2 -0
- package/dist/test/rendering/render-output-diagnostics.test.d.ts.map +1 -0
- package/dist/test/rendering/render-output-diagnostics.test.js +82 -0
- package/dist/test/rendering/render-output-diagnostics.test.js.map +1 -0
- package/dist/test/runtime.test.d.ts +11 -0
- package/dist/test/runtime.test.d.ts.map +1 -0
- package/dist/test/runtime.test.js +385 -0
- package/dist/test/runtime.test.js.map +1 -0
- package/dist/test/tree-test-utils.d.ts +3 -0
- package/dist/test/tree-test-utils.d.ts.map +1 -0
- package/dist/test/tree-test-utils.js +16 -0
- package/dist/test/tree-test-utils.js.map +1 -0
- package/dist/test/utils.test.js +1 -1
- package/dist/test/utils.test.js.map +1 -1
- package/dist/testing/devtools-utils.d.ts.map +1 -1
- package/dist/testing/devtools-utils.js +1 -1
- package/dist/testing/devtools-utils.js.map +1 -1
- package/dist/testing/extend-expect.d.ts.map +1 -1
- package/dist/testing/extend-expect.js +7 -33
- package/dist/testing/extend-expect.js.map +1 -1
- package/dist/testing/render.d.ts +7 -9
- package/dist/testing/render.d.ts.map +1 -1
- package/dist/testing/render.js +7 -17
- package/dist/testing/render.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docs/api/components/Output.md +0 -3
- package/docs/api/components/SourceFile.md +0 -3
- package/docs/api/functions/createComment.md +18 -0
- package/docs/api/functions/createElement.md +19 -0
- package/docs/api/functions/createFragment.md +17 -0
- package/docs/api/functions/createTextNode.md +18 -0
- package/docs/api/functions/emitDiagnosticForTree.md +19 -0
- package/docs/api/functions/ensureIsEmpty.md +1 -1
- package/docs/api/functions/getContextForNode.md +18 -0
- package/docs/api/functions/getContextForRenderNode.md +4 -4
- package/docs/api/functions/getDiagnosticsForTree.md +7 -5
- package/docs/api/functions/getRegisteredDiagnosticsForTree.md +18 -0
- package/docs/api/functions/index.md +17 -12
- package/docs/api/functions/isCustomContext.md +4 -4
- package/docs/api/functions/notifyContentState.md +6 -0
- package/docs/api/functions/printTree.md +6 -16
- package/docs/api/functions/registerDiagnosticsForTree.md +19 -0
- package/docs/api/functions/render.md +1 -2
- package/docs/api/functions/renderAsync.md +1 -2
- package/docs/api/functions/renderTree.md +8 -5
- package/docs/api/functions/reportDiagnosticsForTree.md +18 -0
- package/docs/api/functions/runInContext.md +28 -0
- package/docs/api/functions/sourceFilesForTree.md +6 -16
- package/docs/api/index.md +3 -3
- package/docs/api/testing/functions/index.md +1 -1
- package/docs/api/testing/functions/renderToString.md +1 -1
- package/docs/api/types/AlloyNode.md +22 -0
- package/docs/api/types/Child.md +1 -1
- package/docs/api/types/CommentNode.md +15 -0
- package/docs/api/types/Context.md +13 -15
- package/docs/api/types/ElementNode.md +18 -0
- package/docs/api/types/FragmentNode.md +12 -0
- package/docs/api/types/Insertable.md +7 -0
- package/docs/api/types/NodeType.md +5 -0
- package/docs/api/types/OutputDirectory.md +0 -50
- package/docs/api/types/PrintTreeOptions.md +0 -1
- package/docs/api/types/RenderTreeOptions.md +7 -0
- package/docs/api/types/StiComponentCreator.md +4 -4
- package/docs/api/types/StiSignature.md +1 -1
- package/docs/api/types/TextNode.md +16 -0
- package/docs/api/types/index.md +10 -28
- package/docs/api/variables/COMMENT_NODE.md +5 -0
- package/docs/api/variables/ELEMENT_NODE.md +11 -0
- package/docs/api/variables/FRAGMENT_NODE.md +5 -0
- package/docs/api/variables/TEXT_NODE.md +5 -0
- package/docs/api/variables/index.md +4 -2
- package/docs/formatting.md +1 -1
- package/docs/rendering.md +4 -4
- package/package.json +6 -6
- package/src/components/AccessExpression.test.tsx +1 -1
- package/src/components/Output.tsx +2 -1
- package/src/components/SourceFile.tsx +1 -1
- package/src/content-slot.test.tsx +1 -1
- package/src/context/format-options.ts +1 -1
- package/src/context.ts +37 -4
- package/src/debug/diagnostics.test.tsx +1 -1
- package/src/debug/effects.test.tsx +1 -1
- package/src/debug/file-streaming.ts +115 -0
- package/src/debug/files.test.tsx +15 -11
- package/src/debug/index.ts +11 -11
- package/src/debug/message-format.test.tsx +32 -19
- package/src/debug/render-tree-orphans.test.tsx +10 -19
- package/src/debug/render.test.tsx +206 -78
- package/src/debug/render.ts +642 -495
- package/src/debug/trace-writer.ts +168 -14
- package/src/debug/trace.ts +0 -20
- package/src/devtools/devtools-protocol.ts +43 -0
- package/src/devtools/devtools-server.ts +57 -32
- package/src/devtools-entry.browser.ts +5 -0
- package/src/devtools-entry.ts +5 -0
- package/src/diagnostics.ts +31 -0
- package/src/index.ts +66 -2
- package/src/jsx-runtime.ts +16 -14
- package/src/output-types.ts +47 -0
- package/src/reactivity.ts +186 -40
- package/src/render/get-string-width.ts +201 -0
- package/src/render/index.ts +1 -0
- package/src/render/node-context.ts +14 -0
- package/src/render/node.ts +442 -0
- package/src/render/printer-support.ts +209 -0
- package/src/render/printer.ts +817 -0
- package/src/render-error.ts +98 -0
- package/src/render-output.ts +243 -0
- package/src/runtime/component.ts +2 -2
- package/src/runtime/create-intrinsic.ts +56 -0
- package/src/runtime/fragment.ts +22 -0
- package/src/runtime/index.ts +12 -0
- package/src/runtime/insert.ts +569 -0
- package/src/runtime/intrinsic.ts +14 -70
- package/src/scheduler.ts +40 -25
- package/src/stc.ts +3 -0
- package/src/sti.ts +17 -20
- package/src/symbols/symbol-slot.test.tsx +1 -1
- package/src/test-render.ts +103 -0
- package/src/utils.tsx +55 -37
- package/src/write-output.ts +1 -1
- package/temp/api-testing.json +390 -14
- package/temp/api.json +4320 -4144
- package/test/babel-e2e.test.ts +224 -0
- package/test/components/block.test.tsx +1 -1
- package/test/components/copy-file.test.tsx +2 -1
- package/test/components/update-file.test.tsx +1 -1
- package/test/components/wrap.test.tsx +1 -1
- package/test/control-flow/match.test.tsx +1 -1
- package/test/control-flow/show.test.tsx +1 -1
- package/test/lazy-isempty.test.tsx +6 -6
- package/test/node.test.ts +90 -0
- package/test/output-e2e.test.ts +198 -0
- package/test/reactivity/circular-reactives.test.tsx +1 -1
- package/test/reactivity/cleanup.test.tsx +1 -1
- package/test/rendering/memoization.test.tsx +6 -1
- package/test/rendering/render-output-diagnostics.test.tsx +120 -0
- package/test/runtime.test.ts +448 -0
- package/test/tree-test-utils.ts +23 -0
- package/test/utils.test.tsx +1 -1
- package/testing/devtools-utils.ts +2 -0
- package/testing/extend-expect.ts +8 -46
- package/testing/render.ts +17 -21
- package/dist/dev/src/print-hook.js +0 -10
- package/dist/dev/src/print-hook.js.map +0 -1
- package/dist/dev/src/render.js +0 -872
- package/dist/dev/src/render.js.map +0 -1
- package/dist/src/print-hook.d.ts +0 -14
- package/dist/src/print-hook.d.ts.map +0 -1
- package/dist/src/print-hook.js +0 -10
- package/dist/src/print-hook.js.map +0 -1
- package/dist/src/render.d.ts +0 -155
- package/dist/src/render.d.ts.map +0 -1
- package/dist/src/render.js +0 -872
- package/dist/src/render.js.map +0 -1
- package/docs/api/functions/createIntrinsic.md +0 -19
- package/docs/api/functions/createRenderTreeHook.md +0 -19
- package/docs/api/functions/getElementCache.md +0 -17
- package/docs/api/functions/isIntrinsicElement.md +0 -18
- package/docs/api/functions/isPrintHook.md +0 -18
- package/docs/api/types/AlignIntrinsicElement.md +0 -5
- package/docs/api/types/BrIntrinsicElement.md +0 -5
- package/docs/api/types/BreakParentIntrinsicElement.md +0 -5
- package/docs/api/types/DedentIntrinsicElement.md +0 -5
- package/docs/api/types/DedentToRootIntrinsicElement.md +0 -5
- package/docs/api/types/ElementCache.md +0 -5
- package/docs/api/types/ElementCacheKey.md +0 -5
- package/docs/api/types/FillIntrinsicElement.md +0 -5
- package/docs/api/types/GroupIntrinsicElement.md +0 -5
- package/docs/api/types/HardlineIntrinsicElement.md +0 -5
- package/docs/api/types/HbrIntrinsicElement.md +0 -5
- package/docs/api/types/IfBreakIntrinsicElement.md +0 -5
- package/docs/api/types/IndentIfBreakIntrinsicElement.md +0 -5
- package/docs/api/types/IndentIntrinsicElement.md +0 -5
- package/docs/api/types/IntrinsicElement.md +0 -5
- package/docs/api/types/IntrinsicElementBase.md +0 -9
- package/docs/api/types/LbrIntrinsicElement.md +0 -5
- package/docs/api/types/LineIntrinsicElement.md +0 -5
- package/docs/api/types/LineSuffixBoundaryIntrinsicElement.md +0 -5
- package/docs/api/types/LineSuffixIntrinsicElement.md +0 -5
- package/docs/api/types/LiterallineIntrinsicElement.md +0 -5
- package/docs/api/types/MarkAsRootIntrinsicElement.md +0 -5
- package/docs/api/types/PrintHook.md +0 -10
- package/docs/api/types/RenderedTextTree.md +0 -5
- package/docs/api/types/SbrIntrinsicElement.md +0 -5
- package/docs/api/types/SoftlineIntrinsicElement.md +0 -5
- package/docs/api/variables/intrinsicElementKey.md +0 -5
- package/docs/api/variables/printHookTag.md +0 -7
- package/src/print-hook.ts +0 -22
- package/src/render.ts +0 -1154
package/src/debug/render.ts
CHANGED
|
@@ -1,3 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debug-render module — bridges the AlloyNode render tree to the
|
|
3
|
+
* trace-writer SQLite DB and the devtools WebSocket protocol.
|
|
4
|
+
*
|
|
5
|
+
* # Tree shape
|
|
6
|
+
*
|
|
7
|
+
* The trace tree mirrors the real AlloyNode tree. Component invocations
|
|
8
|
+
* are recorded separately as canonical metadata (`component_instances`
|
|
9
|
+
* and `component_roots`) so devtools can derive presentation groupings
|
|
10
|
+
* without storing UI artifacts as render nodes.
|
|
11
|
+
*
|
|
12
|
+
* Real AlloyNodes are exposed as:
|
|
13
|
+
*
|
|
14
|
+
* - `TextNode` → `kind: "text"`, with `value: data`
|
|
15
|
+
* - `ElementNode` with a marker `localName` (`alloy:source-file`,
|
|
16
|
+
* `alloy:directory`, `alloy:copy-file`) → `kind` set to the marker
|
|
17
|
+
* - any other `ElementNode` → `kind: "intrinsic"` with `name: localName`
|
|
18
|
+
* - `FragmentNode` → `kind: "fragment"`
|
|
19
|
+
* - `CommentNode` (slot/ctx markers) → not exposed (skipped)
|
|
20
|
+
*
|
|
21
|
+
* # Hook surface
|
|
22
|
+
*
|
|
23
|
+
* Mutation hooks:
|
|
24
|
+
* - `nodeAttached(node, parent, before)` — called from
|
|
25
|
+
* `render/node.ts::insertBefore`. Emits a `node_added` event for
|
|
26
|
+
* `node` (and any descendants it brings with it, e.g. a previously
|
|
27
|
+
* built subtree being moved or a fragment splice).
|
|
28
|
+
* - `nodeDetached(node, parent)` — called from
|
|
29
|
+
* `render/node.ts::detach`. Emits `node_removed` for `node` and all
|
|
30
|
+
* descendants.
|
|
31
|
+
*
|
|
32
|
+
* Lifecycle hooks:
|
|
33
|
+
* - `initialize(root)` — called at the start of each `render` /
|
|
34
|
+
* `renderAsync`. Resets module state and emits `render:reset`.
|
|
35
|
+
* - `complete()` — emits `render:complete`.
|
|
36
|
+
* - `flushJobsComplete()` — emits `flushJobs:complete`.
|
|
37
|
+
* - `error(info, stack)` — emits `render:error` and writes a
|
|
38
|
+
* `render_errors` row.
|
|
39
|
+
*
|
|
40
|
+
* Component hooks:
|
|
41
|
+
* - `beginComponent(opts)` — records a component instance and captures
|
|
42
|
+
* the real top-level AlloyNodes it emits.
|
|
43
|
+
*
|
|
44
|
+
* Each hook short-circuits when neither devtools nor the trace DB
|
|
45
|
+
* are enabled.
|
|
46
|
+
*/
|
|
47
|
+
|
|
1
48
|
import { watch } from "@vue/reactivity";
|
|
2
49
|
import * as devalue from "devalue";
|
|
3
50
|
import type {
|
|
@@ -8,34 +55,48 @@ import {
|
|
|
8
55
|
isDevtoolsEnabled,
|
|
9
56
|
registerDevtoolsMessageHandler,
|
|
10
57
|
} from "../devtools/devtools-server.js";
|
|
11
|
-
import {
|
|
12
|
-
isPrintHook,
|
|
13
|
-
type PrintHook,
|
|
14
|
-
type RenderedTextTree,
|
|
15
|
-
} from "../print-hook.js";
|
|
16
58
|
import { getContext, untrack } from "../reactivity.js";
|
|
17
|
-
import
|
|
59
|
+
import { getContextForNode } from "../render/node-context.js";
|
|
60
|
+
import {
|
|
61
|
+
AlloyNode,
|
|
62
|
+
CommentNode,
|
|
63
|
+
ELEMENT_NODE,
|
|
64
|
+
ElementNode,
|
|
65
|
+
FRAGMENT_NODE,
|
|
66
|
+
setMutationListener,
|
|
67
|
+
TEXT_NODE,
|
|
68
|
+
TextNode,
|
|
69
|
+
} from "../render/node.js";
|
|
18
70
|
import { flushJobsAsync } from "../scheduler.js";
|
|
71
|
+
import type { ComponentCreator } from "./../runtime/component.js";
|
|
72
|
+
import {
|
|
73
|
+
flushAllDirtyFiles,
|
|
74
|
+
markFileDirtyForNode,
|
|
75
|
+
reset as resetFileStreaming,
|
|
76
|
+
} from "./file-streaming.js";
|
|
19
77
|
import { sanitizeRecord } from "./serialize.js";
|
|
20
78
|
import { resolveComponentSource } from "./source-map.js";
|
|
21
79
|
import {
|
|
22
|
-
|
|
23
|
-
|
|
80
|
+
deleteComponentInstance,
|
|
81
|
+
deleteComponentRoot,
|
|
82
|
+
insertComponentInstance,
|
|
83
|
+
insertComponentRoot,
|
|
24
84
|
insertDirectory,
|
|
25
85
|
insertOutputFile,
|
|
26
86
|
insertRenderError,
|
|
27
|
-
insertRenderNode,
|
|
28
87
|
isTraceEnabled,
|
|
29
88
|
notifyFlushComplete,
|
|
30
89
|
notifyRenderComplete,
|
|
31
90
|
notifyRenderReset,
|
|
32
91
|
deleteRenderNode as traceDeleteRenderNode,
|
|
33
|
-
|
|
92
|
+
insertRenderNode as traceInsertRenderNode,
|
|
93
|
+
updateComponentInstanceProps,
|
|
34
94
|
updateEffectComponentByContext,
|
|
35
|
-
updateRenderNodeContext,
|
|
36
95
|
} from "./trace-writer.js";
|
|
37
96
|
import { isDebugEnabled, logDevtoolsMessage } from "./trace.js";
|
|
38
97
|
|
|
98
|
+
// #region Public debug types used by runtime and devtools integration
|
|
99
|
+
|
|
39
100
|
/** The kind discriminant for render tree nodes. */
|
|
40
101
|
export type RenderTreeNodeKind = RenderTreeNode["kind"];
|
|
41
102
|
|
|
@@ -54,14 +115,17 @@ export interface RenderNodeActions {
|
|
|
54
115
|
}
|
|
55
116
|
|
|
56
117
|
export interface BeginComponentOptions {
|
|
57
|
-
parent: RenderedTextTree;
|
|
58
|
-
index: number;
|
|
59
|
-
node: RenderedTextTree;
|
|
60
118
|
component: ComponentCreator<unknown>;
|
|
61
119
|
propsSource: Record<string, unknown> | undefined;
|
|
62
120
|
source: RenderTreeNodeInfo["source"] | undefined;
|
|
63
|
-
|
|
64
|
-
|
|
121
|
+
/** Parent AlloyNode the component is being inserted into. */
|
|
122
|
+
parent: AlloyNode;
|
|
123
|
+
/** Optional rerender bindings (only meaningful in devtools). */
|
|
124
|
+
actions?: RenderNodeActions;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function isRerenderEnabled(): boolean {
|
|
128
|
+
return isDevtoolsEnabled();
|
|
65
129
|
}
|
|
66
130
|
|
|
67
131
|
export interface ComponentDebugSession {
|
|
@@ -70,118 +134,145 @@ export interface ComponentDebugSession {
|
|
|
70
134
|
dispose(): void;
|
|
71
135
|
}
|
|
72
136
|
|
|
73
|
-
|
|
74
|
-
|
|
137
|
+
export interface RenderErrorInfo {
|
|
138
|
+
name: string;
|
|
139
|
+
message: string;
|
|
140
|
+
stack?: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface RenderErrorStackEntry extends ProtocolRenderErrorStackEntry {
|
|
144
|
+
props?: Record<string, unknown> | undefined;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// #endregion
|
|
75
148
|
|
|
76
|
-
//
|
|
77
|
-
// Module state — reset in initialize()
|
|
78
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
149
|
+
// #region Module state
|
|
79
150
|
|
|
80
|
-
|
|
81
|
-
let
|
|
82
|
-
|
|
83
|
-
let
|
|
84
|
-
|
|
151
|
+
/** Map AlloyNode → trace node id (assigned when first emitted). */
|
|
152
|
+
let nodeIds = new WeakMap<AlloyNode, number>();
|
|
153
|
+
/** AlloyNodes that have been emitted to the trace tree. */
|
|
154
|
+
let tracked = new WeakSet<AlloyNode>();
|
|
155
|
+
/** Component owner captured while a subtree is built before being rooted. */
|
|
156
|
+
let pendingOwnerComponent = new WeakMap<AlloyNode, ComponentFrame>();
|
|
157
|
+
/** Component roots captured while a subtree is built before being rooted. */
|
|
158
|
+
let pendingRootComponents = new WeakMap<AlloyNode, ComponentFrame[]>();
|
|
159
|
+
/** Sidecar metadata per node id — kind + name + source for cleanup re-emit. */
|
|
160
|
+
let nodeKinds = new Map<
|
|
161
|
+
number,
|
|
85
162
|
{ kind: string; name?: string; source?: RenderTreeNodeInfo["source"] }
|
|
86
163
|
>();
|
|
164
|
+
/** Sidecar: nodes that own a file/directory entry in the trace DB. */
|
|
87
165
|
let fileNodes = new Map<number, { path: string; filetype: string }>();
|
|
88
166
|
let directoryNodes = new Map<number, { path: string }>();
|
|
167
|
+
/** Latest props-serialized value per id (for de-duplicated updates). */
|
|
89
168
|
let nodeProps = new Map<number, string | undefined>();
|
|
169
|
+
/** Devtools-side rerender bindings keyed by render-tree node id. */
|
|
90
170
|
let rerenderActions = new Map<number, RenderNodeActions>();
|
|
91
|
-
let nextId = 1;
|
|
92
|
-
let handlerRegistered = false;
|
|
93
171
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
172
|
+
type ComponentFrame = {
|
|
173
|
+
id: number;
|
|
174
|
+
parentComponentId: number | null;
|
|
175
|
+
hostParent: AlloyNode;
|
|
176
|
+
name: string;
|
|
177
|
+
propsSerialized: string | undefined;
|
|
178
|
+
source: RenderTreeNodeInfo["source"] | undefined;
|
|
179
|
+
contextId: number | null;
|
|
180
|
+
roots: number[];
|
|
181
|
+
rootSet: Set<number>;
|
|
182
|
+
file?: { path: string; filetype: string };
|
|
183
|
+
directory?: { path: string };
|
|
184
|
+
actions?: RenderNodeActions;
|
|
185
|
+
stopWatch?: () => void;
|
|
186
|
+
};
|
|
187
|
+
let componentStack: ComponentFrame[] = [];
|
|
188
|
+
let componentsById = new Map<number, ComponentFrame>();
|
|
189
|
+
let componentByContextId = new Map<number, ComponentFrame>();
|
|
190
|
+
let ownerComponentByNodeId = new Map<number, number>();
|
|
191
|
+
let childComponentsById = new Map<number, Set<number>>();
|
|
192
|
+
let rootComponentsByNodeId = new Map<number, Set<number>>();
|
|
97
193
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
194
|
+
/**
|
|
195
|
+
* Reverse index: parent render-tree id → set of currently-emitted child
|
|
196
|
+
* render-tree ids. Maintained by every `insertRenderNode` call in this
|
|
197
|
+
* module, and used by `emitRemoved` to cascade-remove descendants whose
|
|
198
|
+
* lifetime is bound to the removed parent.
|
|
199
|
+
*/
|
|
200
|
+
let renderChildIds = new Map<number, Set<number>>();
|
|
201
|
+
|
|
202
|
+
function insertRenderNode(
|
|
203
|
+
id: number,
|
|
204
|
+
parentId: number | null,
|
|
205
|
+
kind: string,
|
|
206
|
+
name: string | undefined,
|
|
207
|
+
props: string | undefined,
|
|
208
|
+
sourceFile: string | undefined,
|
|
209
|
+
sourceLine: number | undefined,
|
|
210
|
+
sourceCol: number | undefined,
|
|
211
|
+
contextId: number | null,
|
|
212
|
+
value: string | undefined,
|
|
213
|
+
): void {
|
|
214
|
+
if (parentId !== null) {
|
|
215
|
+
let set = renderChildIds.get(parentId);
|
|
216
|
+
if (!set) {
|
|
217
|
+
set = new Set();
|
|
218
|
+
renderChildIds.set(parentId, set);
|
|
108
219
|
}
|
|
109
|
-
|
|
220
|
+
set.add(id);
|
|
221
|
+
}
|
|
222
|
+
traceInsertRenderNode(
|
|
223
|
+
id,
|
|
224
|
+
parentId,
|
|
225
|
+
kind,
|
|
226
|
+
name,
|
|
227
|
+
props,
|
|
228
|
+
sourceFile,
|
|
229
|
+
sourceLine,
|
|
230
|
+
sourceCol,
|
|
231
|
+
contextId,
|
|
232
|
+
value,
|
|
233
|
+
);
|
|
110
234
|
}
|
|
111
235
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
function emitNodeRemoved(parentId: number | null, id: number) {
|
|
117
|
-
clearRenderTreeChildrenForId(id);
|
|
118
|
-
traceDeleteRenderNode(id);
|
|
119
|
-
|
|
120
|
-
rerenderActions.delete(id);
|
|
121
|
-
nodeProps.delete(id);
|
|
122
|
-
idToNode.delete(id);
|
|
236
|
+
let nextId = 1;
|
|
237
|
+
let nextErrorId = 1;
|
|
238
|
+
let handlerRegistered = false;
|
|
123
239
|
|
|
124
|
-
|
|
125
|
-
if (fileInfo) {
|
|
126
|
-
deleteOutputFile(fileInfo.path);
|
|
127
|
-
fileNodes.delete(id);
|
|
128
|
-
}
|
|
240
|
+
// #endregion
|
|
129
241
|
|
|
130
|
-
|
|
131
|
-
if (dirInfo) {
|
|
132
|
-
deleteDirectory(dirInfo.path);
|
|
133
|
-
directoryNodes.delete(id);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
242
|
+
// #region Lifecycle
|
|
136
243
|
|
|
137
|
-
function
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
list = [];
|
|
141
|
-
entryIds.set(parent, list);
|
|
244
|
+
export function initialize(root: AlloyNode) {
|
|
245
|
+
for (const frame of componentsById.values()) {
|
|
246
|
+
frame.stopWatch?.();
|
|
142
247
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
function getOrCreateNodeId(node: TrackedNode) {
|
|
147
|
-
const existing = nodeIds.get(node);
|
|
148
|
-
if (existing) {
|
|
149
|
-
// Restore reverse mapping — emitNodeRemoved deletes idToNode but nodeIds
|
|
150
|
-
// (WeakMap) survives. Without this, clearRenderTreeChildrenForId can't
|
|
151
|
-
// find the node on the next cleanup, leaving orphaned children in the DB.
|
|
152
|
-
idToNode.set(existing, node);
|
|
153
|
-
return existing;
|
|
248
|
+
if (!isDebugEnabled()) {
|
|
249
|
+
setMutationListener(null);
|
|
250
|
+
return;
|
|
154
251
|
}
|
|
155
|
-
const id = nextId++;
|
|
156
|
-
nodeIds.set(node, id);
|
|
157
|
-
idToNode.set(id, node);
|
|
158
|
-
return id;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export function getRenderNodeId(node: RenderedTextTree | PrintHook) {
|
|
162
|
-
if (!isDebugEnabled()) return undefined;
|
|
163
|
-
return getOrCreateNodeId(node);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function setEntryId(parent: RenderedTextTree, index: number, id: number) {
|
|
167
|
-
const list = getEntryList(parent);
|
|
168
|
-
list[index] = id;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export function initialize(root: RenderedTextTree) {
|
|
172
|
-
if (!isDebugEnabled()) return;
|
|
173
252
|
ensureDevtoolsHandler();
|
|
174
253
|
nodeIds = new WeakMap();
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
254
|
+
tracked = new WeakSet();
|
|
255
|
+
pendingOwnerComponent = new WeakMap();
|
|
256
|
+
pendingRootComponents = new WeakMap();
|
|
257
|
+
renderChildIds = new Map();
|
|
258
|
+
nodeKinds = new Map();
|
|
178
259
|
fileNodes = new Map();
|
|
179
260
|
directoryNodes = new Map();
|
|
180
261
|
nodeProps = new Map();
|
|
181
262
|
rerenderActions = new Map();
|
|
263
|
+
componentStack = [];
|
|
264
|
+
componentsById = new Map();
|
|
265
|
+
componentByContextId = new Map();
|
|
266
|
+
ownerComponentByNodeId = new Map();
|
|
267
|
+
childComponentsById = new Map();
|
|
268
|
+
rootComponentsByNodeId = new Map();
|
|
182
269
|
nextId = 1;
|
|
270
|
+
resetFileStreaming();
|
|
271
|
+
setMutationListener({ attached: nodeAttached, detached: nodeDetached });
|
|
183
272
|
notifyRenderReset();
|
|
184
273
|
const rootId = getOrCreateNodeId(root);
|
|
274
|
+
tracked.add(root);
|
|
275
|
+
nodeKinds.set(rootId, { kind: "root" });
|
|
185
276
|
insertRenderNode(
|
|
186
277
|
rootId,
|
|
187
278
|
null,
|
|
@@ -196,346 +287,402 @@ export function initialize(root: RenderedTextTree) {
|
|
|
196
287
|
);
|
|
197
288
|
}
|
|
198
289
|
|
|
199
|
-
export function
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
)
|
|
203
|
-
if (!isDebugEnabled()) return;
|
|
204
|
-
const id = getOrCreateNodeId(node);
|
|
205
|
-
rerenderActions.set(id, actions);
|
|
290
|
+
export function complete() {
|
|
291
|
+
flushAllDirtyFiles();
|
|
292
|
+
logDevtoolsMessage({ type: "render:complete" });
|
|
293
|
+
notifyRenderComplete();
|
|
206
294
|
}
|
|
207
295
|
|
|
208
|
-
export function
|
|
209
|
-
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
296
|
+
export function flushJobsComplete() {
|
|
297
|
+
logDevtoolsMessage({ type: "flushJobs:complete" });
|
|
298
|
+
notifyFlushComplete();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// #endregion
|
|
302
|
+
|
|
303
|
+
// #region Node-id management
|
|
304
|
+
|
|
305
|
+
function getOrCreateNodeId(node: AlloyNode): number {
|
|
306
|
+
const existing = nodeIds.get(node);
|
|
307
|
+
if (existing !== undefined) return existing;
|
|
308
|
+
const id = nextId++;
|
|
309
|
+
nodeIds.set(node, id);
|
|
310
|
+
return id;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function getRenderNodeId(node: AlloyNode | unknown): number | undefined {
|
|
314
|
+
if (!isDebugEnabled()) return undefined;
|
|
315
|
+
if (!(node instanceof AlloyNode)) return undefined;
|
|
316
|
+
return nodeIds.get(node);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// #endregion
|
|
320
|
+
|
|
321
|
+
// #region Kind classification
|
|
322
|
+
|
|
323
|
+
const SKIPPED_COMMENT_DATAS = new Set([
|
|
324
|
+
"slot:start",
|
|
325
|
+
"slot:end",
|
|
326
|
+
"slot:item:start",
|
|
327
|
+
"slot:item:end",
|
|
328
|
+
"ctx:start",
|
|
329
|
+
"ctx:end",
|
|
330
|
+
"component:start",
|
|
331
|
+
"component:end",
|
|
332
|
+
]);
|
|
333
|
+
|
|
334
|
+
function shouldExposeNode(node: AlloyNode): boolean {
|
|
335
|
+
if (node instanceof CommentNode) {
|
|
336
|
+
// Bookkeeping comments are not part of the render tree.
|
|
337
|
+
return !SKIPPED_COMMENT_DATAS.has(node.data);
|
|
215
338
|
}
|
|
339
|
+
return true;
|
|
216
340
|
}
|
|
217
341
|
|
|
218
|
-
function
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
342
|
+
function classifyNode(node: AlloyNode): {
|
|
343
|
+
kind: string;
|
|
344
|
+
name?: string;
|
|
345
|
+
value?: string;
|
|
346
|
+
} {
|
|
347
|
+
const t = node.nodeType;
|
|
348
|
+
if (t === TEXT_NODE) {
|
|
349
|
+
return { kind: "text", value: (node as TextNode).data };
|
|
350
|
+
}
|
|
351
|
+
if (t === FRAGMENT_NODE) {
|
|
352
|
+
return { kind: "fragment" };
|
|
353
|
+
}
|
|
354
|
+
if (t === ELEMENT_NODE) {
|
|
355
|
+
const ln = (node as ElementNode).localName;
|
|
222
356
|
if (
|
|
223
|
-
|
|
224
|
-
|
|
357
|
+
ln === "alloy:source-file" ||
|
|
358
|
+
ln === "alloy:directory" ||
|
|
359
|
+
ln === "alloy:copy-file"
|
|
225
360
|
) {
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
const rawId = (message as { id?: unknown }).id;
|
|
229
|
-
if (typeof rawId !== "number" && typeof rawId !== "string") return;
|
|
230
|
-
const id = Number(rawId);
|
|
231
|
-
if (!Number.isFinite(id)) return;
|
|
232
|
-
const actions = rerenderActions.get(id);
|
|
233
|
-
if (!actions) return;
|
|
234
|
-
if (message.type === "render:rerender") {
|
|
235
|
-
actions.rerender();
|
|
236
|
-
} else {
|
|
237
|
-
actions.rerenderAndBreak();
|
|
361
|
+
return { kind: ln.slice("alloy:".length), name: ln };
|
|
238
362
|
}
|
|
239
|
-
|
|
240
|
-
}
|
|
363
|
+
return { kind: "intrinsic", name: ln };
|
|
364
|
+
}
|
|
365
|
+
// CommentNode that's exposed — generic "comment" kind. Marker
|
|
366
|
+
// comments are filtered out by `shouldExposeNode`, so this is rare.
|
|
367
|
+
return { kind: "comment", value: (node as CommentNode).data };
|
|
241
368
|
}
|
|
242
369
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
) {
|
|
248
|
-
|
|
249
|
-
const id = nextId++;
|
|
250
|
-
setEntryId(parent, index, id);
|
|
251
|
-
insertRenderNode(
|
|
252
|
-
id,
|
|
253
|
-
getOrCreateNodeId(parent),
|
|
254
|
-
"text",
|
|
255
|
-
undefined,
|
|
256
|
-
undefined,
|
|
257
|
-
undefined,
|
|
258
|
-
undefined,
|
|
259
|
-
undefined,
|
|
260
|
-
null,
|
|
261
|
-
value,
|
|
262
|
-
);
|
|
370
|
+
// #endregion
|
|
371
|
+
|
|
372
|
+
// #region Component ownership resolution
|
|
373
|
+
|
|
374
|
+
function currentComponentFrame(): ComponentFrame | undefined {
|
|
375
|
+
return componentStack[componentStack.length - 1];
|
|
263
376
|
}
|
|
264
377
|
|
|
265
|
-
function
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
node: RenderedTextTree | PrintHook,
|
|
269
|
-
info: RenderTreeNodeInfo,
|
|
270
|
-
) {
|
|
271
|
-
if (!isDebugEnabled()) return;
|
|
272
|
-
const id = getOrCreateNodeId(node);
|
|
273
|
-
if (info.propsSerialized !== undefined) {
|
|
274
|
-
nodeProps.set(id, info.propsSerialized);
|
|
275
|
-
}
|
|
276
|
-
// Remember the kind and source so cached re-adds preserve them
|
|
277
|
-
nodeKinds.set(node, {
|
|
278
|
-
kind: info.kind,
|
|
279
|
-
name: info.name,
|
|
280
|
-
source: info.source,
|
|
281
|
-
});
|
|
282
|
-
setEntryId(parent, index, id);
|
|
283
|
-
insertRenderNode(
|
|
284
|
-
id,
|
|
285
|
-
getOrCreateNodeId(parent),
|
|
286
|
-
info.kind,
|
|
287
|
-
info.name,
|
|
288
|
-
info.propsSerialized,
|
|
289
|
-
info.source?.fileName,
|
|
290
|
-
info.source?.lineNumber,
|
|
291
|
-
info.source?.columnNumber,
|
|
292
|
-
null,
|
|
293
|
-
undefined,
|
|
294
|
-
);
|
|
378
|
+
function currentContextComponentFrame(): ComponentFrame | undefined {
|
|
379
|
+
const ctx = getContext();
|
|
380
|
+
return ctx ? componentByContextId.get(ctx.id) : undefined;
|
|
295
381
|
}
|
|
296
382
|
|
|
297
|
-
function
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
)
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
if (
|
|
315
|
-
|
|
316
|
-
|
|
383
|
+
function findRootComponentsForAttach(parent: AlloyNode): ComponentFrame[] {
|
|
384
|
+
const frames: ComponentFrame[] = [];
|
|
385
|
+
for (let i = componentStack.length - 1; i >= 0; i--) {
|
|
386
|
+
const frame = componentStack[i];
|
|
387
|
+
if (frame.hostParent === parent) frames.unshift(frame);
|
|
388
|
+
}
|
|
389
|
+
return frames;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function ownerFrameForAttach(
|
|
393
|
+
parentId: number | null,
|
|
394
|
+
inheritedOwner: ComponentFrame | undefined,
|
|
395
|
+
): ComponentFrame | undefined {
|
|
396
|
+
if (inheritedOwner) return inheritedOwner;
|
|
397
|
+
const active = currentComponentFrame();
|
|
398
|
+
if (active) return active;
|
|
399
|
+
const contextFrame = currentContextComponentFrame();
|
|
400
|
+
if (contextFrame) return contextFrame;
|
|
401
|
+
if (parentId !== null) {
|
|
402
|
+
const parentOwner = ownerComponentByNodeId.get(parentId);
|
|
403
|
+
if (parentOwner !== undefined) return componentsById.get(parentOwner);
|
|
404
|
+
}
|
|
405
|
+
return undefined;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function recordComponentRoot(
|
|
409
|
+
frame: ComponentFrame,
|
|
410
|
+
renderNodeId: number,
|
|
411
|
+
): void {
|
|
412
|
+
if (frame.rootSet.has(renderNodeId)) return;
|
|
413
|
+
frame.rootSet.add(renderNodeId);
|
|
414
|
+
const ordinal = frame.roots.length;
|
|
415
|
+
frame.roots.push(renderNodeId);
|
|
416
|
+
let components = rootComponentsByNodeId.get(renderNodeId);
|
|
417
|
+
if (!components) {
|
|
418
|
+
components = new Set();
|
|
419
|
+
rootComponentsByNodeId.set(renderNodeId, components);
|
|
420
|
+
}
|
|
421
|
+
components.add(frame.id);
|
|
422
|
+
insertComponentRoot(frame.id, renderNodeId, ordinal);
|
|
423
|
+
if (ordinal === 0) {
|
|
424
|
+
if (frame.file) {
|
|
425
|
+
fileNodes.set(renderNodeId, frame.file);
|
|
426
|
+
insertOutputFile(frame.file.path, frame.file.filetype, renderNodeId);
|
|
427
|
+
}
|
|
428
|
+
if (frame.directory) {
|
|
429
|
+
directoryNodes.set(renderNodeId, frame.directory);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
if (frame.actions) rerenderActions.set(renderNodeId, frame.actions);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function removeComponentRoot(
|
|
436
|
+
frame: ComponentFrame,
|
|
437
|
+
renderNodeId: number,
|
|
438
|
+
deleteWhenEmpty = true,
|
|
439
|
+
): void {
|
|
440
|
+
if (!frame.rootSet.delete(renderNodeId)) return;
|
|
441
|
+
frame.roots = frame.roots.filter((id) => id !== renderNodeId);
|
|
442
|
+
const components = rootComponentsByNodeId.get(renderNodeId);
|
|
443
|
+
if (components) {
|
|
444
|
+
components.delete(frame.id);
|
|
445
|
+
if (components.size === 0) rootComponentsByNodeId.delete(renderNodeId);
|
|
446
|
+
}
|
|
447
|
+
deleteComponentRoot(frame.id, renderNodeId);
|
|
448
|
+
if (deleteWhenEmpty && frame.rootSet.size === 0) {
|
|
449
|
+
deleteComponentFrame(frame);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function deleteComponentFrame(frame: ComponentFrame): void {
|
|
454
|
+
if (!componentsById.delete(frame.id)) return;
|
|
455
|
+
|
|
456
|
+
const children = childComponentsById.get(frame.id);
|
|
457
|
+
if (children) {
|
|
458
|
+
childComponentsById.delete(frame.id);
|
|
459
|
+
for (const childId of [...children]) {
|
|
460
|
+
const child = componentsById.get(childId);
|
|
461
|
+
if (child) deleteComponentFrame(child);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
for (const rootId of [...frame.roots]) {
|
|
466
|
+
removeComponentRoot(frame, rootId, false);
|
|
317
467
|
}
|
|
318
|
-
insertRenderNode(
|
|
319
|
-
id,
|
|
320
|
-
parentId,
|
|
321
|
-
info.kind,
|
|
322
|
-
info.name,
|
|
323
|
-
info.propsSerialized,
|
|
324
|
-
source?.fileName,
|
|
325
|
-
source?.lineNumber,
|
|
326
|
-
source?.columnNumber,
|
|
327
|
-
null,
|
|
328
|
-
undefined,
|
|
329
|
-
);
|
|
330
468
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
recordCachedSubtreeChildrenRecursively(subtree);
|
|
469
|
+
if (frame.parentComponentId !== null) {
|
|
470
|
+
const siblings = childComponentsById.get(frame.parentComponentId);
|
|
471
|
+
siblings?.delete(frame.id);
|
|
335
472
|
}
|
|
473
|
+
if (
|
|
474
|
+
frame.contextId !== null &&
|
|
475
|
+
componentByContextId.get(frame.contextId)?.id === frame.id
|
|
476
|
+
) {
|
|
477
|
+
componentByContextId.delete(frame.contextId);
|
|
478
|
+
}
|
|
479
|
+
nodeProps.delete(frame.id);
|
|
480
|
+
frame.stopWatch?.();
|
|
481
|
+
deleteComponentInstance(frame.id);
|
|
336
482
|
}
|
|
337
483
|
|
|
338
484
|
/**
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
485
|
+
* Decide whether `attachedParent` is currently part of the live trace
|
|
486
|
+
* tree (rooted at the initialized root), and if so which trace node id
|
|
487
|
+
* should be the new node's parent.
|
|
488
|
+
*
|
|
489
|
+
* Returns `undefined` when `attachedParent` isn't tracked — the caller
|
|
490
|
+
* should defer emission until the subtree is later attached at a
|
|
491
|
+
* tracked location.
|
|
343
492
|
*/
|
|
344
|
-
function
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
// that are no longer in the tree would become orphans.
|
|
352
|
-
clearRenderTreeChildren(node);
|
|
353
|
-
|
|
354
|
-
// Rebuild the entryIds for this node
|
|
355
|
-
const list = getEntryList(node);
|
|
356
|
-
list.length = 0;
|
|
357
|
-
|
|
358
|
-
for (let i = 0; i < node.length; i++) {
|
|
359
|
-
const child = node[i];
|
|
360
|
-
if (typeof child === "string") {
|
|
361
|
-
// Text nodes - re-record them with new IDs
|
|
362
|
-
if (child !== "") {
|
|
363
|
-
const id = nextId++;
|
|
364
|
-
list.push(id);
|
|
365
|
-
idToNode.set(id, child as unknown as RenderedTextTree);
|
|
366
|
-
insertRenderNode(
|
|
367
|
-
id,
|
|
368
|
-
parentId,
|
|
369
|
-
"text",
|
|
370
|
-
undefined,
|
|
371
|
-
undefined,
|
|
372
|
-
undefined,
|
|
373
|
-
undefined,
|
|
374
|
-
undefined,
|
|
375
|
-
null,
|
|
376
|
-
child,
|
|
377
|
-
);
|
|
378
|
-
}
|
|
379
|
-
} else if (Array.isArray(child)) {
|
|
380
|
-
// Nested RenderedTextTree - record and recurse, preserving original kind and source
|
|
381
|
-
const id = getOrCreateNodeId(child);
|
|
382
|
-
list.push(id);
|
|
383
|
-
const savedKind = nodeKinds.get(child);
|
|
384
|
-
insertRenderNode(
|
|
385
|
-
id,
|
|
386
|
-
parentId,
|
|
387
|
-
savedKind?.kind ?? "fragment",
|
|
388
|
-
savedKind?.name,
|
|
389
|
-
undefined,
|
|
390
|
-
savedKind?.source?.fileName,
|
|
391
|
-
savedKind?.source?.lineNumber,
|
|
392
|
-
savedKind?.source?.columnNumber,
|
|
393
|
-
null,
|
|
394
|
-
undefined,
|
|
395
|
-
);
|
|
396
|
-
recordCachedSubtreeChildrenRecursively(child);
|
|
397
|
-
} else if (isPrintHook(child)) {
|
|
398
|
-
// PrintHook - record and recurse into subtree
|
|
399
|
-
const id = getOrCreateNodeId(child);
|
|
400
|
-
list.push(id);
|
|
401
|
-
insertRenderNode(
|
|
402
|
-
id,
|
|
403
|
-
parentId,
|
|
404
|
-
"printHook",
|
|
405
|
-
(child as { name?: string }).name ?? "hook",
|
|
406
|
-
undefined,
|
|
407
|
-
undefined,
|
|
408
|
-
undefined,
|
|
409
|
-
undefined,
|
|
410
|
-
null,
|
|
411
|
-
undefined,
|
|
412
|
-
);
|
|
413
|
-
if (child.subtree) {
|
|
414
|
-
const subtreeId = getOrCreateNodeId(child.subtree);
|
|
415
|
-
const hookList = getEntryList(child as unknown as RenderedTextTree);
|
|
416
|
-
hookList.length = 0;
|
|
417
|
-
hookList.push(subtreeId);
|
|
418
|
-
insertRenderNode(
|
|
419
|
-
subtreeId,
|
|
420
|
-
id,
|
|
421
|
-
"fragment",
|
|
422
|
-
undefined,
|
|
423
|
-
undefined,
|
|
424
|
-
undefined,
|
|
425
|
-
undefined,
|
|
426
|
-
undefined,
|
|
427
|
-
null,
|
|
428
|
-
undefined,
|
|
429
|
-
);
|
|
430
|
-
recordCachedSubtreeChildrenRecursively(child.subtree);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
493
|
+
function resolveParentId(
|
|
494
|
+
node: AlloyNode,
|
|
495
|
+
attachedParent: AlloyNode,
|
|
496
|
+
): number | null | undefined {
|
|
497
|
+
if (tracked.has(attachedParent)) {
|
|
498
|
+
const id = nodeIds.get(attachedParent);
|
|
499
|
+
return id === undefined ? null : id;
|
|
433
500
|
}
|
|
501
|
+
return undefined;
|
|
434
502
|
}
|
|
435
503
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
) {
|
|
440
|
-
if (!isDebugEnabled()) return;
|
|
441
|
-
const id = getOrCreateNodeId(node);
|
|
442
|
-
const previous = nodeProps.get(id);
|
|
443
|
-
if (previous === propsSerialized) return;
|
|
444
|
-
nodeProps.set(id, propsSerialized);
|
|
445
|
-
traceUpdateRenderNodeProps(id, propsSerialized);
|
|
446
|
-
}
|
|
504
|
+
// #endregion
|
|
505
|
+
|
|
506
|
+
// #region Tree-mutation hooks
|
|
447
507
|
|
|
448
|
-
|
|
508
|
+
/**
|
|
509
|
+
* Called after `node` is attached as a child of `parent`. Emits an
|
|
510
|
+
* `added` event for `node` itself (if exposed) and recursively for any
|
|
511
|
+
* descendants it brought with it (move / fragment splice / cached subtree).
|
|
512
|
+
*
|
|
513
|
+
* If `parent` isn't part of the live tree yet, stamps the active component
|
|
514
|
+
* ownership onto `node` and defers emission until the subtree finally attaches.
|
|
515
|
+
*/
|
|
516
|
+
export function nodeAttached(node: AlloyNode, parent: AlloyNode): void {
|
|
449
517
|
if (!isDebugEnabled()) return;
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
518
|
+
markFileDirtyForNode(parent);
|
|
519
|
+
const parentId = resolveParentId(node, parent);
|
|
520
|
+
const ownerFrame = currentComponentFrame() ?? currentContextComponentFrame();
|
|
521
|
+
let rootFrames = findRootComponentsForAttach(parent);
|
|
522
|
+
if (
|
|
523
|
+
rootFrames.length === 0 &&
|
|
524
|
+
ownerFrame !== undefined &&
|
|
525
|
+
parentId !== undefined &&
|
|
526
|
+
(parentId === null ||
|
|
527
|
+
ownerComponentByNodeId.get(parentId) !== ownerFrame.id)
|
|
528
|
+
) {
|
|
529
|
+
rootFrames = [ownerFrame];
|
|
457
530
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
531
|
+
if (parentId === undefined) {
|
|
532
|
+
if (ownerFrame) pendingOwnerComponent.set(node, ownerFrame);
|
|
533
|
+
if (rootFrames.length > 0) pendingRootComponents.set(node, rootFrames);
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
attachWithSelf(node, parentId, ownerFrame, rootFrames);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function attachWithSelf(
|
|
540
|
+
node: AlloyNode,
|
|
541
|
+
parentId: number | null,
|
|
542
|
+
inheritedOwner: ComponentFrame | undefined,
|
|
543
|
+
inheritedRoots: ComponentFrame[],
|
|
544
|
+
): void {
|
|
545
|
+
const pendingOwner = pendingOwnerComponent.get(node);
|
|
546
|
+
if (pendingOwner !== undefined) {
|
|
547
|
+
pendingOwnerComponent.delete(node);
|
|
548
|
+
}
|
|
549
|
+
const pendingRoots = pendingRootComponents.get(node);
|
|
550
|
+
if (pendingRoots !== undefined) {
|
|
551
|
+
pendingRootComponents.delete(node);
|
|
552
|
+
}
|
|
553
|
+
const ownerFrame = ownerFrameForAttach(
|
|
554
|
+
parentId,
|
|
555
|
+
pendingOwner ?? inheritedOwner,
|
|
556
|
+
);
|
|
557
|
+
const rootFrames = pendingRoots ?? inheritedRoots;
|
|
558
|
+
if (!shouldExposeNode(node)) {
|
|
559
|
+
for (let c = node.firstChild; c !== null; c = c.nextSibling) {
|
|
560
|
+
attachWithSelf(c, parentId, ownerFrame, rootFrames);
|
|
561
|
+
}
|
|
466
562
|
return;
|
|
467
563
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
564
|
+
if (tracked.has(node)) {
|
|
565
|
+
// Already eagerly emitted (e.g. an alloy:* wrapper pre-emitted via
|
|
566
|
+
// ensureWrapperHostTracked while inside a thunk body). Don't re-emit;
|
|
567
|
+
// just walk newly-attached children that may not have been seen yet.
|
|
568
|
+
const existingId = nodeIds.get(node)!;
|
|
569
|
+
for (let c = node.firstChild; c !== null; c = c.nextSibling) {
|
|
570
|
+
if (!tracked.has(c)) {
|
|
571
|
+
attachWithSelf(c, existingId, ownerFrame, []);
|
|
572
|
+
}
|
|
476
573
|
}
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
const id = getOrCreateNodeId(node);
|
|
577
|
+
emitAdded(node, id, parentId);
|
|
578
|
+
tracked.add(node);
|
|
579
|
+
if (ownerFrame !== undefined) {
|
|
580
|
+
ownerComponentByNodeId.set(id, ownerFrame.id);
|
|
581
|
+
}
|
|
582
|
+
for (const rootFrame of rootFrames) {
|
|
583
|
+
recordComponentRoot(rootFrame, id);
|
|
584
|
+
}
|
|
585
|
+
for (let c = node.firstChild; c !== null; c = c.nextSibling) {
|
|
586
|
+
if (!tracked.has(c)) attachWithSelf(c, id, ownerFrame, []);
|
|
477
587
|
}
|
|
478
588
|
}
|
|
479
589
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
//
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
if (!
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
590
|
+
function emitAdded(node: AlloyNode, id: number, parentId: number | null): void {
|
|
591
|
+
const cls = classifyNode(node);
|
|
592
|
+
// Stamp metadata so cleanup re-emit (and `error()` stack resolution)
|
|
593
|
+
// can recover identity.
|
|
594
|
+
let entry = nodeKinds.get(id);
|
|
595
|
+
if (!entry) {
|
|
596
|
+
entry = { kind: cls.kind, name: cls.name };
|
|
597
|
+
nodeKinds.set(id, entry);
|
|
598
|
+
} else {
|
|
599
|
+
entry.kind = cls.kind;
|
|
600
|
+
if (cls.name !== undefined) entry.name = cls.name;
|
|
601
|
+
}
|
|
602
|
+
const props = nodeProps.get(id);
|
|
603
|
+
insertRenderNode(
|
|
604
|
+
id,
|
|
605
|
+
parentId,
|
|
606
|
+
cls.kind,
|
|
607
|
+
cls.name,
|
|
608
|
+
props,
|
|
609
|
+
entry.source?.fileName,
|
|
610
|
+
entry.source?.lineNumber,
|
|
611
|
+
entry.source?.columnNumber,
|
|
612
|
+
getContextForNode(node)?.id ?? null,
|
|
613
|
+
cls.value,
|
|
614
|
+
);
|
|
490
615
|
}
|
|
491
616
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
) {
|
|
617
|
+
/**
|
|
618
|
+
* Called after `node` has been detached. Emits `node_removed` for
|
|
619
|
+
* `node` and all of its still-attached descendants in post-order.
|
|
620
|
+
*/
|
|
621
|
+
export function nodeDetached(node: AlloyNode, formerParent: AlloyNode): void {
|
|
497
622
|
if (!isDebugEnabled()) return;
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
fileNodes.set(id, { path, filetype });
|
|
501
|
-
insertOutputFile(path, filetype, id);
|
|
623
|
+
markFileDirtyForNode(formerParent);
|
|
624
|
+
detachRecursive(node);
|
|
502
625
|
}
|
|
503
626
|
|
|
504
|
-
function
|
|
505
|
-
|
|
627
|
+
function detachRecursive(node: AlloyNode): void {
|
|
628
|
+
for (let c = node.firstChild; c !== null; c = c.nextSibling) {
|
|
629
|
+
detachRecursive(c);
|
|
630
|
+
}
|
|
631
|
+
if (!tracked.has(node)) return;
|
|
632
|
+
tracked.delete(node);
|
|
633
|
+
if (!shouldExposeNode(node)) return;
|
|
506
634
|
const id = nodeIds.get(node);
|
|
507
635
|
if (id === undefined) return;
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
636
|
+
emitRemoved(id);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function emitRemoved(id: number): void {
|
|
640
|
+
const componentIds = rootComponentsByNodeId.get(id);
|
|
641
|
+
if (componentIds) {
|
|
642
|
+
for (const componentId of [...componentIds]) {
|
|
643
|
+
const component = componentsById.get(componentId);
|
|
644
|
+
if (component) removeComponentRoot(component, id);
|
|
645
|
+
}
|
|
512
646
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
647
|
+
traceDeleteRenderNode(id);
|
|
648
|
+
rerenderActions.delete(id);
|
|
649
|
+
nodeProps.delete(id);
|
|
650
|
+
nodeKinds.delete(id);
|
|
651
|
+
// Files & directories owned by this node.
|
|
652
|
+
fileNodes.delete(id);
|
|
653
|
+
directoryNodes.delete(id);
|
|
654
|
+
ownerComponentByNodeId.delete(id);
|
|
655
|
+
// Cascade to any descendants currently registered as children of this id.
|
|
656
|
+
const children = renderChildIds.get(id);
|
|
657
|
+
if (children !== undefined) {
|
|
658
|
+
renderChildIds.delete(id);
|
|
659
|
+
for (const childId of children) {
|
|
660
|
+
emitRemoved(childId);
|
|
661
|
+
}
|
|
517
662
|
}
|
|
518
663
|
}
|
|
519
664
|
|
|
520
|
-
//
|
|
521
|
-
|
|
522
|
-
//
|
|
665
|
+
// #endregion
|
|
666
|
+
|
|
667
|
+
// #region Component lifecycle
|
|
668
|
+
|
|
669
|
+
function serializeRenderTreeProps(input: Record<string, unknown> | undefined) {
|
|
670
|
+
return untrack(() => {
|
|
671
|
+
if (!input) return undefined;
|
|
672
|
+
const { children: _children, ...rest } = input;
|
|
673
|
+
const sanitized = sanitizeRecord(rest);
|
|
674
|
+
if (!sanitized) return undefined;
|
|
675
|
+
try {
|
|
676
|
+
return devalue.stringify(sanitized);
|
|
677
|
+
} catch {
|
|
678
|
+
return undefined;
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
}
|
|
523
682
|
|
|
524
|
-
/** Begin tracking a component render. Returns a session to record files/dirs and dispose watchers. */
|
|
525
683
|
export function beginComponent(
|
|
526
684
|
options: BeginComponentOptions,
|
|
527
685
|
): ComponentDebugSession {
|
|
528
|
-
const {
|
|
529
|
-
parent,
|
|
530
|
-
index,
|
|
531
|
-
node,
|
|
532
|
-
component,
|
|
533
|
-
propsSource,
|
|
534
|
-
source,
|
|
535
|
-
isExisting,
|
|
536
|
-
actions,
|
|
537
|
-
} = options;
|
|
538
|
-
|
|
539
686
|
if (!isDebugEnabled()) {
|
|
540
687
|
return {
|
|
541
688
|
recordDirectory() {},
|
|
@@ -544,133 +691,142 @@ export function beginComponent(
|
|
|
544
691
|
};
|
|
545
692
|
}
|
|
546
693
|
|
|
694
|
+
const { component, propsSource, source, parent, actions } = options;
|
|
695
|
+
|
|
547
696
|
return untrack(() => {
|
|
548
697
|
let componentName = component.component.name;
|
|
549
698
|
if (componentName === "Provider") {
|
|
550
|
-
const contextName = (component.component as
|
|
551
|
-
|
|
552
|
-
| undefined;
|
|
699
|
+
const contextName = (component.component as { contextName?: string })
|
|
700
|
+
.contextName;
|
|
553
701
|
if (contextName) {
|
|
554
702
|
componentName = `Context ${contextName}`;
|
|
555
703
|
}
|
|
556
704
|
}
|
|
557
705
|
const propsSerialized = serializeRenderTreeProps(propsSource);
|
|
558
706
|
const resolvedSource = resolveComponentSource(source);
|
|
559
|
-
if (isExisting) {
|
|
560
|
-
clearRenderTreeChildren(node);
|
|
561
|
-
} else {
|
|
562
|
-
recordNodeAdded(parent, index, node, {
|
|
563
|
-
kind: "component",
|
|
564
|
-
name: componentName,
|
|
565
|
-
propsSerialized,
|
|
566
|
-
source: resolvedSource,
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
707
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
708
|
+
const id = nextId++;
|
|
709
|
+
const ctx = getContext();
|
|
710
|
+
const parentComponent =
|
|
711
|
+
componentStack[componentStack.length - 1] ??
|
|
712
|
+
(ctx?.owner ? componentByContextId.get(ctx.owner.id) : undefined);
|
|
713
|
+
const frame: ComponentFrame = {
|
|
714
|
+
id,
|
|
715
|
+
parentComponentId: parentComponent?.id ?? null,
|
|
716
|
+
hostParent: parent,
|
|
717
|
+
name: componentName,
|
|
718
|
+
propsSerialized,
|
|
719
|
+
source: resolvedSource,
|
|
720
|
+
contextId: ctx?.id ?? null,
|
|
721
|
+
roots: [],
|
|
722
|
+
rootSet: new Set(),
|
|
723
|
+
actions,
|
|
724
|
+
};
|
|
725
|
+
componentsById.set(id, frame);
|
|
726
|
+
if (frame.parentComponentId !== null) {
|
|
727
|
+
let children = childComponentsById.get(frame.parentComponentId);
|
|
728
|
+
if (!children) {
|
|
729
|
+
children = new Set();
|
|
730
|
+
childComponentsById.set(frame.parentComponentId, children);
|
|
575
731
|
}
|
|
732
|
+
children.add(id);
|
|
733
|
+
}
|
|
734
|
+
componentStack.push(frame);
|
|
735
|
+
if (ctx) componentByContextId.set(ctx.id, frame);
|
|
736
|
+
if (propsSerialized !== undefined) nodeProps.set(id, propsSerialized);
|
|
737
|
+
insertComponentInstance(
|
|
738
|
+
id,
|
|
739
|
+
frame.parentComponentId,
|
|
740
|
+
componentName,
|
|
741
|
+
propsSerialized,
|
|
742
|
+
resolvedSource?.fileName,
|
|
743
|
+
resolvedSource?.lineNumber,
|
|
744
|
+
resolvedSource?.columnNumber,
|
|
745
|
+
frame.contextId,
|
|
746
|
+
);
|
|
747
|
+
if (isTraceEnabled() && ctx) {
|
|
748
|
+
updateEffectComponentByContext(ctx.id, componentName);
|
|
576
749
|
}
|
|
577
|
-
recordNodePropsUpdated(node, propsSerialized);
|
|
578
|
-
registerRenderNodeActions(node, actions);
|
|
579
750
|
|
|
751
|
+
// Watch reactive props and re-emit on change.
|
|
580
752
|
let stopWatch: (() => void) | undefined;
|
|
581
753
|
if (propsSource) {
|
|
582
|
-
const propKeys = Object.keys(propsSource).filter(
|
|
583
|
-
(key) => key !== "children",
|
|
584
|
-
);
|
|
754
|
+
const propKeys = Object.keys(propsSource).filter((k) => k !== "children");
|
|
585
755
|
if (propKeys.length > 0) {
|
|
586
756
|
stopWatch = watch(
|
|
587
|
-
() => propKeys.map((
|
|
757
|
+
() => propKeys.map((k) => propsSource[k]),
|
|
588
758
|
() => {
|
|
589
|
-
const
|
|
590
|
-
|
|
759
|
+
const next = serializeRenderTreeProps(propsSource);
|
|
760
|
+
const previous = nodeProps.get(id);
|
|
761
|
+
if (previous === next) return;
|
|
762
|
+
nodeProps.set(id, next);
|
|
763
|
+
updateComponentInstanceProps(id, next);
|
|
591
764
|
},
|
|
592
765
|
);
|
|
593
766
|
}
|
|
594
767
|
}
|
|
768
|
+
frame.stopWatch = stopWatch;
|
|
595
769
|
|
|
770
|
+
let disposed = false;
|
|
596
771
|
return {
|
|
597
772
|
recordDirectory(path: string) {
|
|
598
|
-
|
|
773
|
+
if (frame.directory) return;
|
|
774
|
+
frame.directory = { path };
|
|
775
|
+
insertDirectory(path);
|
|
599
776
|
},
|
|
600
777
|
recordFile(path: string, filetype: string) {
|
|
601
|
-
|
|
778
|
+
if (frame.file) return;
|
|
779
|
+
frame.file = { path, filetype };
|
|
602
780
|
},
|
|
603
781
|
dispose() {
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
782
|
+
if (disposed) return;
|
|
783
|
+
disposed = true;
|
|
784
|
+
for (let i = componentStack.length - 1; i >= 0; i--) {
|
|
785
|
+
if (componentStack[i].id === id) {
|
|
786
|
+
componentStack.splice(i, 1);
|
|
787
|
+
break;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
607
790
|
},
|
|
608
791
|
};
|
|
609
792
|
});
|
|
610
793
|
}
|
|
611
794
|
|
|
612
|
-
|
|
613
|
-
parent: RenderedTextTree,
|
|
614
|
-
node: RenderedTextTree,
|
|
615
|
-
) {
|
|
616
|
-
recordSubtreeAdded(parent, node, { kind: "customContext" });
|
|
617
|
-
}
|
|
795
|
+
// #endregion
|
|
618
796
|
|
|
619
|
-
|
|
620
|
-
parent: RenderedTextTree,
|
|
621
|
-
index: number,
|
|
622
|
-
hook: PrintHook,
|
|
623
|
-
name: string,
|
|
624
|
-
subtree?: RenderedTextTree,
|
|
625
|
-
) {
|
|
626
|
-
recordNodeAdded(parent, index, hook, { kind: "printHook", name });
|
|
627
|
-
if (subtree) {
|
|
628
|
-
recordSubtreeAdded(hook, subtree);
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
export function appendFragmentChild(
|
|
633
|
-
parent: RenderedTextTree,
|
|
634
|
-
child: RenderedTextTree,
|
|
635
|
-
) {
|
|
636
|
-
recordSubtreeAdded(parent, child, { kind: "fragment" });
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
export function appendTextNode(
|
|
640
|
-
parent: RenderedTextTree,
|
|
641
|
-
index: number,
|
|
642
|
-
value: string,
|
|
643
|
-
) {
|
|
644
|
-
recordTextNode(parent, index, value);
|
|
645
|
-
}
|
|
797
|
+
// #region Devtools rerender bridge
|
|
646
798
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
799
|
+
function ensureDevtoolsHandler() {
|
|
800
|
+
if (handlerRegistered || !isDevtoolsEnabled()) return;
|
|
801
|
+
handlerRegistered = true;
|
|
802
|
+
registerDevtoolsMessageHandler((message) => {
|
|
803
|
+
if (
|
|
804
|
+
message.type !== "render:rerender" &&
|
|
805
|
+
message.type !== "render:rerenderAndBreak"
|
|
806
|
+
) {
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
const rawId = (message as { id?: unknown }).id;
|
|
810
|
+
if (typeof rawId !== "number" && typeof rawId !== "string") return;
|
|
811
|
+
const id = Number(rawId);
|
|
812
|
+
if (!Number.isFinite(id)) return;
|
|
813
|
+
const actions = rerenderActions.get(id);
|
|
814
|
+
if (!actions) return;
|
|
815
|
+
if (message.type === "render:rerender") {
|
|
816
|
+
actions.rerender();
|
|
817
|
+
} else {
|
|
818
|
+
actions.rerenderAndBreak();
|
|
819
|
+
}
|
|
820
|
+
void flushJobsAsync();
|
|
821
|
+
});
|
|
657
822
|
}
|
|
658
823
|
|
|
659
|
-
|
|
824
|
+
// #endregion
|
|
660
825
|
|
|
661
|
-
|
|
662
|
-
name: string;
|
|
663
|
-
message: string;
|
|
664
|
-
stack?: string;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
/** Render error stack entry with optional runtime props (extends protocol type). */
|
|
668
|
-
export interface RenderErrorStackEntry extends ProtocolRenderErrorStackEntry {
|
|
669
|
-
props?: Record<string, unknown> | undefined;
|
|
670
|
-
}
|
|
826
|
+
// #region Errors
|
|
671
827
|
|
|
672
828
|
export function error(
|
|
673
|
-
|
|
829
|
+
err: RenderErrorInfo,
|
|
674
830
|
componentStack: RenderErrorStackEntry[],
|
|
675
831
|
) {
|
|
676
832
|
if (!isDebugEnabled()) return;
|
|
@@ -685,26 +841,17 @@ export function error(
|
|
|
685
841
|
logDevtoolsMessage({
|
|
686
842
|
type: "render:error" as const,
|
|
687
843
|
id: nextErrorId++,
|
|
688
|
-
name:
|
|
689
|
-
message:
|
|
690
|
-
stack:
|
|
844
|
+
name: err.name,
|
|
845
|
+
message: err.message,
|
|
846
|
+
stack: err.stack,
|
|
691
847
|
componentStack: serializedStack,
|
|
692
848
|
});
|
|
693
|
-
|
|
694
849
|
insertRenderError(
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
850
|
+
err.name,
|
|
851
|
+
err.message,
|
|
852
|
+
err.stack,
|
|
698
853
|
JSON.stringify(serializedStack),
|
|
699
854
|
);
|
|
700
855
|
}
|
|
701
856
|
|
|
702
|
-
|
|
703
|
-
logDevtoolsMessage({ type: "render:complete" });
|
|
704
|
-
notifyRenderComplete();
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
export function flushJobsComplete() {
|
|
708
|
-
logDevtoolsMessage({ type: "flushJobs:complete" });
|
|
709
|
-
notifyFlushComplete();
|
|
710
|
-
}
|
|
857
|
+
// #endregion
|