From 5834b9036376b60b4f705121a0ef997d420b7264 Mon Sep 17 00:00:00 2001 From: Jake Lazaroff Date: Fri, 1 Aug 2025 15:15:20 -0400 Subject: [PATCH] Add noNullProps option --- packages/babel-plugin-htm/README.md | 11 +++++++++++ packages/babel-plugin-htm/index.mjs | 4 +++- test/babel.test.mjs | 15 +++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/babel-plugin-htm/README.md b/packages/babel-plugin-htm/README.md index a2bb474..d0e4359 100644 --- a/packages/babel-plugin-htm/README.md +++ b/packages/babel-plugin-htm/README.md @@ -159,6 +159,17 @@ html`
hello ${you}
` ] } ``` +### `noNullProps` _(experimental)_ + +Setting `noNullProps` to `true` passes empty object literals rather than `null` literals when a tag has no props: + +```js +// input: +html`
` +// output: +h('div', {}) +``` + [htm]: https://github.com/developit/htm [Babel docs]: https://babeljs.io/docs/en/babel-plugin-transform-react-jsx#pragma diff --git a/packages/babel-plugin-htm/index.mjs b/packages/babel-plugin-htm/index.mjs index 9e00c37..eb3958e 100644 --- a/packages/babel-plugin-htm/index.mjs +++ b/packages/babel-plugin-htm/index.mjs @@ -10,12 +10,14 @@ import { build, treeify } from '../../src/build.mjs'; * @param {boolean} [options.useBuiltIns=false] Use the native Object.assign instead of trying to polyfill it. * @param {boolean} [options.useNativeSpread=false] Use the native { ...a, ...b } syntax for prop spreads. * @param {boolean} [options.variableArity=true] If `false`, always passes exactly 3 arguments to the pragma function. + * @param {boolean} [options.noNullProps=false] If `true`, passes an empty object instead of null when there are no props. */ export default function htmBabelPlugin({ types: t }, options = {}) { const pragmaString = options.pragma===false ? false : options.pragma || 'h'; const pragma = pragmaString===false ? false : dottedIdentifier(pragmaString); const useBuiltIns = options.useBuiltIns; const useNativeSpread = options.useNativeSpread; + const noNullProps = options.noNullProps; const inlineVNodes = options.monomorphic || pragma===false; const importDeclaration = pragmaImport(options.import || false); @@ -123,7 +125,7 @@ export default function htmBabelPlugin({ types: t }, options = {}) { function spreadNode(args, state) { if (args.length === 0) { - return t.nullLiteral(); + return noNullProps ? t.objectExpression([]) : t.nullLiteral(); } if (args.length > 0 && t.isNode(args[0])) { args.unshift({}); diff --git a/test/babel.test.mjs b/test/babel.test.mjs index c7ad833..cd771e8 100644 --- a/test/babel.test.mjs +++ b/test/babel.test.mjs @@ -360,6 +360,21 @@ describe('htm/babel', () => { }); }); + describe('{noNullProps:true}', () => { + test('should use empty objects instead of null props', () => { + expect( + transform('html`
`;', { + ...options, + plugins: [ + [htmBabelPlugin, { + noNullProps: true + }] + ] + }).code + ).toBe(`h("div",{});`); + }); + }); + describe('{import:Object}', () => { test('should add import', () => { expect(