You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

474 lines
14 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
5 years ago
5 years ago
5 years ago
9 years ago
  1. /*******************************
  2. Install Task
  3. *******************************/
  4. /*
  5. Install tasks
  6. For more notes
  7. * Runs automatically after npm update (hooks)
  8. * (NPM) Install - Will ask for where to put semantic (outside pm folder)
  9. * (NPM) Upgrade - Will look for semantic install, copy over files and update if new version
  10. * Standard installer runs asking for paths to site files etc
  11. */
  12. let
  13. gulp = require('gulp'),
  14. // node dependencies
  15. console = require('better-console'),
  16. extend = require('extend'),
  17. fs = require('fs'),
  18. mkdirp = require('mkdirp'),
  19. path = require('path'),
  20. // gulp dependencies
  21. chmod = require('gulp-chmod'),
  22. del = require('del'),
  23. jsonEditor = require('gulp-json-editor'),
  24. plumber = require('gulp-plumber'),
  25. prompt = require('prompt-sui'),
  26. rename = require('gulp-rename'),
  27. replace = require('gulp-replace'),
  28. requireDotFile = require('require-dot-file'),
  29. wrench = require('wrench-sui'),
  30. // install config
  31. install = require('./config/project/install'),
  32. // user config
  33. config = require('./config/user'),
  34. // release config (name/title/etc)
  35. release = require('./config/project/release'),
  36. {series, parallel, task} = gulp,
  37. // shorthand
  38. questions = install.questions,
  39. files = install.files,
  40. folders = install.folders,
  41. regExp = install.regExp,
  42. settings = install.settings,
  43. source = install.source,
  44. installer
  45. ;
  46. installer = function (callback) {
  47. let
  48. currentConfig = requireDotFile('semantic.json'),
  49. manager = install.getPackageManager(),
  50. rootQuestions = questions.root,
  51. installFolder = false,
  52. answers
  53. ;
  54. console.clear();
  55. /* Test NPM install
  56. manager = {
  57. name : 'NPM',
  58. root : path.normalize(__dirname + '/../')
  59. };
  60. */
  61. /* Don't do end user config if SUI is a sub-module */
  62. if( install.isSubModule() ) {
  63. console.info('SUI is a sub-module, skipping end-user install');
  64. return;
  65. }
  66. /*-----------------
  67. Update SUI
  68. -----------------*/
  69. // run update scripts if semantic.json exists
  70. if(currentConfig && manager.name === 'NPM') {
  71. let
  72. updateFolder = path.join(manager.root, currentConfig.base),
  73. updatePaths = {
  74. config : path.join(manager.root, files.config),
  75. tasks : path.join(updateFolder, folders.tasks),
  76. themeImport : path.join(updateFolder, folders.themeImport),
  77. definition : path.join(currentConfig.paths.source.definitions),
  78. site : path.join(currentConfig.paths.source.site),
  79. theme : path.join(currentConfig.paths.source.themes),
  80. defaultTheme : path.join(currentConfig.paths.source.themes, folders.defaultTheme)
  81. }
  82. ;
  83. // duck-type if there is a project installed
  84. if( fs.existsSync(updatePaths.definition) ) {
  85. // perform update if new version
  86. if(currentConfig.version !== release.version) {
  87. console.log('Updating Semantic UI from ' + currentConfig.version + ' to ' + release.version);
  88. console.info('Updating ui definitions...');
  89. wrench.copyDirSyncRecursive(source.definitions, updatePaths.definition, settings.wrench.overwrite);
  90. console.info('Updating default theme...');
  91. wrench.copyDirSyncRecursive(source.themes, updatePaths.theme, settings.wrench.merge);
  92. wrench.copyDirSyncRecursive(source.defaultTheme, updatePaths.defaultTheme, settings.wrench.overwrite);
  93. console.info('Updating tasks...');
  94. wrench.copyDirSyncRecursive(source.tasks, updatePaths.tasks, settings.wrench.overwrite);
  95. console.info('Updating gulpfile.js');
  96. gulp.src(source.userGulpFile)
  97. .pipe(plumber())
  98. .pipe(gulp.dest(updateFolder))
  99. ;
  100. // copy theme import
  101. console.info('Updating theme import file');
  102. gulp.src(source.themeImport)
  103. .pipe(plumber())
  104. .pipe(gulp.dest(updatePaths.themeImport))
  105. ;
  106. console.info('Adding new site theme files...');
  107. wrench.copyDirSyncRecursive(source.site, updatePaths.site, settings.wrench.merge);
  108. console.info('Updating version...');
  109. // update version number in semantic.json
  110. gulp.src(updatePaths.config)
  111. .pipe(plumber())
  112. .pipe(rename(settings.rename.json)) // preserve file extension
  113. .pipe(jsonEditor({
  114. version: release.version
  115. }))
  116. .pipe(gulp.dest(manager.root))
  117. ;
  118. console.info('Update complete! Run "\x1b[92mgulp build\x1b[0m" to rebuild dist/ files.');
  119. callback();
  120. return;
  121. }
  122. else {
  123. console.log('Current version of Semantic UI already installed');
  124. callback();
  125. return;
  126. }
  127. }
  128. else {
  129. console.error('Cannot locate files to update at path: ', updatePaths.definition);
  130. console.log('Running installer');
  131. }
  132. }
  133. /*--------------
  134. Determine Root
  135. ---------------*/
  136. // PM that supports Build Tools (NPM Only Now)
  137. if(manager.name == 'NPM') {
  138. rootQuestions[0].message = rootQuestions[0].message
  139. .replace('{packageMessage}', 'We detected you are using ' + manager.name + ' Nice!')
  140. .replace('{root}', manager.root)
  141. ;
  142. // set default path to detected PM root
  143. rootQuestions[0].default = manager.root;
  144. rootQuestions[1].default = manager.root;
  145. // insert PM questions after "Install Type" question
  146. Array.prototype.splice.apply(questions.setup, [2, 0].concat(rootQuestions));
  147. // omit cleanup questions for managed install
  148. questions.cleanup = [];
  149. }
  150. /*--------------
  151. Create SUI
  152. ---------------*/
  153. task('run setup', function() {
  154. // If auto-install is switched on, we skip the configuration section and simply reuse the configuration from semantic.json
  155. if(install.shouldAutoInstall()) {
  156. answers = {
  157. overwrite : 'yes',
  158. install : 'auto',
  159. useRoot : true,
  160. semanticRoot : currentConfig.base
  161. };
  162. }
  163. else {
  164. return gulp
  165. .src('gulpfile.js')
  166. .pipe(prompt.prompt(questions.setup, function(setupAnswers) {
  167. // hoist
  168. answers = setupAnswers;
  169. }))
  170. ;
  171. }
  172. });
  173. task('create install files', function(callback) {
  174. /*--------------
  175. Exit Conditions
  176. ---------------*/
  177. // if config exists and user specifies not to proceed
  178. if(answers.overwrite !== undefined && answers.overwrite == 'no') {
  179. callback();
  180. return;
  181. }
  182. console.clear();
  183. if(install.shouldAutoInstall()) {
  184. console.log('Auto-Installing (Without User Interaction)');
  185. }
  186. else {
  187. console.log('Installing');
  188. }
  189. console.log('------------------------------');
  190. /*--------------
  191. Paths
  192. ---------------*/
  193. let
  194. installPaths = {
  195. config : files.config,
  196. configFolder : folders.config,
  197. site : answers.site || folders.site,
  198. themeConfig : files.themeConfig,
  199. themeConfigFolder : folders.themeConfig
  200. }
  201. ;
  202. /*--------------
  203. NPM Install
  204. ---------------*/
  205. // Check if PM install
  206. if(manager && (answers.useRoot || answers.customRoot)) {
  207. // Set root to custom root path if set
  208. if(answers.customRoot) {
  209. if(answers.customRoot === '') {
  210. console.log('Unable to proceed, invalid project root');
  211. return;
  212. }
  213. manager.root = answers.customRoot;
  214. }
  215. // special install paths only for PM install
  216. installPaths = extend(false, {}, installPaths, {
  217. definition : folders.definitions,
  218. lessImport : folders.lessImport,
  219. tasks : folders.tasks,
  220. theme : folders.themes,
  221. defaultTheme : path.join(folders.themes, folders.defaultTheme),
  222. themeImport : folders.themeImport
  223. });
  224. // add project root to semantic root
  225. installFolder = path.join(manager.root, answers.semanticRoot);
  226. // add install folder to all output paths
  227. for(let destination in installPaths) {
  228. if( installPaths.hasOwnProperty(destination) ) {
  229. // config goes in project root, rest in install folder
  230. installPaths[destination] = (destination == 'config' || destination == 'configFolder')
  231. ? path.normalize( path.join(manager.root, installPaths[destination]) )
  232. : path.normalize( path.join(installFolder, installPaths[destination]) )
  233. ;
  234. }
  235. }
  236. // create project folders
  237. try {
  238. mkdirp.sync(installFolder);
  239. mkdirp.sync(installPaths.definition);
  240. mkdirp.sync(installPaths.theme);
  241. mkdirp.sync(installPaths.tasks);
  242. }
  243. catch(error) {
  244. console.error('NPM does not have permissions to create folders at your specified path. Adjust your folders permissions and run "npm install" again');
  245. }
  246. console.log('Installing to \x1b[92m' + answers.semanticRoot + '\x1b[0m');
  247. console.info('Copying UI definitions');
  248. wrench.copyDirSyncRecursive(source.definitions, installPaths.definition, settings.wrench.overwrite);
  249. console.info('Copying UI themes');
  250. wrench.copyDirSyncRecursive(source.themes, installPaths.theme, settings.wrench.merge);
  251. wrench.copyDirSyncRecursive(source.defaultTheme, installPaths.defaultTheme, settings.wrench.overwrite);
  252. console.info('Copying gulp tasks');
  253. wrench.copyDirSyncRecursive(source.tasks, installPaths.tasks, settings.wrench.overwrite);
  254. // copy theme import
  255. console.info('Adding theme files');
  256. gulp.src(source.themeImport)
  257. .pipe(plumber())
  258. .pipe(gulp.dest(installPaths.themeImport))
  259. ;
  260. gulp.src(source.lessImport)
  261. .pipe(plumber())
  262. .pipe(gulp.dest(installPaths.lessImport))
  263. ;
  264. // create gulp file
  265. console.info('Creating gulpfile.js');
  266. gulp.src(source.userGulpFile)
  267. .pipe(plumber())
  268. .pipe(gulp.dest(installFolder))
  269. ;
  270. }
  271. /*--------------
  272. Site Theme
  273. ---------------*/
  274. // Copy _site templates folder to destination
  275. if( fs.existsSync(installPaths.site) ) {
  276. console.info('Site folder exists, merging files (no overwrite)', installPaths.site);
  277. }
  278. else {
  279. console.info('Creating site theme folder', installPaths.site);
  280. }
  281. wrench.copyDirSyncRecursive(source.site, installPaths.site, settings.wrench.merge);
  282. /*--------------
  283. Theme Config
  284. ---------------*/
  285. task('create theme.config', function() {
  286. let
  287. // determine path to site theme folder from theme config
  288. // force CSS path variable to use forward slashes for paths
  289. pathToSite = path.relative(path.resolve(installPaths.themeConfigFolder), path.resolve(installPaths.site)).replace(/\\/g,'/'),
  290. siteVariable = "@siteFolder : '" + pathToSite + "/';"
  291. ;
  292. // rewrite site variable in theme.less
  293. console.info('Adjusting @siteFolder to: ', pathToSite + '/');
  294. if(fs.existsSync(installPaths.themeConfig)) {
  295. console.info('Modifying src/theme.config (LESS config)', installPaths.themeConfig);
  296. return gulp.src(installPaths.themeConfig)
  297. .pipe(plumber())
  298. .pipe(replace(regExp.siteVariable, siteVariable))
  299. .pipe(gulp.dest(installPaths.themeConfigFolder))
  300. ;
  301. }
  302. else {
  303. console.info('Creating src/theme.config (LESS config)', installPaths.themeConfig);
  304. return gulp.src(source.themeConfig)
  305. .pipe(plumber())
  306. .pipe(rename({ extname : '' }))
  307. .pipe(replace(regExp.siteVariable, siteVariable))
  308. .pipe(gulp.dest(installPaths.themeConfigFolder))
  309. ;
  310. }
  311. });
  312. /*--------------
  313. Semantic.json
  314. ---------------*/
  315. task('create semantic.json', function() {
  316. let
  317. jsonConfig = install.createJSON(answers)
  318. ;
  319. // adjust variables in theme.less
  320. if( fs.existsSync(installPaths.config) ) {
  321. console.info('Extending config file (semantic.json)', installPaths.config);
  322. return gulp.src(installPaths.config)
  323. .pipe(rename(settings.rename.json)) // preserve file extension
  324. .pipe(jsonEditor(jsonConfig))
  325. .pipe(gulp.dest(installPaths.configFolder))
  326. ;
  327. }
  328. else {
  329. console.info('Creating config file (semantic.json)', installPaths.config);
  330. return gulp.src(source.config)
  331. .pipe(plumber())
  332. .pipe(rename({ extname : '' })) // remove .template from ext
  333. .pipe(jsonEditor(jsonConfig))
  334. .pipe(gulp.dest(installPaths.configFolder))
  335. ;
  336. }
  337. });
  338. series(
  339. task('create theme.config'),
  340. task('create semantic.json'),
  341. function doSetupCallback(innerCallback) {
  342. innerCallback();
  343. callback();
  344. }
  345. )();
  346. });
  347. task('clean up install', function(callback) {
  348. // Completion Message
  349. if(installFolder && !install.shouldAutoInstall()) {
  350. console.log('\n Setup Complete! \n Installing Peer Dependencies. \x1b[0;31mPlease refrain from ctrl + c\x1b[0m... \n After completion navigate to \x1b[92m' + answers.semanticRoot + '\x1b[0m and run "\x1b[92mgulp build\x1b[0m" to build');
  351. callback();
  352. return;
  353. }
  354. else {
  355. console.log('');
  356. console.log('');
  357. }
  358. // If auto-install is switched on, we skip the configuration section and simply build the dependencies
  359. if(install.shouldAutoInstall()) {
  360. return gulp.start('build');
  361. }
  362. else {
  363. return gulp
  364. .src('gulpfile.js')
  365. .pipe(prompt.prompt(questions.cleanup, function(answers) {
  366. if(answers.cleanup == 'yes') {
  367. del(install.setupFiles);
  368. }
  369. if(answers.build == 'yes') {
  370. gulp.start('build');
  371. }
  372. }))
  373. ;
  374. }
  375. });
  376. series(
  377. task('run setup'),
  378. task('create install files'),
  379. task('clean up install'),
  380. function doInstallCallback(innerCallback) {
  381. innerCallback();
  382. callback();
  383. }
  384. )();
  385. };
  386. /* Export with Metadata */
  387. installer.displayName = 'install';
  388. installer.description = 'Installs semantic';
  389. module.exports = installer;