CoolFace
Apppublic

AK-21/Graphite-Industrial-Intelligence

sourceHugging Faceupdated 3mo agoView on Hugging Face
0likes
stringifier.js375 linesDownload Raw Back to lib
1'use strict'2 3// Escapes sequences that could break out of an HTML <style> context.4// Uses CSS unicode escaping (\3c = '<') which is valid CSS and parsed5// correctly by all compliant CSS consumers.6const STYLE_TAG = /(<)(\/?style\b)/gi7const COMMENT_OPEN = /(<)(!--)/g8 9function escapeHTMLInCSS(str) {10  if (typeof str !== 'string') return str11  if (!str.includes('<')) return str12  return str.replace(STYLE_TAG, '\\3c $2').replace(COMMENT_OPEN, '\\3c $2')13}14 15const DEFAULT_RAW = {16  after: '\n',17  beforeClose: '\n',18  beforeComment: '\n',19  beforeDecl: '\n',20  beforeOpen: ' ',21  beforeRule: '\n',22  colon: ': ',23  commentLeft: ' ',24  commentRight: ' ',25  emptyBody: '',26  indent: '    ',27  semicolon: false28}29 30function capitalize(str) {31  return str[0].toUpperCase() + str.slice(1)32}33 34class Stringifier {35  constructor(builder) {36    this.builder = builder37  }38 39  atrule(node, semicolon) {40    let raws = node.raws41    let name = '@' + node.name42    let params = node.params ? this.rawValue(node, 'params') : ''43 44    if (typeof raws.afterName !== 'undefined') {45      name += raws.afterName46    } else if (params) {47      name += ' '48    }49 50    if (node.nodes) {51      this.block(node, name + params)52    } else {53      let end = (raws.between || '') + (semicolon ? ';' : '')54      this.builder(escapeHTMLInCSS(name + params + end), node)55    }56  }57 58  beforeAfter(node, detect) {59    let value60    if (node.type === 'decl') {61      value = this.raw(node, null, 'beforeDecl')62    } else if (node.type === 'comment') {63      value = this.raw(node, null, 'beforeComment')64    } else if (detect === 'before') {65      value = this.raw(node, null, 'beforeRule')66    } else {67      value = this.raw(node, null, 'beforeClose')68    }69 70    let buf = node.parent71    let depth = 072    while (buf && buf.type !== 'root') {73      depth += 174      buf = buf.parent75    }76 77    if (value.includes('\n')) {78      let indent = this.raw(node, null, 'indent')79      if (indent.length) {80        for (let step = 0; step < depth; step++) value += indent81      }82    }83 84    return value85  }86 87  block(node, start) {88    let between = this.raw(node, 'between', 'beforeOpen')89    this.builder(escapeHTMLInCSS(start + between) + '{', node, 'start')90 91    let after92    if (node.nodes && node.nodes.length) {93      this.body(node)94      after = this.raw(node, 'after')95    } else {96      after = this.raw(node, 'after', 'emptyBody')97    }98 99    if (after) this.builder(escapeHTMLInCSS(after))100    this.builder('}', node, 'end')101  }102 103  body(node) {104    let nodes = node.nodes105    let last = nodes.length - 1106    while (last > 0) {107      if (nodes[last].type !== 'comment') break108      last -= 1109    }110 111    let semicolon = this.raw(node, 'semicolon')112    let isDocument = node.type === 'document'113    for (let i = 0; i < nodes.length; i++) {114      let child = nodes[i]115      let before = this.raw(child, 'before')116      if (before) this.builder(isDocument ? before : escapeHTMLInCSS(before))117      this.stringify(child, last !== i || semicolon)118    }119  }120 121  comment(node) {122    let left = this.raw(node, 'left', 'commentLeft')123    let right = this.raw(node, 'right', 'commentRight')124    this.builder(escapeHTMLInCSS('/*' + left + node.text + right + '*/'), node)125  }126 127  decl(node, semicolon) {128    let raws = node.raws129    let between = this.raw(node, 'between', 'colon')130 131    let string = node.prop + between + this.rawValue(node, 'value')132 133    if (node.important) {134      string += raws.important || ' !important'135    }136 137    if (semicolon) string += ';'138    this.builder(escapeHTMLInCSS(string), node)139  }140 141  document(node) {142    this.body(node)143  }144 145  raw(node, own, detect) {146    let value147    if (!detect) detect = own148 149    // Already had150    if (own) {151      value = node.raws[own]152      if (typeof value !== 'undefined') return value153    }154 155    let parent = node.parent156 157    if (detect === 'before') {158      // Hack for first rule in CSS159      if (!parent || (parent.type === 'root' && parent.first === node)) {160        return ''161      }162 163      // `root` nodes in `document` should use only their own raws164      if (parent && parent.type === 'document') {165        return ''166      }167    }168 169    // Floating child without parent170    if (!parent) return DEFAULT_RAW[detect]171 172    // Detect style by other nodes173    let root = node.root()174    let cache = root.rawCache || (root.rawCache = {})175    if (typeof cache[detect] !== 'undefined') {176      return cache[detect]177    }178 179    if (detect === 'before' || detect === 'after') {180      return this.beforeAfter(node, detect)181    } else {182      let method = 'raw' + capitalize(detect)183      if (this[method]) {184        value = this[method](root, node)185      } else {186        root.walk(i => {187          value = i.raws[own]188          if (typeof value !== 'undefined') return false189        })190      }191    }192 193    if (typeof value === 'undefined') value = DEFAULT_RAW[detect]194 195    cache[detect] = value196    return value197  }198 199  rawBeforeClose(root) {200    let value201    root.walk(i => {202      if (i.nodes && i.nodes.length > 0) {203        if (typeof i.raws.after !== 'undefined') {204          value = i.raws.after205          if (value.includes('\n')) {206            value = value.replace(/[^\n]+$/, '')207          }208          return false209        }210      }211    })212    if (value) value = value.replace(/\S/g, '')213    return value214  }215 216  rawBeforeComment(root, node) {217    let value218    root.walkComments(i => {219      if (typeof i.raws.before !== 'undefined') {220        value = i.raws.before221        if (value.includes('\n')) {222          value = value.replace(/[^\n]+$/, '')223        }224        return false225      }226    })227    if (typeof value === 'undefined') {228      value = this.raw(node, null, 'beforeDecl')229    } else if (value) {230      value = value.replace(/\S/g, '')231    }232    return value233  }234 235  rawBeforeDecl(root, node) {236    let value237    root.walkDecls(i => {238      if (typeof i.raws.before !== 'undefined') {239        value = i.raws.before240        if (value.includes('\n')) {241          value = value.replace(/[^\n]+$/, '')242        }243        return false244      }245    })246    if (typeof value === 'undefined') {247      value = this.raw(node, null, 'beforeRule')248    } else if (value) {249      value = value.replace(/\S/g, '')250    }251    return value252  }253 254  rawBeforeOpen(root) {255    let value256    root.walk(i => {257      if (i.type !== 'decl') {258        value = i.raws.between259        if (typeof value !== 'undefined') return false260      }261    })262    return value263  }264 265  rawBeforeRule(root) {266    let value267    root.walk(i => {268      if (i.nodes && (i.parent !== root || root.first !== i)) {269        if (typeof i.raws.before !== 'undefined') {270          value = i.raws.before271          if (value.includes('\n')) {272            value = value.replace(/[^\n]+$/, '')273          }274          return false275        }276      }277    })278    if (value) value = value.replace(/\S/g, '')279    return value280  }281 282  rawColon(root) {283    let value284    root.walkDecls(i => {285      if (typeof i.raws.between !== 'undefined') {286        value = i.raws.between.replace(/[^\s:]/g, '')287        return false288      }289    })290    return value291  }292 293  rawEmptyBody(root) {294    let value295    root.walk(i => {296      if (i.nodes && i.nodes.length === 0) {297        value = i.raws.after298        if (typeof value !== 'undefined') return false299      }300    })301    return value302  }303 304  rawIndent(root) {305    if (root.raws.indent) return root.raws.indent306    let value307    root.walk(i => {308      let p = i.parent309      if (p && p !== root && p.parent && p.parent === root) {310        if (typeof i.raws.before !== 'undefined') {311          let parts = i.raws.before.split('\n')312          value = parts[parts.length - 1]313          value = value.replace(/\S/g, '')314          return false315        }316      }317    })318    return value319  }320 321  rawSemicolon(root) {322    let value323    root.walk(i => {324      if (i.nodes && i.nodes.length && i.last.type === 'decl') {325        value = i.raws.semicolon326        if (typeof value !== 'undefined') return false327      }328    })329    return value330  }331 332  rawValue(node, prop) {333    let value = node[prop]334    let raw = node.raws[prop]335    if (raw && raw.value === value) {336      return raw.raw337    }338 339    return value340  }341 342  root(node) {343    this.body(node)344    if (node.raws.after) {345      let after = node.raws.after346      let isDocument = node.parent && node.parent.type === 'document'347      this.builder(isDocument ? after : escapeHTMLInCSS(after))348    }349  }350 351  rule(node) {352    this.block(node, this.rawValue(node, 'selector'))353    if (node.raws.ownSemicolon) {354      this.builder(escapeHTMLInCSS(node.raws.ownSemicolon), node, 'end')355    }356  }357 358  stringify(node, semicolon) {359    /* c8 ignore start */360    if (!this[node.type]) {361      throw new Error(362        'Unknown AST node type ' +363          node.type +364          '. ' +365          'Maybe you need to change PostCSS stringifier.'366      )367    }368    /* c8 ignore stop */369    this[node.type](node, semicolon)370  }371}372 373module.exports = Stringifier374Stringifier.default = Stringifier375