From 659f4e0ddcf21710b1797ab823a1187c744c8d2d Mon Sep 17 00:00:00 2001 From: David Bushong Date: Sat, 21 Dec 2019 21:10:53 -0800 Subject: [PATCH] feat: terse Fragment syntax --- README.md | 19 +++++++++++++++++++ lib/phy.js | 10 +++++++++- lib/typedefs.d.ts | 1 + test/phy.test.js | 16 ++++++++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7509e78..12d8062 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,25 @@ class SomeComponent extends Component { module.exports = SomeComponent; ``` +You can create a Fragment tersely by passing only one argument: an +array of other nodes or strings: + +```js +const frag = h([ + h('div', 'one'), + h('div', 'two'), + 'three' +]); + +// is equivalent to: + +const { Fragment } = require('preact'); +const frag = h(Fragment, [ + h('div', 'one'), + h('div', 'two'), + 'three' +]); + ## Optional Tag Helpers At the cost of a modestly larger import and slight function call overhead, diff --git a/lib/phy.js b/lib/phy.js index b6fcb41..7c1bce6 100644 --- a/lib/phy.js +++ b/lib/phy.js @@ -69,11 +69,19 @@ function isAttributes(obj) { // possible arg combos (eliding createElement) (kids* = 0-or-more-kids): /** * @param {typeof preact.createElement} createElement - * @param {string | ComponentType} selector + * @param {string | ComponentType | (string | ComponentType)[]} selector * @param {Readonly>} [attrs] * @param {ComponentChildren[]} kids + * @return {preact.VNode} */ function h(createElement, selector, attrs, ...kids) { + if (Array.isArray(selector)) { + if (attrs != null || kids.length > 0) { + throw new Error('Fragment mode does not accept attrs or kids'); + } + return h(createElement, preact.Fragment, undefined, selector); + } + if (attrs) { if (!isAttributes(attrs)) { kids.unshift(/** @type {ComponentChildren} */ (attrs)); diff --git a/lib/typedefs.d.ts b/lib/typedefs.d.ts index 50f5c13..d2c3f58 100644 --- a/lib/typedefs.d.ts +++ b/lib/typedefs.d.ts @@ -3,6 +3,7 @@ import p from 'preact'; declare function isPreactNode(obj: object): boolean; +declare function phy(fragments: (string | ComponentType)[]): VNode; declare function phy( selectorOrComp: string | ComponentType, ...kids: ComponentChildren[] diff --git a/test/phy.test.js b/test/phy.test.js index 0a53f22..f71c27d 100644 --- a/test/phy.test.js +++ b/test/phy.test.js @@ -93,6 +93,11 @@ const tests = [ h('litter-box', {}, [h(Fragment, {}, ['kitten', h('toy', 'mouse')])]), 'kittenmouse', ], + [ + 'Fragment terse usage', + h(['kitten', h('toy', 'mouse')]), + 'kittenmouse', + ], ]; describe('phy', () => { @@ -102,6 +107,17 @@ describe('phy', () => { assert.equal(test[2], render(test[1])); }); }); + + it('throws on surplus fragment args', () => { + assert.include( + 'Fragment mode', + assert.throws(() => h(['a', 'b'], { some: 'attr' })).message + ); + assert.include( + 'Fragment mode', + assert.throws(() => h(['a', 'b'], undefined, 'kid')).message + ); + }); }); describe('isVNode', () => {