rtlcss.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. #!/usr/bin/env node
  2. var path = require('path')
  3. var fs = require('fs')
  4. var chalk = require('chalk')
  5. var mkdirp = require('mkdirp')
  6. var postcss = require('postcss')
  7. var rtlcss = require('../lib/rtlcss')
  8. var configLoader = require('../lib/config-loader')
  9. var input, output, directory, ext, config, currentErrorcode, arg
  10. var args = process.argv.slice(2)
  11. var shouldBreak = false
  12. process.on('exit', function () { process.reallyExit(currentErrorcode) })
  13. function printWarning () {
  14. console.warn(chalk.yellow.apply(this, printWarning.arguments))
  15. }
  16. function printInfo () {
  17. console.info(chalk.green.apply(this, printInfo.arguments))
  18. }
  19. function printError () {
  20. console.error(chalk.red.apply(this, printError.arguments))
  21. }
  22. function printHelp () {
  23. console.log('Usage: rtlcss [option option=parameter ...] [source] [destination]')
  24. console.log('')
  25. /*eslint-disable*/
  26. var options = [
  27. 'Option ' , 'Description ',
  28. '--------------', '----------------------------------------------',
  29. '-h,--help' , 'Print help (this message) and exit.',
  30. '-v,--version' , 'Print version number and exit.',
  31. '-c,--config' , 'Path to configuration settings file.',
  32. '- ,--stdin' , 'Read from stdin stream.',
  33. '-d,--dirctory' , 'Process all *.css files from input directory (recursive).',
  34. '-e,--ext' , 'Used with -d option to set the output files extension.\n\t\t Default: ".rtl.css".',
  35. '-s,--silent' , 'Silent mode, no warnings or errors are printed.'
  36. ]
  37. /*eslint-enable */
  38. for (var x = 0; x < options.length; x++) {
  39. console.log(options[x++], '\t', options[x])
  40. }
  41. console.log('')
  42. console.log('*If no destination is specified, output will be written to the same input folder as {source}.rtl.{ext}')
  43. console.log('')
  44. printInfo('RTLCSS version: ' + require('../package.json').version)
  45. printInfo('Report issues to: https://github.com/MohammadYounes/rtlcss/issues')
  46. }
  47. while ((arg = args.shift())) {
  48. switch (arg) {
  49. case '-h':
  50. case '--help':
  51. printHelp()
  52. shouldBreak = true
  53. break
  54. case '-v':
  55. case '--version':
  56. printInfo('rtlcss version: ' + require('../package.json').version)
  57. shouldBreak = true
  58. break
  59. case '-c':
  60. case '--config':
  61. arg = args.shift()
  62. try {
  63. config = configLoader.load(path.resolve(arg))
  64. } catch (e) {
  65. printError('rtlcss: invalid config file. ', e)
  66. shouldBreak = true
  67. currentErrorcode = 1
  68. }
  69. break
  70. case '-d':
  71. case '--directory':
  72. directory = true
  73. break
  74. case '-e':
  75. case '--ext':
  76. ext = args.shift()
  77. break
  78. case '-s':
  79. case '--silent':
  80. console.log = console.info = console.warn = function () {}
  81. break
  82. case '-':
  83. case '--stdin':
  84. input = '-'
  85. break
  86. default:
  87. if (arg[0] === '-') {
  88. printError('rtlcss: unknown option. ' + arg)
  89. shouldBreak = true
  90. } else {
  91. if (!input) {
  92. input = path.resolve(arg)
  93. } else if (!output) {
  94. output = path.resolve(arg)
  95. }
  96. }
  97. break
  98. }
  99. }
  100. if (!shouldBreak) {
  101. if (!directory && !input) {
  102. printError('rtlcss: no input file')
  103. console.log('')
  104. printHelp()
  105. shouldBreak = true
  106. }
  107. if (!config && input !== '-') {
  108. try {
  109. var cwd = input
  110. if (directory !== true) {
  111. cwd = path.dirname(input)
  112. }
  113. config = configLoader.load(null, cwd)
  114. } catch (e) {
  115. printError('rtlcss: invalid config file. ', e)
  116. currentErrorcode = 1
  117. shouldBreak = true
  118. }
  119. }
  120. }
  121. if (!shouldBreak) {
  122. if (!output && input !== '-') {
  123. if (directory !== true) {
  124. output = path.extname(input) ? input.replace(/\.[^.]*$/, function (ext) { return '.rtl' + ext }) : input + '.rtl'
  125. } else {
  126. output = input
  127. }
  128. }
  129. var processCSSFile = function (e, data, outputName) {
  130. if (e) {
  131. printError('rtlcss: ' + e.message)
  132. return
  133. }
  134. var result
  135. var opt = { map: false }
  136. if (input !== '-') {
  137. opt.from = input
  138. opt.to = output
  139. }
  140. if (!config) {
  141. printWarning('rtlcss: Warning! No config present, using defaults.')
  142. result = postcss([rtlcss]).process(data, opt)
  143. } else {
  144. if ('map' in config === true && input !== '-') {
  145. opt.map = config.map
  146. }
  147. result = postcss([rtlcss.configure(config)]).process(data, opt)
  148. }
  149. if (output) {
  150. var savePath = outputName
  151. if (directory !== true) {
  152. savePath = output
  153. }
  154. printInfo('Saving:', savePath)
  155. fs.writeFile(savePath, result.css, 'utf8', function (err) { err && printError(err) })
  156. if (result.map) {
  157. fs.writeFile(savePath + '.map', result.map, 'utf8', function (err) { err && printError(err) })
  158. }
  159. } else {
  160. console.log(result.css)
  161. }
  162. }
  163. var walk = function (dir, done) {
  164. fs.readdir(dir, function (error, list) {
  165. if (error) {
  166. return done(error)
  167. }
  168. var i = 0
  169. ;(function next () {
  170. var file = list[i++]
  171. if (!file) {
  172. return done(null)
  173. }
  174. file = dir + path.sep + file
  175. fs.stat(file, function (err, stat) {
  176. if (err) {
  177. printError(err)
  178. } else if (stat && stat.isDirectory()) {
  179. walk(file, function (err) {
  180. if (err) {
  181. printError(err)
  182. } else {
  183. next()
  184. }
  185. })
  186. } else {
  187. // process only *.css
  188. if (/\.(css)$/.test(file)) {
  189. // compute output directory
  190. var relativePath = path.relative(input, file).split(path.sep)
  191. relativePath.pop()
  192. relativePath.push(path.basename(file).replace('.css', ext || '.rtl.css'))
  193. // set rtl file name
  194. var rtlFile = path.join(output, relativePath.join(path.sep))
  195. // create output directory if it does not exist
  196. var dirName = path.dirname(rtlFile)
  197. if (!fs.existsSync(dirName)) {
  198. mkdirp.sync(dirName)
  199. }
  200. // read and process the file.
  201. fs.readFile(file, 'utf8', function (e, data) {
  202. try {
  203. processCSSFile(e, data, rtlFile)
  204. } catch (e) {
  205. printError('rtlcss: error processing file', file)
  206. printError(e)
  207. }
  208. })
  209. }
  210. next()
  211. }
  212. })
  213. })()
  214. })
  215. }
  216. if (input !== '-') {
  217. if (directory !== true) {
  218. fs.stat(input, function (error, stat) {
  219. if (error) {
  220. printError(error)
  221. } else if (stat && stat.isDirectory()) {
  222. printError('rtlcss: Input expected to be a file, use -d option to process a directory.')
  223. } else {
  224. fs.readFile(input, 'utf8', function (e, data) {
  225. try {
  226. processCSSFile(e, data)
  227. } catch (e) {
  228. printError('rtlcss: error processing file', input)
  229. printError(e)
  230. }
  231. })
  232. }
  233. })
  234. } else {
  235. walk(input, function (error) {
  236. if (error) {
  237. printError('rtlcss: ' + error)
  238. }
  239. })
  240. }
  241. } else {
  242. process.stdin.resume()
  243. process.stdin.setEncoding('utf8')
  244. var buffer = ''
  245. process.stdin.on('data', function (data) {
  246. buffer += data
  247. })
  248. process.on('SIGINT', function () {
  249. processCSSFile(false, buffer)
  250. process.exit()
  251. })
  252. process.stdin.on('end', function () {
  253. processCSSFile(false, buffer)
  254. })
  255. }
  256. }