escape and minify for production

This commit is contained in:
Bo Yao 2018-06-28 10:09:21 -04:00
parent 77521e12c7
commit c73384a44e
5 changed files with 136 additions and 68 deletions

View file

@ -9,7 +9,10 @@
# Usage # Usage
[Examples of usage] ## Minify for production
```lisp
(write root-element :pretty nil)
```
# License # License

View file

@ -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

View file

@ -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
View 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
View 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
(#\< "&lt;")
(#\> "&gt;")
(#\& "&amp;")
(#\' "&#039;")
(#\" "&quot;")
(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))