preprocessor.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. var fs = require('graceful-fs')
  2. var crypto = require('crypto')
  3. var mm = require('minimatch')
  4. var isBinaryFile = require('isbinaryfile')
  5. var combineLists = require('combine-lists')
  6. var log = require('./logger').create('preprocess')
  7. var sha1 = function (data) {
  8. var hash = crypto.createHash('sha1')
  9. hash.update(data)
  10. return hash.digest('hex')
  11. }
  12. var createNextProcessor = function (preprocessors, file, done) {
  13. return function nextPreprocessor (error, content) {
  14. // normalize B-C
  15. if (arguments.length === 1 && typeof error === 'string') {
  16. content = error
  17. error = null
  18. }
  19. if (error) {
  20. file.content = null
  21. file.contentPath = null
  22. return done(error)
  23. }
  24. if (!preprocessors.length) {
  25. file.contentPath = null
  26. file.content = content
  27. file.sha = sha1(content)
  28. return done()
  29. }
  30. preprocessors.shift()(content, file, nextPreprocessor)
  31. }
  32. }
  33. var createPreprocessor = function (config, basePath, injector) {
  34. var alreadyDisplayedErrors = {}
  35. var instances = {}
  36. var patterns = Object.keys(config)
  37. var emitter = injector.get('emitter')
  38. var instantiatePreprocessor = function (name) {
  39. if (alreadyDisplayedErrors[name]) {
  40. return
  41. }
  42. var p
  43. try {
  44. p = injector.get('preprocessor:' + name)
  45. } catch (e) {
  46. if (e.message.indexOf('No provider for "preprocessor:' + name + '"') !== -1) {
  47. log.error('Can not load "%s", it is not registered!\n ' +
  48. 'Perhaps you are missing some plugin?', name)
  49. } else {
  50. log.error('Can not load "%s"!\n ' + e.stack, name)
  51. }
  52. alreadyDisplayedErrors[name] = true
  53. emitter.emit('load_error', 'preprocessor', name)
  54. }
  55. return p
  56. }
  57. var allPreprocessors = []
  58. patterns.forEach(function (pattern) {
  59. allPreprocessors = combineLists(allPreprocessors, config[pattern])
  60. })
  61. allPreprocessors.forEach(instantiatePreprocessor)
  62. return function preprocess (file, done) {
  63. patterns = Object.keys(config)
  64. var retryCount = 0
  65. var maxRetries = 3
  66. function readFileCallback (err, buffer) {
  67. if (err) {
  68. log.warn(err)
  69. if (retryCount < maxRetries) {
  70. retryCount++
  71. log.warn('retrying ' + retryCount)
  72. fs.readFile(file.originalPath, readFileCallback)
  73. return
  74. } else {
  75. throw err
  76. }
  77. }
  78. isBinaryFile(buffer, buffer.length, function (err, thisFileIsBinary) {
  79. if (err) {
  80. throw err
  81. }
  82. var preprocessorNames = []
  83. for (var i = 0; i < patterns.length; i++) {
  84. if (mm(file.originalPath, patterns[i], {dot: true})) {
  85. if (thisFileIsBinary) {
  86. log.warn('Ignoring preprocessing (%s) %s because it is a binary file.',
  87. config[patterns[i]].join(', '), file.originalPath)
  88. } else {
  89. preprocessorNames = combineLists(preprocessorNames, config[patterns[i]])
  90. }
  91. }
  92. }
  93. var preprocessors = []
  94. var nextPreprocessor = createNextProcessor(preprocessors, file, done)
  95. preprocessorNames.forEach(function (name) {
  96. var p = instances[name]
  97. if (p == null) {
  98. p = instantiatePreprocessor(name)
  99. }
  100. if (p == null) {
  101. if (!alreadyDisplayedErrors[name]) {
  102. alreadyDisplayedErrors[name] = true
  103. log.error('Failed to instantiate preprocessor %s', name)
  104. emitter.emit('load_error', 'preprocessor', name)
  105. }
  106. return
  107. }
  108. instances[name] = p
  109. preprocessors.push(p)
  110. })
  111. nextPreprocessor(null, thisFileIsBinary ? buffer : buffer.toString())
  112. })
  113. }
  114. return fs.readFile(file.originalPath, readFileCallback)
  115. }
  116. }
  117. createPreprocessor.$inject = ['config.preprocessors', 'config.basePath', 'injector']
  118. exports.createPreprocessor = createPreprocessor