diff --git a/src/defhsx.lisp b/src/defhsx.lisp index 9eb5fd4..7a2cc82 100644 --- a/src/defhsx.lisp +++ b/src/defhsx.lisp @@ -9,23 +9,31 @@ #:defcomp)) (in-package #:hsx/defhsx) +(defun %create-element (type &rest body) + (multiple-value-bind (props children) + (parse-body body) + (create-element type props children))) + (defmacro defhsx (name element-type) `(defmacro ,name (&body body) - (multiple-value-bind (props children) - (parse-body body) - `(create-element ,',element-type (list ,@props) ,@children)))) + `(%create-element ,',element-type ,@body))) (defun parse-body (body) - (if (keywordp (first body)) - (loop :for thing :on body :by #'cddr - :for (k v) := thing - :when (and (keywordp k) v) - :append (list k v) :into props - :when (not (keywordp k)) - :return (values props thing) - :finally (return (values props nil))) - (values nil body))) - + (cond (; plist + (and (listp (first body)) + (keywordp (first (first body)))) + (values (first body) (rest body))) + (; inline-plist + (keywordp (first body)) + (loop :for thing :on body :by #'cddr + :for (k v) := thing + :when (and (keywordp k) v) + :append (list k v) :into props + :when (not (keywordp k)) + :return (values props thing) + :finally (return (values props nil)))) + (t (values nil body)))) + (defmacro deftag (name) `(eval-when (:compile-toplevel :load-toplevel :execute) (defhsx ,name ,(make-keyword name)))) diff --git a/src/element.lisp b/src/element.lisp index b563716..65b10b4 100644 --- a/src/element.lisp +++ b/src/element.lisp @@ -1,6 +1,11 @@ (defpackage #:hsx/element (:use #:cl) - (:export #:create-element + (:export #:element + #:tag + #:html-tag + #:fragment + #:component + #:create-element #:element-type #:element-props #:element-children @@ -31,7 +36,7 @@ ;;;; factory -(defun create-element (type props &rest children) +(defun create-element (type props children) (make-instance (cond ((functionp type) 'component) ((eq type :<>) 'fragment) ((eq type :html) 'html-tag) diff --git a/src/hsx.lisp b/src/hsx.lisp index 9c814e5..599796a 100644 --- a/src/hsx.lisp +++ b/src/hsx.lisp @@ -10,7 +10,9 @@ (defun find-builtin-symbols (node) (if (atom node) - (or (find-symbol (string node) :hsx/builtin) + (or (and (symbolp node) + (not (keywordp node)) + (find-symbol (string node) :hsx/builtin)) node) (cons (find-builtin-symbols (car node)) (mapcar (lambda (n) diff --git a/tests/defhsx.lisp b/tests/defhsx.lisp index dca1184..513354f 100644 --- a/tests/defhsx.lisp +++ b/tests/defhsx.lisp @@ -4,71 +4,49 @@ #:hsx/defhsx #:hsx/builtin) (:import-from #:hsx/element - #:create-element)) + #:element-props + #:element-children)) (in-package #:hsx-test/defhsx) (def-suite defhsx-test) (in-suite defhsx-test) (test empty-hsx - (is (equal '(create-element - :div - (list)) - (macroexpand-1 - '(div))))) + (let ((elm (div))) + (is (null (element-props elm))) + (is (null (element-children elm))))) -(test hsx-with-props - (is (equal '(create-element - :div - (list :prop1 "value1" :prop2 "value2")) - (macroexpand-1 - '(div :prop1 "value1" :prop2 "value2"))))) +(test hsx-with-static-props + (let ((elm (div :prop1 "value1" :prop2 "value2"))) + (is (equal '(:prop1 "value1" :prop2 "value2") + (element-props elm))) + (is (null (element-children elm))))) + +(test hsx-with-dynamic-props + (let* ((props '(:prop1 "value1" :prop2 "value2")) + (elm (div props))) + (is (equal props (element-props elm))) + (is (null (element-children elm))))) (test hsx-with-children - (is (equal '(create-element - :div - (list) + (let ((elm (div "child1" - "child2") - (macroexpand-1 - '(div - "child1" - "child2"))))) + "child2"))) + (is (null (element-props elm))) + (is (equal (list "child1" "child2") (element-children elm))))) -(test hsx-with-props-and-children - (is (equal '(create-element - :div - (list :prop1 "value1" :prop2 "value2") +(test hsx-with-static-props-and-children + (let ((elm (div :prop1 "value1" :prop2 "value2" "child1" - "child2") - (macroexpand-1 - '(div :prop1 "value1" :prop2 "value2" - "child1" - "child2"))))) + "child2"))) + (is (equal '(:prop1 "value1" :prop2 "value2") + (element-props elm))) + (is (equal (list "child1" "child2") (element-children elm))))) -(deftag custom) - -(test hsx-for-custom-tag - (is (equal '(create-element - :custom - (list :prop1 "value1" :prop2 "value2") +(test hsx-with-dynamic-props-and-children + (let* ((props '(:prop1 "value1" :prop2 "value2")) + (elm (div props "child1" - "child2") - (macroexpand-1 - '(custom :prop1 "value1" :prop2 "value2" - "child1" - "child2"))))) - -(defcomp comp (&key prop1 prop2 children) - (declare (ignore prop1 prop2 children))) - -(test hsx-for-component - (is (equal '(create-element - (fdefinition '%comp) - (list :prop1 "value1" :prop2 "value2") - "child1" - "child2") - (macroexpand-1 - '(comp :prop1 "value1" :prop2 "value2" - "child1" - "child2"))))) + "child2"))) + (is (equal props (element-props elm))) + (is (equal (list "child1" "child2") (element-children elm))))) diff --git a/tests/element.lisp b/tests/element.lisp index 9104639..71a04ae 100644 --- a/tests/element.lisp +++ b/tests/element.lisp @@ -7,72 +7,58 @@ (def-suite element-test) (in-suite element-test) -(test tag - (let ((elm (create-element :p - '(:class "red") - "Hello," - "World"))) - (is (eq :p (element-type elm))) - (is (equal '(:class "red") (element-props elm))) - (is (equal (list "Hello," "World") (element-children elm))))) +(test element-class + (is (typep (create-element :div nil nil) 'tag)) + (is (typep (create-element :html nil nil) 'html-tag)) + (is (typep (create-element :<> nil nil) 'fragment)) + (is (typep (create-element (lambda ()) nil nil) 'component)) + (signals error (create-element "div" nil nil))) (test flatten-children (let* ((elm (create-element :p nil - "a" - nil - (list "b" (list nil "c")) - (cons "d" "e")))) + (list "a" + nil + (list "b" (list nil "c")) + (cons "d" "e"))))) (is (equal (list "a" "b" "c" "d" "e") (element-children elm))))) -(defun comp1 (&key title children) +(defun comp1 (&key prop children) (create-element :div nil - title - children)) + (list prop + children))) (test component-accepting-keyword-args - (let* ((elm (create-element #'comp1 - '(:title "foo") - "bar")) - (expanded (expand-component elm))) - (is (eq #'comp1 (element-type elm))) - (is (equal '(:title "foo") (element-props elm))) - (is (equal (list "bar") (element-children elm))) - (is (eq :div (element-type expanded))) - (is (equal (list "foo" "bar") (element-children expanded))))) + (let ((elm (expand-component (create-element #'comp1 + '(:prop "value") + (list "child"))))) + (is (eq :div (element-type elm))) + (is (equal (list "value" "child") (element-children elm))))) (defun comp2 (&rest props) (create-element :div nil - (getf props :title) - (getf props :children))) + (list (getf props :prop) + (getf props :children)))) (test component-accepting-property-list - (let* ((elm (create-element #'comp2 - '(:title "foo") - "bar")) - (expanded (expand-component elm))) - (is (eq #'comp2 (element-type elm))) - (is (equal '(:title "foo") (element-props elm))) - (is (equal (list "bar") (element-children elm))) - (is (eq :div (element-type expanded))) - (is (equal (list "foo" "bar") (element-children expanded))))) + (let ((elm (expand-component (create-element #'comp2 + '(:prop "value") + (list "child"))))) + (is (eq :div (element-type elm))) + (is (equal (list "value" "child") (element-children elm))))) -(defun comp3 (&rest props &key title children &allow-other-keys) +(defun comp3 (&rest props &key prop children &allow-other-keys) (create-element :div nil - title - children - (getf props :other-key))) + (list prop + children + (getf props :other-key)))) (test component-accepting-keyword-args-and-property-list - (let* ((elm (create-element #'comp3 - '(:title "foo" :other-key "baz") - "bar")) - (expanded (expand-component elm))) - (is (eq #'comp3 (element-type elm))) - (is (equal '(:title "foo" :other-key "baz") (element-props elm))) - (is (equal (list "bar") (element-children elm))) - (is (eq :div (element-type expanded))) - (is (equal (list "foo" "bar" "baz") (element-children expanded))))) + (let ((elm (expand-component (create-element #'comp3 + '(:prop "value" :other-key "other") + (list "child"))))) + (is (eq :div (element-type elm))) + (is (equal (list "value" "child" "other") (element-children elm))))) diff --git a/tests/hsx.lisp b/tests/hsx.lisp index 38ea94d..2f8d914 100644 --- a/tests/hsx.lisp +++ b/tests/hsx.lisp @@ -9,16 +9,18 @@ (in-suite hsx-test) (test find-symbols - (is (equal '(hsx/builtin:div + (is (equal '(hsx/builtin:div '(:div "div") div (hsx/builtin:div - div - (hsx/builtin:div)) - div) + 'div + (hsx/builtin:div) + :div) + "div") (macroexpand-1 - '(hsx (div + '(hsx (div '(:div "div") div (div - div - (div)) - div)))))) + 'div + (div) + :div) + "div")))))) diff --git a/tests/renderer.lisp b/tests/renderer.lisp index 65c6cd2..a14caa7 100644 --- a/tests/renderer.lisp +++ b/tests/renderer.lisp @@ -7,8 +7,8 @@ (in-package :hsx-test/renderer) (in-readtable mstrings:mstring-syntax) -(def-suite renderer) -(in-suite renderer) +(def-suite renderer-test) +(in-suite renderer-test) (test empty-tag (is (string= "<div></div>"