utils.js 4.7 KB

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