build-examples.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. /**
  2. * build-examples.js
  3. * --------
  4. * Searches for each example's `js/app.es6` file.
  5. * Creates a new watchify instance for each `app.es6`.
  6. * Changes to Uppy's source will trigger rebundling.
  7. *
  8. * Run as:
  9. *
  10. * build-examples.js # to build all examples one-off
  11. * build-examples.js watch # to keep rebuilding examples with an internal watchify
  12. * build-examples.js <path> # to build just one example app.es6
  13. * build-examples.js <path> <path> # to build just one example app.es6 to a specific location
  14. *
  15. * Note:
  16. * Since each example is dependent on Uppy's source,
  17. * changing one source file causes the 'file changed'
  18. * notification to fire multiple times. To stop this,
  19. * files are added to a 'muted' Set that is checked
  20. * before announcing a changed file. It's removed from
  21. * the Set when it has been bundled.
  22. */
  23. const createStream = require('fs').createWriteStream
  24. const glob = require('multi-glob').glob
  25. const chalk = require('chalk')
  26. const path = require('path')
  27. const mkdirp = require('mkdirp')
  28. const notifier = require('node-notifier')
  29. const babelify = require('babelify')
  30. const aliasify = require('aliasify')
  31. const browserify = require('browserify')
  32. const watchify = require('watchify')
  33. const bresolve = require('browser-resolve')
  34. function useSourcePackages (b) {
  35. b._bresolve = (id, opts, cb) => {
  36. bresolve(id, opts, (err, result, pkg) => {
  37. if (err) return cb(err)
  38. if (/packages\/@uppy\/.*?\/lib\//.test(result)) {
  39. result = result.replace(/packages\/@uppy\/(.*?)\/lib\//, 'packages/@uppy/$1/src/')
  40. }
  41. cb(err, result, pkg)
  42. })
  43. }
  44. }
  45. const webRoot = __dirname
  46. let srcPattern = `${webRoot}/src/examples/**/app.es6`
  47. let dstPattern = `${webRoot}/public/examples/**/app.js`
  48. const watchifyEnabled = process.argv[2] === 'watch'
  49. const browserifyPlugins = [useSourcePackages]
  50. if (watchifyEnabled) {
  51. browserifyPlugins.push(watchify)
  52. }
  53. // Instead of 'watch', build-examples.js can also take a path as cli argument.
  54. // In this case we'll only bundle the specified path/pattern
  55. if (!watchifyEnabled && process.argv[2]) {
  56. srcPattern = process.argv[2]
  57. if (process.argv[3]) {
  58. dstPattern = process.argv[3]
  59. }
  60. }
  61. // Find each app.es6 file with glob.
  62. glob(srcPattern, (err, files) => {
  63. if (err) throw new Error(err)
  64. if (watchifyEnabled) {
  65. console.log('--> Watching examples..')
  66. }
  67. const muted = new Set()
  68. // Create a new watchify instance for each file.
  69. files.forEach((file) => {
  70. const b = browserify(file, {
  71. cache: {},
  72. packageCache: {},
  73. debug: true,
  74. plugin: browserifyPlugins
  75. })
  76. // Aliasing for using `require('uppy')`, etc.
  77. b
  78. .transform(babelify, {
  79. root: path.join(__dirname, '..')
  80. })
  81. .transform(aliasify, {
  82. aliases: {
  83. '@uppy': `./${path.relative(process.cwd(), path.join(__dirname, '../packages/@uppy'))}`
  84. }
  85. })
  86. // Listeners for changes, errors, and completion.
  87. b
  88. .on('update', bundle)
  89. .on('error', onError)
  90. .on('file', (file) => {
  91. // When file completes, unmute it.
  92. muted.delete(file)
  93. })
  94. // Call bundle() manually to start watch processes.
  95. bundle()
  96. /**
  97. * Creates bundle and writes it to static and public folders.
  98. * Changes to
  99. * @param {[type]} ids [description]
  100. * @return {[type]} [description]
  101. */
  102. function bundle (ids = []) {
  103. ids.forEach((id) => {
  104. if (!muted.has(id)) {
  105. console.info(chalk.cyan('change:'), path.relative(process.cwd(), id))
  106. muted.add(id)
  107. }
  108. })
  109. const exampleName = path.basename(path.dirname(file))
  110. const output = dstPattern.replace('**', exampleName)
  111. const parentDir = path.dirname(output)
  112. mkdirp.sync(parentDir)
  113. console.info(chalk.grey(`⏳ building: ${path.relative(process.cwd(), file)}`))
  114. b
  115. .bundle()
  116. .on('error', onError)
  117. .pipe(createStream(output))
  118. .on('finish', () => {
  119. console.info(chalk.green(`✓ built: ${path.relative(process.cwd(), file)}`))
  120. })
  121. }
  122. })
  123. })
  124. /**
  125. * Logs to console and shows desktop notification on error.
  126. * Calls `this.emit(end)` to stop bundling.
  127. * @param {object} err Error object
  128. */
  129. function onError (err) {
  130. console.error(chalk.red('✗ error:'), chalk.red(err.message))
  131. notifier.notify({
  132. title: 'Build failed:',
  133. message: err.message
  134. })
  135. this.emit('end')
  136. // When running without watch, process.exit(1) on error
  137. if (!watchifyEnabled) {
  138. process.exit(1)
  139. }
  140. }