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.

777 lines
20 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. /*
  2. All configurable options are defined inside build.config
  3. Please adjust this to your site's settings
  4. */
  5. /*******************************
  6. Set-up
  7. *******************************/
  8. var
  9. gulp = require('gulp-help')(require('gulp')),
  10. // node components & oddballs
  11. console = require('better-console'),
  12. del = require('del'),
  13. extend = require('extend'),
  14. fs = require('fs'),
  15. path = require('path'),
  16. wrench = require('wrench'),
  17. // gulp dependencies
  18. autoprefixer = require('gulp-autoprefixer'),
  19. clone = require('gulp-clone'),
  20. concat = require('gulp-concat'),
  21. concatCSS = require('gulp-concat-css'),
  22. copy = require('gulp-copy'),
  23. debug = require('gulp-debug'),
  24. flatten = require('gulp-flatten'),
  25. header = require('gulp-header'),
  26. jeditor = require('gulp-json-editor'),
  27. karma = require('gulp-karma'),
  28. less = require('gulp-less'),
  29. minifyCSS = require('gulp-minify-css'),
  30. notify = require('gulp-notify'),
  31. plumber = require('gulp-plumber'),
  32. print = require('gulp-print'),
  33. prompt = require('gulp-prompt'),
  34. rename = require('gulp-rename'),
  35. replace = require('gulp-replace'),
  36. sourcemaps = require('gulp-sourcemaps'),
  37. uglify = require('gulp-uglify'),
  38. util = require('gulp-util'),
  39. watch = require('gulp-watch'),
  40. // config
  41. banner = require('./tasks/banner'),
  42. comments = require('./tasks/comments'),
  43. defaults = require('./tasks/defaults'),
  44. log = require('./tasks/log'),
  45. questions = require('./tasks/questions'),
  46. settings = require('./tasks/gulp-settings'),
  47. // admin
  48. release = require('./tasks/admin/release'),
  49. git = require('gulp-git'),
  50. githubAPI = require('github'),
  51. oAuthToken = fs.existsSync('./tasks/admin/oauth.js')
  52. ? require('./tasks/admin/oauth')
  53. : false,
  54. github,
  55. // local
  56. runSetup = false,
  57. overwrite = true,
  58. config,
  59. package,
  60. github,
  61. // derived
  62. base,
  63. clean,
  64. output,
  65. source,
  66. assetPaths,
  67. componentGlob,
  68. // temporary
  69. folder
  70. ;
  71. /*******************************
  72. Read Settings
  73. *******************************/
  74. try {
  75. // try to load json
  76. var
  77. config = require(defaults.files.config),
  78. package = require(defaults.files.npm)
  79. ;
  80. }
  81. catch(error) {
  82. var config = false;
  83. }
  84. /*******************************
  85. Values Derived From Config
  86. *******************************/
  87. var
  88. getConfigValues = function() {
  89. if(!config) {
  90. runSetup = true;
  91. config = defaults;
  92. }
  93. config = extend(true, {}, defaults, config);
  94. // shorthand
  95. base = config.base;
  96. clean = config.paths.clean;
  97. output = config.paths.output;
  98. source = config.paths.source;
  99. // create glob for matching filenames from selected components
  100. componentGlob = (typeof config.components == 'object')
  101. ? (config.components.length > 1)
  102. ? '{' + config.components.join(',') + '}'
  103. : config.components[0]
  104. : ''
  105. ;
  106. // relative paths
  107. assetPaths = {
  108. uncompressed : path.relative(output.uncompressed, output.themes),
  109. compressed : path.relative(output.compressed, output.themes),
  110. packaged : path.relative(output.packaged, output.themes)
  111. };
  112. // add base to values
  113. for(var folder in source) {
  114. if(source.hasOwnProperty(folder)) {
  115. source[folder] = base + source[folder];
  116. }
  117. }
  118. for(folder in output) {
  119. if(output.hasOwnProperty(folder)) {
  120. output[folder] = base + output[folder];
  121. }
  122. }
  123. clean = base + clean;
  124. }
  125. ;
  126. getConfigValues();
  127. /*******************************
  128. Tasks
  129. *******************************/
  130. gulp.task('default', false, [
  131. 'check install'
  132. ]);
  133. gulp.task('watch', 'Watch for site/theme changes (Default Task)', function(callback) {
  134. console.clear();
  135. console.log('Watching source files for changes');
  136. if(!fs.existsSync(config.files.theme)) {
  137. console.error('Cant compile LESS. Run "grunt install" to create a theme config file');
  138. return;
  139. }
  140. // watching changes in style
  141. gulp
  142. .watch([
  143. source.config,
  144. source.definitions + '**/*.less',
  145. source.site + '**/*.{overrides,variables}',
  146. source.themes + '**/*.{overrides,variables}'
  147. ], function(file) {
  148. var
  149. srcPath,
  150. stream,
  151. compressedStream,
  152. uncompressedStream
  153. ;
  154. gulp.src(file.path)
  155. .pipe(print(log.modified))
  156. ;
  157. // recompile only definition file
  158. srcPath = util.replaceExtension(file.path, '.less');
  159. srcPath = srcPath.replace(config.regExp.themePath, source.definitions);
  160. srcPath = srcPath.replace(source.site, source.definitions);
  161. // get relative asset path (path returns wrong path? hardcoded)
  162. // assetPaths.source = path.relative(srcPath, path.resolve(source.themes));
  163. assetPaths.source = '../../themes';
  164. if( fs.existsSync(srcPath) ) {
  165. // unified css stream
  166. stream = gulp.src(srcPath)
  167. .pipe(plumber())
  168. //.pipe(sourcemaps.init())
  169. .pipe(less(settings.less))
  170. .pipe(replace(comments.variables.in, comments.variables.out))
  171. .pipe(replace(comments.large.in, comments.large.out))
  172. .pipe(replace(comments.small.in, comments.small.out))
  173. .pipe(replace(comments.tiny.in, comments.tiny.out))
  174. .pipe(autoprefixer(settings.prefix))
  175. ;
  176. // use 2 concurrent streams from same source
  177. uncompressedStream = stream.pipe(clone());
  178. compressedStream = stream.pipe(clone());
  179. uncompressedStream
  180. .pipe(replace(assetPaths.source, assetPaths.uncompressed))
  181. //.pipe(sourcemaps.write('/', settings.sourcemap))
  182. .pipe(header(banner, settings.header))
  183. .pipe(gulp.dest(output.uncompressed))
  184. .pipe(print(log.created))
  185. .on('end', function() {
  186. gulp.start('package uncompressed css');
  187. })
  188. ;
  189. compressedStream = stream
  190. .pipe(clone())
  191. .pipe(replace(assetPaths.source, assetPaths.compressed))
  192. .pipe(minifyCSS(settings.minify))
  193. .pipe(rename(settings.rename.minCSS))
  194. //.pipe(sourcemaps.write('/', settings.sourcemap))
  195. .pipe(header(banner, settings.header))
  196. .pipe(gulp.dest(output.compressed))
  197. .pipe(print(log.created))
  198. .on('end', function() {
  199. gulp.start('package compressed css');
  200. })
  201. ;
  202. }
  203. })
  204. ;
  205. // watch changes in assets
  206. gulp
  207. .watch([
  208. source.themes + '**/assets/**'
  209. ], function(file) {
  210. // copy assets
  211. gulp.src(file.path, { base: source.themes })
  212. .pipe(gulp.dest(output.themes))
  213. .pipe(print(log.created))
  214. ;
  215. })
  216. ;
  217. // watch changes in js
  218. gulp
  219. .watch([
  220. source.definitions + '**/*.js'
  221. ], function(file) {
  222. gulp.src(file.path)
  223. .pipe(gulp.dest(output.uncompressed))
  224. .pipe(print(log.created))
  225. .pipe(sourcemaps.init())
  226. .pipe(uglify(settings.uglify))
  227. .pipe(rename(settings.rename.minJS))
  228. .pipe(gulp.dest(output.compressed))
  229. .pipe(print(log.created))
  230. .on('end', function() {
  231. gulp.start('package compressed js');
  232. gulp.start('package uncompressed js');
  233. })
  234. ;
  235. })
  236. ;
  237. });
  238. // Builds all files
  239. gulp.task('build', 'Builds all files from source', function(callback) {
  240. var
  241. stream,
  242. compressedStream,
  243. uncompressedStream
  244. ;
  245. console.info('Building Semantic');
  246. if(!fs.existsSync(config.files.theme)) {
  247. console.error('Cant build LESS. Run "grunt install" to create a theme config file');
  248. return;
  249. }
  250. // get relative asset path (path returns wrong path?)
  251. // assetPaths.source = path.relative(srcPath, path.resolve(source.themes));
  252. assetPaths.source = '../../themes'; // hardcoded
  253. // copy assets
  254. gulp.src(source.themes + '**/assets/**')
  255. .pipe(gulp.dest(output.themes))
  256. ;
  257. // javascript stream
  258. gulp.src(source.definitions + '**/*.js')
  259. .pipe(flatten())
  260. .pipe(gulp.dest(output.uncompressed))
  261. .pipe(print(log.created))
  262. // .pipe(sourcemaps.init())
  263. .pipe(uglify(settings.uglify))
  264. .pipe(rename(settings.rename.minJS))
  265. .pipe(header(banner, settings.header))
  266. .pipe(gulp.dest(output.compressed))
  267. .pipe(print(log.created))
  268. .on('end', function() {
  269. gulp.start('package compressed js');
  270. gulp.start('package uncompressed js');
  271. })
  272. ;
  273. // unified css stream
  274. stream = gulp.src(source.definitions + '**/*.less')
  275. .pipe(plumber())
  276. //.pipe(sourcemaps.init())
  277. .pipe(less(settings.less))
  278. .pipe(flatten())
  279. .pipe(replace(comments.variables.in, comments.variables.out))
  280. .pipe(replace(comments.large.in, comments.large.out))
  281. .pipe(replace(comments.small.in, comments.small.out))
  282. .pipe(replace(comments.tiny.in, comments.tiny.out))
  283. .pipe(autoprefixer(settings.prefix))
  284. ;
  285. // use 2 concurrent streams from same source
  286. uncompressedStream = stream.pipe(clone());
  287. compressedStream = stream.pipe(clone());
  288. uncompressedStream
  289. .pipe(replace(assetPaths.source, assetPaths.uncompressed))
  290. //.pipe(sourcemaps.write('/', settings.sourcemap))
  291. .pipe(header(banner, settings.header))
  292. .pipe(gulp.dest(output.uncompressed))
  293. .pipe(print(log.created))
  294. .on('end', function() {
  295. gulp.start('package uncompressed css');
  296. })
  297. ;
  298. compressedStream = stream
  299. .pipe(clone())
  300. .pipe(replace(assetPaths.source, assetPaths.compressed))
  301. .pipe(minifyCSS(settings.minify))
  302. .pipe(rename(settings.rename.minCSS))
  303. //.pipe(sourcemaps.write('/', settings.sourcemap))
  304. .pipe(header(banner, settings.header))
  305. .pipe(gulp.dest(output.compressed))
  306. .pipe(print(log.created))
  307. .on('end', function() {
  308. callback();
  309. gulp.start('package compressed css');
  310. })
  311. ;
  312. });
  313. // cleans distribution files
  314. gulp.task('clean', 'Clean dist folder', function(callback) {
  315. return del([clean], settings.del, callback);
  316. });
  317. gulp.task('version', 'Displays current version of Semantic', function(callback) {
  318. console.log('Semantic UI ' + package.version);
  319. });
  320. /*--------------
  321. Internal
  322. ---------------*/
  323. gulp.task('package uncompressed css', false, function() {
  324. return gulp.src(output.uncompressed + '**/' + componentGlob + '!(*.min|*.map).css')
  325. .pipe(replace(assetPaths.uncompressed, assetPaths.packaged))
  326. .pipe(concatCSS('semantic.css'))
  327. .pipe(gulp.dest(output.packaged))
  328. .pipe(print(log.created))
  329. ;
  330. });
  331. gulp.task('package compressed css', false, function() {
  332. return gulp.src(output.uncompressed + '**/' + componentGlob + '!(*.min|*.map).css')
  333. .pipe(replace(assetPaths.uncompressed, assetPaths.packaged))
  334. .pipe(concatCSS('semantic.min.css'))
  335. .pipe(minifyCSS(settings.minify))
  336. .pipe(header(banner, settings.header))
  337. .pipe(gulp.dest(output.packaged))
  338. .pipe(print(log.created))
  339. ;
  340. });
  341. gulp.task('package uncompressed js', false, function() {
  342. return gulp.src(output.uncompressed + '**/' + componentGlob + '!(*.min|*.map).js')
  343. .pipe(replace(assetPaths.uncompressed, assetPaths.packaged))
  344. .pipe(concat('semantic.js'))
  345. .pipe(header(banner, settings.header))
  346. .pipe(gulp.dest(output.packaged))
  347. .pipe(print(log.created))
  348. ;
  349. });
  350. gulp.task('package compressed js', false, function() {
  351. return gulp.src(output.uncompressed + '**/' + componentGlob + '!(*.min|*.map).js')
  352. .pipe(replace(assetPaths.uncompressed, assetPaths.packaged))
  353. .pipe(concat('semantic.min.js'))
  354. .pipe(uglify(settings.uglify))
  355. .pipe(header(banner, settings.header))
  356. .pipe(gulp.dest(output.packaged))
  357. .pipe(print(log.created))
  358. ;
  359. });
  360. /*--------------
  361. Config
  362. ---------------*/
  363. gulp.task('check install', false, function () {
  364. setTimeout(function() {
  365. if( runSetup || !fs.existsSync(config.files.site)) {
  366. console.log('No semantic.json file found. Starting install...');
  367. gulp.start('install');
  368. }
  369. else {
  370. gulp.start('watch');
  371. }
  372. }, 50);
  373. });
  374. gulp.task('install', 'Set-up project for first time', function () {
  375. console.clear();
  376. gulp
  377. .src('gulpfile.js')
  378. .pipe(prompt.prompt(questions.setup, function(answers) {
  379. var
  380. siteVariable = /@siteFolder .*\'(.*)/mg,
  381. siteDestination = answers.site || config.folders.site,
  382. pathToSite = path.relative(path.resolve(config.folders.theme), path.resolve(siteDestination)),
  383. sitePathReplace = "@siteFolder : '" + pathToSite + "/';",
  384. configExists = fs.existsSync(config.files.config),
  385. themeConfigExists = fs.existsSync(config.files.theme),
  386. siteExists = fs.existsSync(siteDestination),
  387. jsonSource = (configExists)
  388. ? config.files.config
  389. : config.templates.config,
  390. json = {
  391. paths: {
  392. source: {},
  393. output: {}
  394. }
  395. }
  396. ;
  397. // exit if config exists and user specifies no overwrite
  398. if(answers.overwrite !== undefined && answers.overwrite == 'no') {
  399. return;
  400. }
  401. console.clear();
  402. console.log('Installing');
  403. console.log('------------------------------');
  404. // create site files
  405. if(siteExists) {
  406. console.info('Site folder exists, merging files (no overwrite)', siteDestination);
  407. }
  408. else {
  409. console.info('Creating site theme folder', siteDestination);
  410. }
  411. // copy recursively without overwrite
  412. wrench.copyDirSyncRecursive(config.templates.site, siteDestination, settings.wrench.recursive);
  413. // adjust less variable for site folder location
  414. console.info('Adjusting @siteFolder', sitePathReplace);
  415. if(themeConfigExists) {
  416. gulp.src(config.files.site)
  417. .pipe(replace(siteVariable, sitePathReplace))
  418. .pipe(gulp.dest(config.folders.theme))
  419. ;
  420. }
  421. else {
  422. console.info('Creating src/theme.config (LESS config)');
  423. gulp.src(config.templates.theme)
  424. .pipe(rename({ extname : '' }))
  425. .pipe(replace(siteVariable, sitePathReplace))
  426. .pipe(gulp.dest(config.folders.theme))
  427. ;
  428. }
  429. // determine semantic.json config
  430. if(answers.components) {
  431. json.components = answers.components;
  432. }
  433. if(answers.dist) {
  434. answers.dist = answers.dist;
  435. json.paths.output = {
  436. packaged : answers.dist + '/',
  437. uncompressed : answers.dist + '/components/',
  438. compressed : answers.dist + '/components/',
  439. themes : answers.dist + '/themes/'
  440. };
  441. }
  442. if(answers.site) {
  443. json.paths.source.site = answers.site + '/';
  444. }
  445. if(answers.packaged) {
  446. json.paths.output.packaged = answers.packaged + '/';
  447. }
  448. if(answers.compressed) {
  449. json.paths.output.compressed = answers.compressed + '/';
  450. }
  451. if(answers.uncompressed) {
  452. json.paths.output.uncompressed = answers.uncompressed + '/';
  453. }
  454. // write semantic.json
  455. if(configExists) {
  456. console.info('Extending semantic.json (Gulp config)');
  457. gulp.src(jsonSource)
  458. .pipe(plumber())
  459. .pipe(rename(settings.rename.json))
  460. .pipe(jeditor(json))
  461. .pipe(gulp.dest('./'))
  462. ;
  463. }
  464. else {
  465. console.info('Creating semantic.json (Gulp config)');
  466. gulp.src(jsonSource)
  467. .pipe(plumber())
  468. .pipe(rename({ extname : '' }))
  469. .pipe(jeditor(json))
  470. .pipe(gulp.dest('./'))
  471. ;
  472. }
  473. console.log('');
  474. console.log('');
  475. }))
  476. .pipe(prompt.prompt(questions.cleanup, function(answers) {
  477. if(answers.cleanup == 'yes') {
  478. del(config.setupFiles);
  479. }
  480. if(answers.build == 'yes') {
  481. config = require(config.files.config);
  482. getConfigValues();
  483. gulp.start('build');
  484. }
  485. }))
  486. ;
  487. });
  488. /*******************************
  489. Admin Tasks
  490. *******************************/
  491. var
  492. adminQuestions = require('./tasks/admin/questions')
  493. ;
  494. gulp.task('docs', false, function() {
  495. gulp
  496. .src('gulpfile.js')
  497. .pipe(prompt.prompt(adminQuestions.docs, function(answers) {
  498. }))
  499. ;
  500. });
  501. /* Moves watched files to static site generator output */
  502. gulp.task('serve-docs', false, function () {
  503. config = require('./tasks/admin/docs.json');
  504. getConfigValues();
  505. // copy source files
  506. gulp
  507. .watch([
  508. 'src/**/*.*'
  509. ], function(file) {
  510. console.clear();
  511. return gulp.src(file.path, { base: 'src/' })
  512. .pipe(gulp.dest(output.less))
  513. .pipe(print(log.created))
  514. ;
  515. })
  516. ;
  517. gulp.start('watch');
  518. });
  519. /* Builds files to docs source */
  520. gulp.task('build-docs', false, function () {
  521. console.clear();
  522. // pushes to docpad files
  523. config = require('./tasks/admin/docs.json');
  524. getConfigValues();
  525. gulp.start('build');
  526. // copy source
  527. gulp.src('src/**/*.*')
  528. .pipe(gulp.dest(output.less))
  529. .pipe(print(log.created))
  530. ;
  531. });
  532. /* Release */
  533. gulp.task('release', false, function() {
  534. // gulp bump
  535. // Ask for release type (minor, major, patch)
  536. // Bump package.json
  537. // Bump composer.json
  538. if(!oAuthToken) {
  539. console.error('Must add node include tasks/admin/oauth.js with oauth token for GitHub');
  540. return;
  541. }
  542. github = new githubAPI({
  543. version : '3.0.0',
  544. debug : true,
  545. protocol : 'https',
  546. timeout : 5000
  547. });
  548. github.authenticate({
  549. type: "oauth",
  550. token: oAuthToken
  551. });
  552. // gulp build
  553. gulp.start('release components');
  554. // release-component
  555. // #Create SCSS Version
  556. // #Create RTL Release
  557. });
  558. /* Bump Version */
  559. gulp.task('bump', false, function () {
  560. // bump package.json
  561. // bump composer.json
  562. });
  563. /*--------------
  564. Internal
  565. ---------------*/
  566. //gulp.task('release components', false, ['build', 'copy release components'], function() {
  567. gulp.task('release components', false, ['copy release components'], function() {
  568. var
  569. index = 0,
  570. total = release.components.length,
  571. stream,
  572. stepGit
  573. ;
  574. console.log('Handling git');
  575. // Do Git commands synchronously, to avoid issues
  576. stepGit = function() {
  577. index = index + 1;
  578. if(index >= total) {
  579. return;
  580. }
  581. var
  582. component = release.components[index],
  583. outputDirectory = release.folderRoot + component,
  584. capitalizedComponent = component.charAt(0).toUpperCase() + component.slice(1),
  585. repo = release.repoRoot + capitalizedComponent,
  586. gitURL = 'git@github.com:' + release.owner + '/' + repo + '.git',
  587. repoURL = 'https://github.com/' + release.owner + '/' + repo + '/',
  588. gitOptions = { cwd: path.resolve(outputDirectory) }
  589. ;
  590. // exit conditions
  591. if(index > total) {
  592. return;
  593. }
  594. // try pull
  595. git.pull('origin', 'master', gitOptions, function(error) {
  596. if(error) {
  597. // initialize local repo
  598. git.init(gitOptions, function(error) {
  599. if(error) {
  600. console.error('Error initializing repo', error);
  601. return;
  602. }
  603. // add remote url
  604. git.addRemote('origin', gitURL, gitOptions, function(error) {
  605. if(error) {
  606. console.error('Unable to add remote', error);
  607. }
  608. // try pull
  609. git.pull('origin', 'master', gitOptions, function(error) {
  610. if(error) {
  611. // Repo doesnt exist creating
  612. }
  613. stepGit();
  614. });
  615. });
  616. });
  617. }
  618. else {
  619. stepGit();
  620. }
  621. });
  622. };
  623. stepGit();
  624. // create bower.json *ignore*
  625. /*
  626. // check if is a repo locally
  627. git
  628. .add('./')
  629. ;
  630. // if not try creating repo
  631. github.repos.get({
  632. user : release.owner,
  633. repo : release.repo
  634. }, function(error, response) {
  635. if(error) {
  636. console.log(error);
  637. }
  638. else {
  639. console.log(JSON.stringify(response));
  640. }
  641. });
  642. // after create add remote to git
  643. */
  644. // create tagged version
  645. // Copy dist/components/{name}(.css|.min.css|.min.js|.js) to "../ui-{name}/"
  646. // (manually copy over asset changes)
  647. });
  648. gulp.task('copy release components', false, function() {
  649. var
  650. stream,
  651. index
  652. ;
  653. console.log('Moving files to component folders');
  654. for(index in release.components) {
  655. var
  656. component = release.components[index],
  657. outputDirectory = release.folderRoot + component
  658. ;
  659. // copy files into folder
  660. stream = gulp.src(output.compressed + component + '.*')
  661. .pipe(plumber())
  662. .pipe(flatten())
  663. .pipe(gulp.dest(outputDirectory)) // pipe to output directory
  664. ;
  665. }
  666. });