Node-Red configuration
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

stringifier.js 8.0KB

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