From ecd5127d1efc0a3d17fba05e2fe85743132d3b7f Mon Sep 17 00:00:00 2001 From: Claudia Meadows Date: Mon, 11 Aug 2025 10:55:14 -0700 Subject: [PATCH 1/2] Fix the error message selection condition I have no idea how this eluded me when I first wrote this. --- render/vnode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/vnode.js b/render/vnode.js index 215980f21..696295309 100644 --- a/render/vnode.js +++ b/render/vnode.js @@ -19,7 +19,7 @@ Vnode.normalizeChildren = function(input) { for (var i = 1; i < input.length; i++) { if ((input[i] != null && input[i].key != null) !== isKeyed) { throw new TypeError( - isKeyed && (input[i] != null || typeof input[i] === "boolean") + isKeyed && (input[i] != null && typeof input[i] !== "boolean") ? "In fragments, vnodes must either all have keys or none have keys. You may wish to consider using an explicit keyed empty fragment, m.fragment({key: ...}), instead of a hole." : "In fragments, vnodes must either all have keys or none have keys." ) From f396bf134b36ff25c72aa55df289ce1121cfe328 Mon Sep 17 00:00:00 2001 From: Claudia Meadows Date: Mon, 11 Aug 2025 12:59:11 -0700 Subject: [PATCH 2/2] Correct the fix, add tests just to make sure behavior was correct from the beginning --- render/tests/test-normalizeChildren.js | 64 ++++++++++++++++++++++++++ render/vnode.js | 2 +- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/render/tests/test-normalizeChildren.js b/render/tests/test-normalizeChildren.js index a7cd5ba5b..68fab4c60 100644 --- a/render/tests/test-normalizeChildren.js +++ b/render/tests/test-normalizeChildren.js @@ -53,4 +53,68 @@ o.spec("normalizeChildren", function() { ]) }).throws(TypeError) }) + o("disallows mixed keys, ending with null", function() { + o(function() { + Vnode.normalizeChildren([ + {key: 1}, + null, + ]) + }).throws(TypeError) + }) + o("disallows mixed keys, starting with null", function() { + o(function() { + Vnode.normalizeChildren([ + {data: 1}, + null, + ]) + }).throws(TypeError) + }) + o("disallows mixed keys, ending with undefined", function() { + o(function() { + Vnode.normalizeChildren([ + {key: 1}, + undefined, + ]) + }).throws(TypeError) + }) + o("disallows mixed keys, starting with undefined", function() { + o(function() { + Vnode.normalizeChildren([ + {data: 1}, + undefined, + ]) + }).throws(TypeError) + }) + o("disallows mixed keys, ending with false", function() { + o(function() { + Vnode.normalizeChildren([ + {key: 1}, + false, + ]) + }).throws(TypeError) + }) + o("disallows mixed keys, starting with false", function() { + o(function() { + Vnode.normalizeChildren([ + {data: 1}, + false, + ]) + }).throws(TypeError) + }) + o("disallows mixed keys, ending with true", function() { + o(function() { + Vnode.normalizeChildren([ + {key: 1}, + true, + ]) + }).throws(TypeError) + }) + o("disallows mixed keys, starting with true", function() { + o(function() { + Vnode.normalizeChildren([ + {data: 1}, + true, + ]) + }).throws(TypeError) + }) }) diff --git a/render/vnode.js b/render/vnode.js index 696295309..821b21bb2 100644 --- a/render/vnode.js +++ b/render/vnode.js @@ -19,7 +19,7 @@ Vnode.normalizeChildren = function(input) { for (var i = 1; i < input.length; i++) { if ((input[i] != null && input[i].key != null) !== isKeyed) { throw new TypeError( - isKeyed && (input[i] != null && typeof input[i] !== "boolean") + isKeyed && (input[i] == null || typeof input[i] === "boolean") ? "In fragments, vnodes must either all have keys or none have keys. You may wish to consider using an explicit keyed empty fragment, m.fragment({key: ...}), instead of a hole." : "In fragments, vnodes must either all have keys or none have keys." )