1
1
import { quotePropIfNecessary , quoteNameIfNecessary } from '../../../utils/quoteIfNecessary' ;
2
2
import isVoidElementName from '../../../utils/isVoidElementName' ;
3
+ import Attribute from '../../nodes/Attribute' ;
4
+ import Node from '../../nodes/shared/Node' ;
5
+ import { escapeTemplate } from '../../../utils/stringify' ;
3
6
4
7
// source: https://gist.github.com/ArjanSchouten/0b8574a6ad7f5065a5e7
5
8
const boolean_attributes = new Set ( [
@@ -71,7 +74,7 @@ export default function(node, renderer, options) {
71
74
args . push ( attribute . expression . snippet ) ;
72
75
} else {
73
76
if ( attribute . name === 'value' && node . name === 'textarea' ) {
74
- textareaContents = attribute . stringifyForSsr ( ) ;
77
+ textareaContents = stringifyAttribute ( attribute ) ;
75
78
} else if ( attribute . isTrue ) {
76
79
args . push ( `{ ${ quoteNameIfNecessary ( attribute . name ) } : true }` ) ;
77
80
} else if (
@@ -82,18 +85,18 @@ export default function(node, renderer, options) {
82
85
// a boolean attribute with one non-Text chunk
83
86
args . push ( `{ ${ quoteNameIfNecessary ( attribute . name ) } : ${ attribute . chunks [ 0 ] . snippet } }` ) ;
84
87
} else {
85
- args . push ( `{ ${ quoteNameIfNecessary ( attribute . name ) } : \`${ attribute . stringifyForSsr ( ) } \` }` ) ;
88
+ args . push ( `{ ${ quoteNameIfNecessary ( attribute . name ) } : \`${ stringifyAttribute ( attribute ) } \` }` ) ;
86
89
}
87
90
}
88
91
} ) ;
89
92
90
93
openingTag += "${@spread([" + args . join ( ', ' ) + "])}" ;
91
94
} else {
92
- node . attributes . forEach ( ( attribute : Node ) => {
95
+ node . attributes . forEach ( ( attribute : Attribute ) => {
93
96
if ( attribute . type !== 'Attribute' ) return ;
94
97
95
98
if ( attribute . name === 'value' && node . name === 'textarea' ) {
96
- textareaContents = attribute . stringifyForSsr ( ) ;
99
+ textareaContents = stringifyAttribute ( attribute ) ;
97
100
} else if ( attribute . isTrue ) {
98
101
openingTag += ` ${ attribute . name } ` ;
99
102
} else if (
@@ -105,9 +108,14 @@ export default function(node, renderer, options) {
105
108
openingTag += '${' + attribute . chunks [ 0 ] . snippet + ' ? " ' + attribute . name + '" : "" }' ;
106
109
} else if ( attribute . name === 'class' && classExpr ) {
107
110
addClassAttribute = false ;
108
- openingTag += ` class="\${[\`${ attribute . stringifyForSsr ( ) } \`, ${ classExpr } ].join(' ').trim() }"` ;
111
+ openingTag += ` class="\${[\`${ stringifyAttribute ( attribute ) } \`, ${ classExpr } ].join(' ').trim() }"` ;
112
+ } else if ( attribute . isConcatenated || ! attribute . isDynamic ) {
113
+ openingTag += ` ${ attribute . name } ="${ stringifyAttribute ( attribute ) } "` ;
109
114
} else {
110
- openingTag += ` ${ attribute . name } ="${ attribute . stringifyForSsr ( ) } "` ;
115
+ const { name } = attribute ;
116
+ const { snippet } = attribute . chunks [ 0 ] ;
117
+
118
+ openingTag += '${(v => v == null ? "" : ` ' + name + '=${' + snippet + '}`)(' + snippet + ')}' ;
111
119
}
112
120
} ) ;
113
121
}
@@ -129,4 +137,16 @@ export default function(node, renderer, options) {
129
137
if ( ! isVoidElementName ( node . name ) ) {
130
138
renderer . append ( `</${ node . name } >` ) ;
131
139
}
140
+ }
141
+
142
+ function stringifyAttribute ( attribute : Attribute ) {
143
+ return attribute . chunks
144
+ . map ( ( chunk : Node ) => {
145
+ if ( chunk . type === 'Text' ) {
146
+ return escapeTemplate ( escape ( chunk . data ) . replace ( / " / g, '"' ) ) ;
147
+ }
148
+
149
+ return '${@escape(' + chunk . snippet + ')}' ;
150
+ } )
151
+ . join ( '' ) ;
132
152
}
0 commit comments