[d565449] | 1 | 'use strict'
|
---|
| 2 |
|
---|
| 3 | const DEFAULT_RAW = {
|
---|
| 4 | after: '\n',
|
---|
| 5 | beforeClose: '\n',
|
---|
| 6 | beforeComment: '\n',
|
---|
| 7 | beforeDecl: '\n',
|
---|
| 8 | beforeOpen: ' ',
|
---|
| 9 | beforeRule: '\n',
|
---|
| 10 | colon: ': ',
|
---|
| 11 | commentLeft: ' ',
|
---|
| 12 | commentRight: ' ',
|
---|
| 13 | emptyBody: '',
|
---|
| 14 | indent: ' ',
|
---|
| 15 | semicolon: false
|
---|
| 16 | }
|
---|
| 17 |
|
---|
| 18 | function capitalize(str) {
|
---|
| 19 | return str[0].toUpperCase() + str.slice(1)
|
---|
| 20 | }
|
---|
| 21 |
|
---|
| 22 | class Stringifier {
|
---|
| 23 | constructor(builder) {
|
---|
| 24 | this.builder = builder
|
---|
| 25 | }
|
---|
| 26 |
|
---|
| 27 | atrule(node, semicolon) {
|
---|
| 28 | let name = '@' + node.name
|
---|
| 29 | let params = node.params ? this.rawValue(node, 'params') : ''
|
---|
| 30 |
|
---|
| 31 | if (typeof node.raws.afterName !== 'undefined') {
|
---|
| 32 | name += node.raws.afterName
|
---|
| 33 | } else if (params) {
|
---|
| 34 | name += ' '
|
---|
| 35 | }
|
---|
| 36 |
|
---|
| 37 | if (node.nodes) {
|
---|
| 38 | this.block(node, name + params)
|
---|
| 39 | } else {
|
---|
| 40 | let end = (node.raws.between || '') + (semicolon ? ';' : '')
|
---|
| 41 | this.builder(name + params + end, node)
|
---|
| 42 | }
|
---|
| 43 | }
|
---|
| 44 |
|
---|
| 45 | beforeAfter(node, detect) {
|
---|
| 46 | let value
|
---|
| 47 | if (node.type === 'decl') {
|
---|
| 48 | value = this.raw(node, null, 'beforeDecl')
|
---|
| 49 | } else if (node.type === 'comment') {
|
---|
| 50 | value = this.raw(node, null, 'beforeComment')
|
---|
| 51 | } else if (detect === 'before') {
|
---|
| 52 | value = this.raw(node, null, 'beforeRule')
|
---|
| 53 | } else {
|
---|
| 54 | value = this.raw(node, null, 'beforeClose')
|
---|
| 55 | }
|
---|
| 56 |
|
---|
| 57 | let buf = node.parent
|
---|
| 58 | let depth = 0
|
---|
| 59 | while (buf && buf.type !== 'root') {
|
---|
| 60 | depth += 1
|
---|
| 61 | buf = buf.parent
|
---|
| 62 | }
|
---|
| 63 |
|
---|
| 64 | if (value.includes('\n')) {
|
---|
| 65 | let indent = this.raw(node, null, 'indent')
|
---|
| 66 | if (indent.length) {
|
---|
| 67 | for (let step = 0; step < depth; step++) value += indent
|
---|
| 68 | }
|
---|
| 69 | }
|
---|
| 70 |
|
---|
| 71 | return value
|
---|
| 72 | }
|
---|
| 73 |
|
---|
| 74 | block(node, start) {
|
---|
| 75 | let between = this.raw(node, 'between', 'beforeOpen')
|
---|
| 76 | this.builder(start + between + '{', node, 'start')
|
---|
| 77 |
|
---|
| 78 | let after
|
---|
| 79 | if (node.nodes && node.nodes.length) {
|
---|
| 80 | this.body(node)
|
---|
| 81 | after = this.raw(node, 'after')
|
---|
| 82 | } else {
|
---|
| 83 | after = this.raw(node, 'after', 'emptyBody')
|
---|
| 84 | }
|
---|
| 85 |
|
---|
| 86 | if (after) this.builder(after)
|
---|
| 87 | this.builder('}', node, 'end')
|
---|
| 88 | }
|
---|
| 89 |
|
---|
| 90 | body(node) {
|
---|
| 91 | let last = node.nodes.length - 1
|
---|
| 92 | while (last > 0) {
|
---|
| 93 | if (node.nodes[last].type !== 'comment') break
|
---|
| 94 | last -= 1
|
---|
| 95 | }
|
---|
| 96 |
|
---|
| 97 | let semicolon = this.raw(node, 'semicolon')
|
---|
| 98 | for (let i = 0; i < node.nodes.length; i++) {
|
---|
| 99 | let child = node.nodes[i]
|
---|
| 100 | let before = this.raw(child, 'before')
|
---|
| 101 | if (before) this.builder(before)
|
---|
| 102 | this.stringify(child, last !== i || semicolon)
|
---|
| 103 | }
|
---|
| 104 | }
|
---|
| 105 |
|
---|
| 106 | comment(node) {
|
---|
| 107 | let left = this.raw(node, 'left', 'commentLeft')
|
---|
| 108 | let right = this.raw(node, 'right', 'commentRight')
|
---|
| 109 | this.builder('/*' + left + node.text + right + '*/', node)
|
---|
| 110 | }
|
---|
| 111 |
|
---|
| 112 | decl(node, semicolon) {
|
---|
| 113 | let between = this.raw(node, 'between', 'colon')
|
---|
| 114 | let string = node.prop + between + this.rawValue(node, 'value')
|
---|
| 115 |
|
---|
| 116 | if (node.important) {
|
---|
| 117 | string += node.raws.important || ' !important'
|
---|
| 118 | }
|
---|
| 119 |
|
---|
| 120 | if (semicolon) string += ';'
|
---|
| 121 | this.builder(string, node)
|
---|
| 122 | }
|
---|
| 123 |
|
---|
| 124 | document(node) {
|
---|
| 125 | this.body(node)
|
---|
| 126 | }
|
---|
| 127 |
|
---|
| 128 | raw(node, own, detect) {
|
---|
| 129 | let value
|
---|
| 130 | if (!detect) detect = own
|
---|
| 131 |
|
---|
| 132 | // Already had
|
---|
| 133 | if (own) {
|
---|
| 134 | value = node.raws[own]
|
---|
| 135 | if (typeof value !== 'undefined') return value
|
---|
| 136 | }
|
---|
| 137 |
|
---|
| 138 | let parent = node.parent
|
---|
| 139 |
|
---|
| 140 | if (detect === 'before') {
|
---|
| 141 | // Hack for first rule in CSS
|
---|
| 142 | if (!parent || (parent.type === 'root' && parent.first === node)) {
|
---|
| 143 | return ''
|
---|
| 144 | }
|
---|
| 145 |
|
---|
| 146 | // `root` nodes in `document` should use only their own raws
|
---|
| 147 | if (parent && parent.type === 'document') {
|
---|
| 148 | return ''
|
---|
| 149 | }
|
---|
| 150 | }
|
---|
| 151 |
|
---|
| 152 | // Floating child without parent
|
---|
| 153 | if (!parent) return DEFAULT_RAW[detect]
|
---|
| 154 |
|
---|
| 155 | // Detect style by other nodes
|
---|
| 156 | let root = node.root()
|
---|
| 157 | if (!root.rawCache) root.rawCache = {}
|
---|
| 158 | if (typeof root.rawCache[detect] !== 'undefined') {
|
---|
| 159 | return root.rawCache[detect]
|
---|
| 160 | }
|
---|
| 161 |
|
---|
| 162 | if (detect === 'before' || detect === 'after') {
|
---|
| 163 | return this.beforeAfter(node, detect)
|
---|
| 164 | } else {
|
---|
| 165 | let method = 'raw' + capitalize(detect)
|
---|
| 166 | if (this[method]) {
|
---|
| 167 | value = this[method](root, node)
|
---|
| 168 | } else {
|
---|
| 169 | root.walk(i => {
|
---|
| 170 | value = i.raws[own]
|
---|
| 171 | if (typeof value !== 'undefined') return false
|
---|
| 172 | })
|
---|
| 173 | }
|
---|
| 174 | }
|
---|
| 175 |
|
---|
| 176 | if (typeof value === 'undefined') value = DEFAULT_RAW[detect]
|
---|
| 177 |
|
---|
| 178 | root.rawCache[detect] = value
|
---|
| 179 | return value
|
---|
| 180 | }
|
---|
| 181 |
|
---|
| 182 | rawBeforeClose(root) {
|
---|
| 183 | let value
|
---|
| 184 | root.walk(i => {
|
---|
| 185 | if (i.nodes && i.nodes.length > 0) {
|
---|
| 186 | if (typeof i.raws.after !== 'undefined') {
|
---|
| 187 | value = i.raws.after
|
---|
| 188 | if (value.includes('\n')) {
|
---|
| 189 | value = value.replace(/[^\n]+$/, '')
|
---|
| 190 | }
|
---|
| 191 | return false
|
---|
| 192 | }
|
---|
| 193 | }
|
---|
| 194 | })
|
---|
| 195 | if (value) value = value.replace(/\S/g, '')
|
---|
| 196 | return value
|
---|
| 197 | }
|
---|
| 198 |
|
---|
| 199 | rawBeforeComment(root, node) {
|
---|
| 200 | let value
|
---|
| 201 | root.walkComments(i => {
|
---|
| 202 | if (typeof i.raws.before !== 'undefined') {
|
---|
| 203 | value = i.raws.before
|
---|
| 204 | if (value.includes('\n')) {
|
---|
| 205 | value = value.replace(/[^\n]+$/, '')
|
---|
| 206 | }
|
---|
| 207 | return false
|
---|
| 208 | }
|
---|
| 209 | })
|
---|
| 210 | if (typeof value === 'undefined') {
|
---|
| 211 | value = this.raw(node, null, 'beforeDecl')
|
---|
| 212 | } else if (value) {
|
---|
| 213 | value = value.replace(/\S/g, '')
|
---|
| 214 | }
|
---|
| 215 | return value
|
---|
| 216 | }
|
---|
| 217 |
|
---|
| 218 | rawBeforeDecl(root, node) {
|
---|
| 219 | let value
|
---|
| 220 | root.walkDecls(i => {
|
---|
| 221 | if (typeof i.raws.before !== 'undefined') {
|
---|
| 222 | value = i.raws.before
|
---|
| 223 | if (value.includes('\n')) {
|
---|
| 224 | value = value.replace(/[^\n]+$/, '')
|
---|
| 225 | }
|
---|
| 226 | return false
|
---|
| 227 | }
|
---|
| 228 | })
|
---|
| 229 | if (typeof value === 'undefined') {
|
---|
| 230 | value = this.raw(node, null, 'beforeRule')
|
---|
| 231 | } else if (value) {
|
---|
| 232 | value = value.replace(/\S/g, '')
|
---|
| 233 | }
|
---|
| 234 | return value
|
---|
| 235 | }
|
---|
| 236 |
|
---|
| 237 | rawBeforeOpen(root) {
|
---|
| 238 | let value
|
---|
| 239 | root.walk(i => {
|
---|
| 240 | if (i.type !== 'decl') {
|
---|
| 241 | value = i.raws.between
|
---|
| 242 | if (typeof value !== 'undefined') return false
|
---|
| 243 | }
|
---|
| 244 | })
|
---|
| 245 | return value
|
---|
| 246 | }
|
---|
| 247 |
|
---|
| 248 | rawBeforeRule(root) {
|
---|
| 249 | let value
|
---|
| 250 | root.walk(i => {
|
---|
| 251 | if (i.nodes && (i.parent !== root || root.first !== i)) {
|
---|
| 252 | if (typeof i.raws.before !== 'undefined') {
|
---|
| 253 | value = i.raws.before
|
---|
| 254 | if (value.includes('\n')) {
|
---|
| 255 | value = value.replace(/[^\n]+$/, '')
|
---|
| 256 | }
|
---|
| 257 | return false
|
---|
| 258 | }
|
---|
| 259 | }
|
---|
| 260 | })
|
---|
| 261 | if (value) value = value.replace(/\S/g, '')
|
---|
| 262 | return value
|
---|
| 263 | }
|
---|
| 264 |
|
---|
| 265 | rawColon(root) {
|
---|
| 266 | let value
|
---|
| 267 | root.walkDecls(i => {
|
---|
| 268 | if (typeof i.raws.between !== 'undefined') {
|
---|
| 269 | value = i.raws.between.replace(/[^\s:]/g, '')
|
---|
| 270 | return false
|
---|
| 271 | }
|
---|
| 272 | })
|
---|
| 273 | return value
|
---|
| 274 | }
|
---|
| 275 |
|
---|
| 276 | rawEmptyBody(root) {
|
---|
| 277 | let value
|
---|
| 278 | root.walk(i => {
|
---|
| 279 | if (i.nodes && i.nodes.length === 0) {
|
---|
| 280 | value = i.raws.after
|
---|
| 281 | if (typeof value !== 'undefined') return false
|
---|
| 282 | }
|
---|
| 283 | })
|
---|
| 284 | return value
|
---|
| 285 | }
|
---|
| 286 |
|
---|
| 287 | rawIndent(root) {
|
---|
| 288 | if (root.raws.indent) return root.raws.indent
|
---|
| 289 | let value
|
---|
| 290 | root.walk(i => {
|
---|
| 291 | let p = i.parent
|
---|
| 292 | if (p && p !== root && p.parent && p.parent === root) {
|
---|
| 293 | if (typeof i.raws.before !== 'undefined') {
|
---|
| 294 | let parts = i.raws.before.split('\n')
|
---|
| 295 | value = parts[parts.length - 1]
|
---|
| 296 | value = value.replace(/\S/g, '')
|
---|
| 297 | return false
|
---|
| 298 | }
|
---|
| 299 | }
|
---|
| 300 | })
|
---|
| 301 | return value
|
---|
| 302 | }
|
---|
| 303 |
|
---|
| 304 | rawSemicolon(root) {
|
---|
| 305 | let value
|
---|
| 306 | root.walk(i => {
|
---|
| 307 | if (i.nodes && i.nodes.length && i.last.type === 'decl') {
|
---|
| 308 | value = i.raws.semicolon
|
---|
| 309 | if (typeof value !== 'undefined') return false
|
---|
| 310 | }
|
---|
| 311 | })
|
---|
| 312 | return value
|
---|
| 313 | }
|
---|
| 314 |
|
---|
| 315 | rawValue(node, prop) {
|
---|
| 316 | let value = node[prop]
|
---|
| 317 | let raw = node.raws[prop]
|
---|
| 318 | if (raw && raw.value === value) {
|
---|
| 319 | return raw.raw
|
---|
| 320 | }
|
---|
| 321 |
|
---|
| 322 | return value
|
---|
| 323 | }
|
---|
| 324 |
|
---|
| 325 | root(node) {
|
---|
| 326 | this.body(node)
|
---|
| 327 | if (node.raws.after) this.builder(node.raws.after)
|
---|
| 328 | }
|
---|
| 329 |
|
---|
| 330 | rule(node) {
|
---|
| 331 | this.block(node, this.rawValue(node, 'selector'))
|
---|
| 332 | if (node.raws.ownSemicolon) {
|
---|
| 333 | this.builder(node.raws.ownSemicolon, node, 'end')
|
---|
| 334 | }
|
---|
| 335 | }
|
---|
| 336 |
|
---|
| 337 | stringify(node, semicolon) {
|
---|
| 338 | /* c8 ignore start */
|
---|
| 339 | if (!this[node.type]) {
|
---|
| 340 | throw new Error(
|
---|
| 341 | 'Unknown AST node type ' +
|
---|
| 342 | node.type +
|
---|
| 343 | '. ' +
|
---|
| 344 | 'Maybe you need to change PostCSS stringifier.'
|
---|
| 345 | )
|
---|
| 346 | }
|
---|
| 347 | /* c8 ignore stop */
|
---|
| 348 | this[node.type](node, semicolon)
|
---|
| 349 | }
|
---|
| 350 | }
|
---|
| 351 |
|
---|
| 352 | module.exports = Stringifier
|
---|
| 353 | Stringifier.default = Stringifier
|
---|