8
0

tools.js 6.9 KB

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