utils.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  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. },
  63. /**
  64. * Creates a function adding transpilation pipes to the `pipes` param.
  65. * Used to generate `formats.reduce()` callback where `formats` is an array
  66. * of formats that should be generated.
  67. *
  68. * @param {String} distDir The `dist/` directory path.
  69. * @returns {Function}
  70. */
  71. addFormat( distDir ) {
  72. return ( pipes, format ) => {
  73. const conversionPipes = [];
  74. conversionPipes.push( utils.pickVersionedFile( format ) );
  75. if ( format != 'esnext' ) {
  76. conversionPipes.push( utils.transpile( format ) );
  77. }
  78. conversionPipes.push(
  79. utils.dist( distDir, format )
  80. .on( 'data', ( file ) => {
  81. gutil.log( `Finished writing '${ gutil.colors.cyan( file.path ) }'` );
  82. } )
  83. );
  84. pipes.push( multipipe.apply( null, conversionPipes ) );
  85. return pipes;
  86. };
  87. },
  88. /**
  89. * Allows us to pick one of files suffixed with the format (`__esnext`, `__amd`, or `__cjs`).
  90. *
  91. * For example: we have `load__esnext.js`, `load__amd.js` and `load__cjs.js`. After applying this
  92. * transformation when compiling code for a specific format the proper file will be renamed to `load.js`.
  93. *
  94. * @param {String} format
  95. * @returns {Stream}
  96. */
  97. pickVersionedFile( format ) {
  98. return rename( ( path ) => {
  99. const regexp = new RegExp( `__${ format }$` );
  100. path.basename = path.basename.replace( regexp, '' );
  101. } );
  102. },
  103. /**
  104. * Moves files out of `ckeditor5-xxx/src/*` directories to `ckeditor5-xxx/*`.
  105. *
  106. * @returns {Stream}
  107. */
  108. unpackModules() {
  109. return rename( ( file ) => {
  110. const dir = file.dirname.split( path.sep );
  111. // Validate the input for the clear conscious.
  112. if ( dir[ 0 ].indexOf( 'ckeditor5-' ) !== 0 ) {
  113. throw new Error( 'Path should start with "ckeditor5-".' );
  114. }
  115. if ( dir[ 1 ] != 'src' ) {
  116. throw new Error( 'Path should start with "ckeditor5-*/src".' );
  117. }
  118. // Remove 'src'.
  119. dir.splice( 1, 1 );
  120. file.dirname = path.join.apply( null, dir );
  121. } );
  122. },
  123. /**
  124. * Adds `ckeditor5/` to a file path.
  125. *
  126. * @returns {Stream}
  127. */
  128. wrapCKEditor5Module() {
  129. return rename( ( file ) => {
  130. file.dirname = path.join( file.dirname, 'ckeditor5' );
  131. } );
  132. },
  133. /**
  134. * Appends file extension to file URLs. Tries to not touch named modules.
  135. *
  136. * @param {String} source
  137. * @returns {String}
  138. */
  139. appendModuleExtension( source ) {
  140. if ( /^https?:|\.[\/\\]/.test( source ) && !/\.js$/.test( source ) ) {
  141. return source + '.js';
  142. }
  143. return source;
  144. }
  145. };
  146. module.exports = utils;