escape and minify for production
This commit is contained in:
parent
77521e12c7
commit
c73384a44e
5 changed files with 136 additions and 68 deletions
|
@ -9,7 +9,10 @@
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
[Examples of usage]
|
## Minify for production
|
||||||
|
```lisp
|
||||||
|
(write root-element :pretty nil)
|
||||||
|
```
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,9 @@
|
||||||
:components ((:module "src"
|
:components ((:module "src"
|
||||||
:serial t
|
:serial t
|
||||||
:components
|
:components
|
||||||
((:file "flute"))))
|
((:file "package")
|
||||||
|
(:file "util")
|
||||||
|
(:file "flute"))))
|
||||||
:description "A beautiful, easilly composable html generation library"
|
:description "A beautiful, easilly composable html generation library"
|
||||||
:long-description
|
:long-description
|
||||||
#.(uiop:read-file-string
|
#.(uiop:read-file-string
|
||||||
|
|
|
@ -1,33 +1,3 @@
|
||||||
(in-package :cl-user)
|
|
||||||
(defpackage flute
|
|
||||||
(:use :cl)
|
|
||||||
(:import-from :assoc-utils
|
|
||||||
:alist
|
|
||||||
:alistp
|
|
||||||
:hash-alist
|
|
||||||
:aget
|
|
||||||
:delete-from-alistf)
|
|
||||||
(:import-from :let-over-lambda
|
|
||||||
:defmacro!
|
|
||||||
:mkstr
|
|
||||||
:flatten)
|
|
||||||
(:import-from :alexandria
|
|
||||||
:make-keyword)
|
|
||||||
(:export
|
|
||||||
;; all html5 elements, e.g. div, nav, media, export in code
|
|
||||||
;; except <time> and <map> conflicts with cl symbol, are defined and exported as |time|, |map|
|
|
||||||
:define-element
|
|
||||||
:attrs
|
|
||||||
:attrs-alist
|
|
||||||
:make-attrs
|
|
||||||
:copy-attrs
|
|
||||||
:html
|
|
||||||
:element-tag
|
|
||||||
:element-attrs
|
|
||||||
:element-children
|
|
||||||
:user-element-expand-to
|
|
||||||
:*expand-user-element*
|
|
||||||
:h))
|
|
||||||
(in-package :flute)
|
(in-package :flute)
|
||||||
|
|
||||||
(defclass element ()
|
(defclass element ()
|
||||||
|
@ -48,16 +18,36 @@
|
||||||
((expand-to :initarg :expand-to
|
((expand-to :initarg :expand-to
|
||||||
:accessor user-element-expand-to)))
|
:accessor user-element-expand-to)))
|
||||||
|
|
||||||
(defun make-builtin-element (&rest args &key tag attrs children)
|
(defun make-builtin-element (&key tag attrs children)
|
||||||
(apply #'make-instance 'builtin-element args))
|
(make-instance 'builtin-element :tag tag :attrs attrs
|
||||||
|
:children (escape-children children)))
|
||||||
|
|
||||||
(defun make-builtin-element-with-prefix (&rest args &key tag attrs children prefix)
|
(defun make-builtin-element-with-prefix (&key tag attrs children prefix)
|
||||||
(apply #'make-instance 'builtin-element-with-prefix args))
|
(make-instance 'builtin-element-with-prefix :tag tag :attrs attrs :prefix prefix
|
||||||
|
:children (escape-children children)))
|
||||||
|
|
||||||
(defun make-user-element (&rest args &key tag attrs children expand-to)
|
(defun make-user-element (&rest args &key tag attrs children expand-to)
|
||||||
(apply #'make-instance 'user-element args))
|
(make-instance 'user-element :tag tag :attrs attrs :expand-to expand-to
|
||||||
|
:children (escape-children children)))
|
||||||
|
|
||||||
(defstruct attrs alist)
|
(defstruct (attrs (:constructor %make-attrs))
|
||||||
|
alist)
|
||||||
|
|
||||||
|
(defvar *escape-html* :utf8
|
||||||
|
"Specify the escape option when generate html, can be :UTF8, :ASCII, :ATTR or NIL.
|
||||||
|
If :UTF8, escape only #\<, #\> and #\& in body, and \" in attribute keys. #\' will
|
||||||
|
in attribute keys will not be escaped since flute will always use double quote for
|
||||||
|
attribute keys.
|
||||||
|
If :ASCII, besides what escaped in :UTF8, also escape all non-ascii characters.
|
||||||
|
If :ATTR, only #\" in attribute values will be escaped.
|
||||||
|
If NIL, nothing is escaped and programmer is responsible to escape elements properly.
|
||||||
|
When given :ASCII and :ATTR, it's possible to insert html text as a children, e.g.
|
||||||
|
(div :id \"container\" \"Some <b>text</b>\")")
|
||||||
|
|
||||||
|
(defun make-attrs (&keys alist)
|
||||||
|
(if *escape-html*
|
||||||
|
(%make-attrs :alist (escape-attrs-alist alist))
|
||||||
|
(%make-attrs :alist alist)))
|
||||||
|
|
||||||
(defmethod (setf attr) (value (attrs attrs) key)
|
(defmethod (setf attr) (value (attrs attrs) key)
|
||||||
(setf (aget (attrs-alist) key) value))
|
(setf (aget (attrs-alist) key) value))
|
||||||
|
@ -101,16 +91,6 @@
|
||||||
(t
|
(t
|
||||||
(values (make-attrs :alist nil) (flatten attrs-and-children)))))
|
(values (make-attrs :alist nil) (flatten attrs-and-children)))))
|
||||||
|
|
||||||
(defun plist-alist (plist)
|
|
||||||
(loop for (k v) on plist by #'cddr
|
|
||||||
collect (cons k v)))
|
|
||||||
|
|
||||||
(defun alist-plist* (alist)
|
|
||||||
(mapcan (lambda (kv)
|
|
||||||
(list (string-downcase (car kv))
|
|
||||||
(cdr kv)))
|
|
||||||
alist))
|
|
||||||
|
|
||||||
(defvar *builtin-elements* (make-hash-table))
|
(defvar *builtin-elements* (make-hash-table))
|
||||||
|
|
||||||
(defun html (&rest attrs-and-children)
|
(defun html (&rest attrs-and-children)
|
||||||
|
@ -192,26 +172,6 @@
|
||||||
stream)
|
stream)
|
||||||
(call-next-method)))
|
(call-next-method)))
|
||||||
|
|
||||||
(defun tree-leaves%% (tree test result)
|
|
||||||
(if tree
|
|
||||||
(if (listp tree)
|
|
||||||
(cons
|
|
||||||
(tree-leaves%% (car tree) test result)
|
|
||||||
(tree-leaves%% (cdr tree) test result))
|
|
||||||
(if (funcall test tree)
|
|
||||||
(funcall result tree)
|
|
||||||
tree))))
|
|
||||||
|
|
||||||
(defmacro tree-leaves (tree test result)
|
|
||||||
`(tree-leaves%%
|
|
||||||
,tree
|
|
||||||
(lambda (x)
|
|
||||||
(declare (ignorable x))
|
|
||||||
,test)
|
|
||||||
(lambda (x)
|
|
||||||
(declare (ignorable x))
|
|
||||||
,result)))
|
|
||||||
|
|
||||||
(defmacro h (&body body)
|
(defmacro h (&body body)
|
||||||
`(progn
|
`(progn
|
||||||
,@(tree-leaves
|
,@(tree-leaves
|
||||||
|
|
31
src/package.lisp
Normal file
31
src/package.lisp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
(in-package :cl-user)
|
||||||
|
(defpackage flute
|
||||||
|
(:use :cl)
|
||||||
|
(:import-from :assoc-utils
|
||||||
|
:alist
|
||||||
|
:alistp
|
||||||
|
:hash-alist
|
||||||
|
:aget
|
||||||
|
:delete-from-alistf)
|
||||||
|
(:import-from :let-over-lambda
|
||||||
|
:defmacro!
|
||||||
|
:mkstr
|
||||||
|
:flatten)
|
||||||
|
(:import-from :alexandria
|
||||||
|
:make-keyword)
|
||||||
|
(:export
|
||||||
|
;; all html5 elements, e.g. div, nav, media, export in code except
|
||||||
|
;; <time> and <map> conflicts with cl symbol, are defined and
|
||||||
|
;; exported as |time|, |map|
|
||||||
|
:define-element
|
||||||
|
:attrs
|
||||||
|
:attrs-alist
|
||||||
|
:make-attrs
|
||||||
|
:copy-attrs
|
||||||
|
:html
|
||||||
|
:element-tag
|
||||||
|
:element-attrs
|
||||||
|
:element-children
|
||||||
|
:user-element-expand-to
|
||||||
|
:*expand-user-element*
|
||||||
|
:h))
|
72
src/util.lisp
Normal file
72
src/util.lisp
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
(in-package :flute)
|
||||||
|
|
||||||
|
(defun plist-alist (plist)
|
||||||
|
(loop for (k v) on plist by #'cddr
|
||||||
|
collect (cons k v)))
|
||||||
|
|
||||||
|
(defun alist-plist* (alist)
|
||||||
|
(mapcan (lambda (kv)
|
||||||
|
(list (string-downcase (car kv))
|
||||||
|
(cdr kv)))
|
||||||
|
alist))
|
||||||
|
|
||||||
|
(defun tree-leaves%% (tree test result)
|
||||||
|
(if tree
|
||||||
|
(if (listp tree)
|
||||||
|
(cons
|
||||||
|
(tree-leaves%% (car tree) test result)
|
||||||
|
(tree-leaves%% (cdr tree) test result))
|
||||||
|
(if (funcall test tree)
|
||||||
|
(funcall result tree)
|
||||||
|
tree))))
|
||||||
|
|
||||||
|
(defmacro tree-leaves (tree test result)
|
||||||
|
`(tree-leaves%%
|
||||||
|
,tree
|
||||||
|
(lambda (x)
|
||||||
|
(declare (ignorable x))
|
||||||
|
,test)
|
||||||
|
(lambda (x)
|
||||||
|
(declare (ignorable x))
|
||||||
|
,result)))
|
||||||
|
|
||||||
|
(defun utf8-html-escape-char-p (char)
|
||||||
|
(find char "<>&"))
|
||||||
|
|
||||||
|
(defun ascii-html-escape-char-p (char)
|
||||||
|
(or (utf8-html-escape-char-p char)
|
||||||
|
(> (char-code char) 127)))
|
||||||
|
|
||||||
|
(defun attr-value-escape-char-p (char)
|
||||||
|
(eql char #\"))
|
||||||
|
|
||||||
|
(defun escape-char (char)
|
||||||
|
(case char
|
||||||
|
(#\< "<")
|
||||||
|
(#\> ">")
|
||||||
|
(#\& "&")
|
||||||
|
(#\' "'")
|
||||||
|
(#\" """)
|
||||||
|
(t (format nil "&#~d;" (char-code char)))))
|
||||||
|
|
||||||
|
(defun escape-string (string &optional (test #'utf8-html-escape-char-p))
|
||||||
|
(with-output-to-string (s)
|
||||||
|
(loop
|
||||||
|
for c across string
|
||||||
|
do (write (if (funcall test c) (escape-char c) c) :stream s :escape nil))))
|
||||||
|
|
||||||
|
(defun escape-attrs-alist (alist)
|
||||||
|
(mapcar (lambda (kv)
|
||||||
|
(cons (car kv)
|
||||||
|
(escape-string (cdr kv) #'attr-value-escape-char-p)))
|
||||||
|
alist))
|
||||||
|
|
||||||
|
(defun escape-children (children)
|
||||||
|
(mapcar (lambda (child)
|
||||||
|
(if (stringp child)
|
||||||
|
(case *escape-html*
|
||||||
|
(:utf8 (escape-string child))
|
||||||
|
(:ascii (escape-string child #'ascii-html-escape-char-p))
|
||||||
|
(otherwise child))
|
||||||
|
child))
|
||||||
|
children))
|
Loading…
Reference in a new issue