8
0

tools.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. 'use strict';
  6. const gutil = require( 'gulp-util' );
  7. module.exports = {
  8. /**
  9. * Executes a shell command.
  10. *
  11. * @param {String} command The command to be executed.
  12. * @param {Boolean} [logOutput] When set to `false` command's output will not be logged. When set to `true`,
  13. * stdout and stderr will be logged. Defaults to `true`.
  14. * @returns {String} The command output.
  15. */
  16. shExec( command, logOutput ) {
  17. const sh = require( 'shelljs' );
  18. sh.config.silent = true;
  19. logOutput = logOutput !== false;
  20. const ret = sh.exec( command );
  21. const logColor = ret.code ? gutil.colors.red : gutil.colors.grey;
  22. if ( logOutput ) {
  23. if ( ret.stdout ) {
  24. console.log( '\n' + logColor( ret.stdout.trim() ) + '\n' );
  25. }
  26. if ( ret.stderr ) {
  27. console.log( '\n' + gutil.colors.grey( ret.stderr.trim() ) + '\n' );
  28. }
  29. }
  30. if ( ret.code ) {
  31. throw new Error( `Error while executing ${ command }: ${ ret.stderr }` );
  32. }
  33. return ret.stdout;
  34. },
  35. /**
  36. * Links directory located in source path to directory located in destination path.
  37. * @param {String} source
  38. * @param {String} destination
  39. */
  40. linkDirectories( source, destination ) {
  41. const fs = require( 'fs' );
  42. // Remove destination directory if exists.
  43. if ( this.isSymlink( destination ) ) {
  44. this.removeSymlink( destination );
  45. } else if ( this.isDirectory( destination ) ) {
  46. this.shExec( `rm -rf ${ destination }` );
  47. }
  48. fs.symlinkSync( source, destination, 'dir' );
  49. },
  50. /**
  51. * Returns array with all directories under specified path.
  52. *
  53. * @param {String} path
  54. * @returns {Array}
  55. */
  56. getDirectories( path ) {
  57. const fs = require( 'fs' );
  58. const pth = require( 'path' );
  59. return fs.readdirSync( path ).filter( item => {
  60. return this.isDirectory( pth.join( path, item ) );
  61. } );
  62. },
  63. /**
  64. * Returns true if path points to existing directory.
  65. *
  66. * @param {String} path
  67. * @returns {Boolean}
  68. */
  69. isDirectory( path ) {
  70. const fs = require( 'fs' );
  71. try {
  72. return fs.statSync( path ).isDirectory();
  73. } catch ( e ) {}
  74. return false;
  75. },
  76. /**
  77. * Returns true if path points to existing file.
  78. *
  79. * @param {String} path
  80. * @returns {Boolean}
  81. */
  82. isFile( path ) {
  83. const fs = require( 'fs' );
  84. try {
  85. return fs.statSync( path ).isFile();
  86. } catch ( e ) {}
  87. return false;
  88. },
  89. /**
  90. * Returns true if path points to symbolic link.
  91. *
  92. * @param {String} path
  93. */
  94. isSymlink( path ) {
  95. const fs = require( 'fs' );
  96. try {
  97. return fs.lstatSync( path ).isSymbolicLink();
  98. } catch ( e ) {}
  99. return false;
  100. },
  101. /**
  102. * Updates JSON file under specified path.
  103. * @param {String} path Path to file on disk.
  104. * @param {Function} updateFunction Function that will be called with parsed JSON object. It should return
  105. * modified JSON object to save.
  106. */
  107. updateJSONFile( path, updateFunction ) {
  108. const fs = require( 'fs' );
  109. const contents = fs.readFileSync( path, 'utf-8' );
  110. let json = JSON.parse( contents );
  111. json = updateFunction( json );
  112. fs.writeFileSync( path, JSON.stringify( json, null, 2 ) + '\n', 'utf-8' );
  113. },
  114. /**
  115. * Reinserts all object's properties in alphabetical order (character's Unicode value).
  116. * Used for JSON.stringify method which takes keys in insertion order.
  117. *
  118. * @param { Object } obj
  119. * @returns { Object } Same object with sorted keys.
  120. */
  121. sortObject( obj ) {
  122. Object.keys( obj ).sort().forEach( key => {
  123. const val = obj[ key ];
  124. delete obj[ key ];
  125. obj[ key ] = val;
  126. } );
  127. return obj;
  128. },
  129. /**
  130. * Returns name of the NPM module located under provided path.
  131. *
  132. * @param {String} modulePath Path to NPM module.
  133. */
  134. readPackageName( modulePath ) {
  135. const fs = require( 'fs' );
  136. const path = require( 'path' );
  137. const packageJSONPath = path.join( modulePath, 'package.json' );
  138. if ( !this.isFile( packageJSONPath ) ) {
  139. return null;
  140. }
  141. const contents = fs.readFileSync( packageJSONPath, 'utf-8' );
  142. const json = JSON.parse( contents );
  143. return json.name || null;
  144. },
  145. /**
  146. * Calls `npm install` command in specified path.
  147. *
  148. * @param {String} path
  149. */
  150. npmInstall( path ) {
  151. this.shExec( `cd ${ path } && npm install` );
  152. },
  153. /**
  154. * Calls `npm uninstall <name>` command in specified path.
  155. *
  156. * @param {String} path
  157. * @param {String} name
  158. */
  159. npmUninstall( path, name ) {
  160. this.shExec( `cd ${ path } && npm uninstall ${ name }` );
  161. },
  162. /**
  163. * Calls `npm update --dev` command in specified path.
  164. *
  165. * @param {String} path
  166. */
  167. npmUpdate( path ) {
  168. this.shExec( `cd ${ path } && npm update --dev` );
  169. },
  170. /**
  171. * Copies source files into destination directory and replaces contents of the file using provided `replace` object.
  172. *
  173. * // Each occurrence of `{{appName}}` inside README.md and CHANGES.md will be changed to `ckeditor5`.
  174. * tools.copyTemplateFiles( [ 'README.md', 'CHANGES.md' ], '/new/path', { '{{AppName}}': 'ckeditor5' } );
  175. *
  176. * @param {Array} sources Source files.
  177. * @param {String} destination Path to destination directory.
  178. * @param {Object} [replace] Object with data to fill template. Method will take object's keys and replace their
  179. * occurrences with value stored under that key.
  180. */
  181. copyTemplateFiles( sources, destination, replace ) {
  182. const path = require( 'path' );
  183. const fs = require( 'fs-extra' );
  184. replace = replace || {};
  185. destination = path.resolve( destination );
  186. const regexps = [];
  187. for ( let variableName in replace ) {
  188. regexps.push( variableName );
  189. }
  190. const regexp = new RegExp( regexps.join( '|' ), 'g' );
  191. const replaceFunction = ( matched ) => replace[ matched ];
  192. fs.ensureDirSync( destination );
  193. sources.forEach( source => {
  194. source = path.resolve( source );
  195. let fileData = fs.readFileSync( source, 'utf8' );
  196. fileData = fileData.replace( regexp, replaceFunction );
  197. fs.writeFileSync( path.join( destination, path.basename( source ) ), fileData, 'utf8' );
  198. } );
  199. },
  200. /**
  201. * Executes 'npm view' command for provided module name and returns Git url if one is found. Returns null if
  202. * module cannot be found.
  203. *
  204. * @param {String} name Name of the module.
  205. * @returns {*}
  206. */
  207. getGitUrlFromNpm( name ) {
  208. try {
  209. const info = JSON.parse( this.shExec( `npm view ${ name } repository --json`, false ) );
  210. if ( info && info.type == 'git' ) {
  211. return info.url;
  212. }
  213. } catch ( error ) {
  214. // Throw error only when different than E404.
  215. if ( error.message.indexOf( 'npm ERR! code E404' ) == -1 ) {
  216. throw error;
  217. }
  218. }
  219. return null;
  220. },
  221. /**
  222. * Unlinks symbolic link under specified path.
  223. *
  224. * @param {String} path
  225. */
  226. removeSymlink( path ) {
  227. const fs = require( 'fs' );
  228. fs.unlinkSync( path );
  229. }
  230. };