diff --git a/.changeset/witty-timers-reply.md b/.changeset/witty-timers-reply.md new file mode 100644 index 000000000..b312716f6 --- /dev/null +++ b/.changeset/witty-timers-reply.md @@ -0,0 +1,5 @@ +--- +'@kitajs/html': patch +--- + +Fixed primitives rendering to equal react output diff --git a/packages/html/index.js b/packages/html/index.js index 5f8f53bba..0075e2680 100644 --- a/packages/html/index.js +++ b/packages/html/index.js @@ -386,10 +386,13 @@ function contentsToString(contents, escape) { switch (typeof content) { case 'string': case 'number': - case 'boolean': + // Bigint is the only case where it differs from React. + // where React renders a empty string and we render the whole number. case 'bigint': result += content; continue; + case 'boolean': + continue; } if (!content) { @@ -402,11 +405,15 @@ function contentsToString(contents, escape) { continue; } - // @ts-ignore - Type instantiation is excessively deep and possibly infinite. - return Promise.all(contents.slice(index)).then(function resolveContents(resolved) { - resolved.unshift(result); - return contentsToString(resolved, escape); - }); + if (typeof content.then === 'function') { + // @ts-ignore - Type instantiation is excessively deep and possibly infinite. + return Promise.all(contents.slice(index)).then(function resolveContents(resolved) { + resolved.unshift(result); + return contentsToString(resolved, escape); + }); + } + + throw new Error('Objects are not valid as a KitaJSX child'); } // escapeHtml is faster with longer strings, that's @@ -427,11 +434,13 @@ function contentToString(content, safe) { switch (typeof content) { case 'string': return safe ? escapeHtml(content) : content; - case 'number': - case 'boolean': + // Bigint is the only case where it differs from React. + // where React renders a empty string and we render the whole number. case 'bigint': return content.toString(); + case 'boolean': + return ''; } if (!content) { @@ -442,9 +451,13 @@ function contentToString(content, safe) { return contentsToString(content, safe); } - return content.then(function resolveContent(resolved) { - return contentToString(resolved, safe); - }); + if (typeof content.then === 'function') { + return content.then(function resolveContent(resolved) { + return contentToString(resolved, safe); + }); + } + + throw new Error('Objects are not valid as a KitaJSX child'); } /** diff --git a/packages/html/jsx.d.ts b/packages/html/jsx.d.ts index 882d4bb56..cc148b2e8 100644 --- a/packages/html/jsx.d.ts +++ b/packages/html/jsx.d.ts @@ -855,73 +855,73 @@ declare namespace JSX { // All the WAI-ARIA 1.1 role attribute values from https://www.w3.org/TR/wai-aria-1.1/#role_definitions type AriaRole = - | "alert" - | "alertdialog" - | "application" - | "article" - | "banner" - | "button" - | "cell" - | "checkbox" - | "columnheader" - | "combobox" - | "complementary" - | "contentinfo" - | "definition" - | "dialog" - | "directory" - | "document" - | "feed" - | "figure" - | "form" - | "grid" - | "gridcell" - | "group" - | "heading" - | "img" - | "link" - | "list" - | "listbox" - | "listitem" - | "log" - | "main" - | "marquee" - | "math" - | "menu" - | "menubar" - | "menuitem" - | "menuitemcheckbox" - | "menuitemradio" - | "navigation" - | "none" - | "note" - | "option" - | "presentation" - | "progressbar" - | "radio" - | "radiogroup" - | "region" - | "row" - | "rowgroup" - | "rowheader" - | "scrollbar" - | "search" - | "searchbox" - | "separator" - | "slider" - | "spinbutton" - | "status" - | "switch" - | "tab" - | "table" - | "tablist" - | "tabpanel" - | "term" - | "textbox" - | "timer" - | "toolbar" - | "tooltip" - | "tree" - | "treegrid" - | "treeitem" + | 'alert' + | 'alertdialog' + | 'application' + | 'article' + | 'banner' + | 'button' + | 'cell' + | 'checkbox' + | 'columnheader' + | 'combobox' + | 'complementary' + | 'contentinfo' + | 'definition' + | 'dialog' + | 'directory' + | 'document' + | 'feed' + | 'figure' + | 'form' + | 'grid' + | 'gridcell' + | 'group' + | 'heading' + | 'img' + | 'link' + | 'list' + | 'listbox' + | 'listitem' + | 'log' + | 'main' + | 'marquee' + | 'math' + | 'menu' + | 'menubar' + | 'menuitem' + | 'menuitemcheckbox' + | 'menuitemradio' + | 'navigation' + | 'none' + | 'note' + | 'option' + | 'presentation' + | 'progressbar' + | 'radio' + | 'radiogroup' + | 'region' + | 'row' + | 'rowgroup' + | 'rowheader' + | 'scrollbar' + | 'search' + | 'searchbox' + | 'separator' + | 'slider' + | 'spinbutton' + | 'status' + | 'switch' + | 'tab' + | 'table' + | 'tablist' + | 'tabpanel' + | 'term' + | 'textbox' + | 'timer' + | 'toolbar' + | 'tooltip' + | 'tree' + | 'treegrid' + | 'treeitem' | (string & {}); diff --git a/packages/html/test/attributes.test.tsx b/packages/html/test/attributes.test.tsx index 7a7ca8701..da0b72735 100644 --- a/packages/html/test/attributes.test.tsx +++ b/packages/html/test/attributes.test.tsx @@ -48,6 +48,15 @@ describe('Attributes', () => { assert.equal('
',
); }); + test('Undefined', () => { + assert.equal( + '
', + + ); + }); + test('Dates & Objects', () => { const date = new Date(); assert.equal(, ``); diff --git a/packages/html/test/misc.test.tsx b/packages/html/test/misc.test.tsx index ef0503562..57e401c22 100644 --- a/packages/html/test/misc.test.tsx +++ b/packages/html/test/misc.test.tsx @@ -19,35 +19,110 @@ describe('Miscellaneous', () => { assert.equal(
, '
'); }); - test('Falsy values', () => { + test('Primitive values renders exactly like React', () => { + assert.equal(
{false}
,
); + assert.equal(
{null}
,
); + assert.equal(
{undefined}
,
); + assert.equal(
{0}
,
0
); + assert.equal(
{432}
,
432
); + assert.equal(
{NaN}
,
NaN
); + assert.equal(
{true}
,
); + assert.equal(
{Infinity}
,
Infinity
); + assert.equal(
{-Infinity}
,
-Infinity
); + assert.equal(
{[1, 2, 3]}
,
123
); + assert.equal( - <> +
+ {false} {false} +
, +
+ ); + assert.equal( +
{null} + {null} +
, +
+ ); + assert.equal( +
+ {undefined} {undefined} +
, +
+ ); + assert.equal( +
{0} + {0} +
, +
00
+ ); + assert.equal( +
+ {432} + {432} +
, +
432432
+ ); + assert.equal( +
+ {NaN} {NaN} +
, +
NaNNaN
+ ); + assert.equal( +
{true} + {true} +
, +
+ ); + assert.equal( +
+ {Infinity} {Infinity} +
, +
InfinityInfinity
+ ); + assert.equal( +
{-Infinity} - {123n} - , - <> - <>false - <> - <> - <>0 - <>NaN - <>true - <>Infinity - <>-Infinity - <>123 - + {-Infinity} +
, +
-Infinity-Infinity
); - assert.equal( - , - '
' +
+ {[1, 2, 3]} + {[1, 2, 3]} +
, +
123123
+ ); + + // Bigint is the only case where it differs from React. + // where React renders a empty string and we render the whole number. + assert.equal(
{123456789123456789n}
,
123456789123456789
); + assert.equal(<>{123456789123456789n}, <>123456789123456789); + }); + + test('Rendering objects throws', () => { + assert.throws( + //@ts-expect-error - should warn about invalid child + () =>
{{}}
, + /Objects are not valid as a KitaJSX child/ + ); + + assert.throws( + //@ts-expect-error - should warn about invalid child + () => ( +
+ {{}} {{}} +
+ ), + /Objects are not valid as a KitaJSX child/ ); });