8
0

utils.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /* jshint node: true, esnext: true */
  2. 'use strict';
  3. const path = require( 'path' );
  4. const gulp = require( 'gulp' );
  5. const rename = require( 'gulp-rename' );
  6. const babel = require( 'gulp-babel' );
  7. const gulpWatch = require( 'gulp-watch' );
  8. const gulpPlumber = require( 'gulp-plumber' );
  9. const gutil = require( 'gulp-util' );
  10. const multipipe = require( 'multipipe' );
  11. const PassThrough = require( 'stream' ).PassThrough;
  12. const utils = {
  13. /**
  14. * Creates a pass-through stream.
  15. *
  16. * @returns {Stream}
  17. */
  18. noop() {
  19. return new PassThrough( { objectMode: true } );
  20. },
  21. /**
  22. * Transforms a stream of files into a watched stream of files.
  23. *
  24. * @param {String} glob The glob pattern to watch.
  25. * @param {Object} opts Gulp watcher opts.
  26. * @returns {Stream}
  27. */
  28. watch( glob, opts ) {
  29. return multipipe(
  30. // Note: we're using plumber only when watching. In other cases we should fail quickly and loudly.
  31. gulpPlumber(),
  32. gulpWatch( glob, opts )
  33. );
  34. },
  35. /**
  36. * Saves the files piped into this stream to the `dist/` directory.
  37. *
  38. * @param {String} distDir The `dist/` directory path.
  39. * @param {String} format The format of the distribution (`esnext`, `amd`, or `cjs`).
  40. * @returns {Stream}
  41. */
  42. dist( distDir, format ) {
  43. const destDir = path.join( distDir, format );
  44. return gulp.dest( destDir );
  45. },
  46. /**
  47. * Transpiles files piped into this stream to the given format (`amd` or `cjs`).
  48. *
  49. * @param {String} format
  50. * @returns {Stream}
  51. */
  52. transpile( format ) {
  53. const babelModuleTranspilers = {
  54. amd: 'amd',
  55. cjs: 'commonjs'
  56. };
  57. const babelModuleTranspiler = babelModuleTranspilers[ format ];
  58. if ( !babelModuleTranspiler ) {
  59. throw new Error( `Incorrect format: ${ format }` );
  60. }
  61. return babel( {
  62. plugins: [
  63. // Note: When plugin is specified by its name, Babel loads it from a context of a
  64. // currently transpiled file (in our case - e.g. from ckeditor5-core/src/foo.js).
  65. // Obviously that fails, since we have all the plugins installed only in ckeditor5/
  66. // and we want to have them only there to avoid installing them dozens of times.
  67. //
  68. // Anyway, I haven't found in the docs that you can also pass a plugin instance here,
  69. // but it works... so let's hope it will.
  70. require( `babel-plugin-transform-es2015-modules-${ babelModuleTranspiler }` )
  71. ],
  72. // Ensure that all paths ends with '.js' because Require.JS (unlike Common.JS/System.JS)
  73. // will not add it to module names which look like paths.
  74. resolveModuleSource: ( source ) => {
  75. return utils.appendModuleExtension( source );
  76. }
  77. } );
  78. },
  79. /**
  80. * Creates a function adding transpilation pipes to the `pipes` param.
  81. * Used to generate `formats.reduce()` callback where `formats` is an array
  82. * of formats that should be generated.
  83. *
  84. * @param {String} distDir The `dist/` directory path.
  85. * @returns {Function}
  86. */
  87. addFormat( distDir ) {
  88. return ( pipes, format ) => {
  89. const conversionPipes = [];
  90. conversionPipes.push( utils.pickVersionedFile( format ) );
  91. if ( format != 'esnext' ) {
  92. conversionPipes.push( utils.transpile( format ) );
  93. }
  94. conversionPipes.push(
  95. utils.dist( distDir, format )
  96. .on( 'data', ( file ) => {
  97. gutil.log( `Finished writing '${ gutil.colors.cyan( file.path ) }'` );
  98. } )
  99. );
  100. pipes.push( multipipe.apply( null, conversionPipes ) );
  101. return pipes;
  102. };
  103. },
  104. /**
  105. * Allows us to pick one of files suffixed with the format (`__esnext`, `__amd`, or `__cjs`).
  106. *
  107. * For example: we have `load__esnext.js`, `load__amd.js` and `load__cjs.js`. After applying this
  108. * transformation when compiling code for a specific format the proper file will be renamed to `load.js`.
  109. *
  110. * @param {String} format
  111. * @returns {Stream}
  112. */
  113. pickVersionedFile( format ) {
  114. return rename( ( path ) => {
  115. const regexp = new RegExp( `__${ format }$` );
  116. path.basename = path.basename.replace( regexp, '' );
  117. } );
  118. },
  119. /**
  120. * Moves files out of `ckeditor5-xxx/src/*` directories to `ckeditor5-xxx/*`.
  121. *
  122. * @returns {Stream}
  123. */
  124. unpackModules() {
  125. return rename( ( file ) => {
  126. const dir = file.dirname.split( path.sep );
  127. // Validate the input for the clear conscious.
  128. if ( dir[ 0 ].indexOf( 'ckeditor5-' ) !== 0 ) {
  129. throw new Error( 'Path should start with "ckeditor5-".' );
  130. }
  131. if ( dir[ 1 ] != 'src' ) {
  132. throw new Error( 'Path should start with "ckeditor5-*/src".' );
  133. }
  134. // Remove 'src'.
  135. dir.splice( 1, 1 );
  136. file.dirname = path.join.apply( null, dir );
  137. } );
  138. },
  139. /**
  140. * Adds `ckeditor5/` to a file path.
  141. *
  142. * @returns {Stream}
  143. */
  144. wrapCKEditor5Module() {
  145. return rename( ( file ) => {
  146. file.dirname = path.join( file.dirname, 'ckeditor5' );
  147. } );
  148. },
  149. /**
  150. * Appends file extension to file URLs. Tries to not touch named modules.
  151. *
  152. * @param {String} source
  153. * @returns {String}
  154. */
  155. appendModuleExtension( source ) {
  156. if ( /^https?:|\.[\/\\]/.test( source ) && !/\.js$/.test( source ) ) {
  157. return source + '.js';
  158. }
  159. return source;
  160. }
  161. };
  162. module.exports = utils;