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.

763 lines
19 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
9 years ago
  1. /*******************************
  2. Set-up
  3. *******************************/
  4. var
  5. fs = require('fs'),
  6. path = require('path'),
  7. defaults = require('../defaults'),
  8. release = require('./release'),
  9. requireDotFile = require('require-dot-file')
  10. ;
  11. /*******************************
  12. When to Ask
  13. *******************************/
  14. /* Preconditions for install questions */
  15. var when = {
  16. // path
  17. changeRoot: function(questions) {
  18. return (questions.useRoot !== undefined && questions.useRoot !== true);
  19. },
  20. // permissions
  21. changePermissions: function(questions) {
  22. return (questions.changePermissions && questions.changePermissions === true);
  23. },
  24. // install
  25. hasConfig: function() {
  26. return requireDotFile('semantic.json', process.cwd());
  27. },
  28. allowOverwrite: function(questions) {
  29. return (questions.overwrite === undefined || questions.overwrite == 'yes');
  30. },
  31. notAuto: function(questions) {
  32. return (questions.install !== 'auto' && (questions.overwrite === undefined || questions.overwrite == 'yes'));
  33. },
  34. custom: function(questions) {
  35. return (questions.install === 'custom' && (questions.overwrite === undefined || questions.overwrite == 'yes'));
  36. },
  37. express: function(questions) {
  38. return (questions.install === 'express' && (questions.overwrite === undefined || questions.overwrite == 'yes'));
  39. },
  40. // customize
  41. customize: function(questions) {
  42. return (questions.customize === true);
  43. },
  44. primaryColor: function(questions) {
  45. return (questions.primaryColor);
  46. },
  47. secondaryColor: function(questions) {
  48. return (questions.secondaryColor);
  49. }
  50. };
  51. /*******************************
  52. Response Filters
  53. *******************************/
  54. /* Filters to user input from install questions */
  55. var filter = {
  56. removeTrailingSlash: function(path) {
  57. return path.replace(/(\/$|\\$)+/mg, '');
  58. }
  59. };
  60. /*******************************
  61. Configuration
  62. *******************************/
  63. module.exports = {
  64. // check whether install is setup
  65. isSetup: function() {
  66. return when.hasConfig();
  67. },
  68. // detect whether there is a semantic.json configuration and that the auto-install option is set to true
  69. shouldAutoInstall: function() {
  70. var
  71. config = when.hasConfig()
  72. ;
  73. return config['autoInstall'];
  74. },
  75. // checks if files are in a PM directory
  76. getPackageManager: function(directory) {
  77. var
  78. // returns last matching result (avoid sub-module detection)
  79. walk = function(directory) {
  80. var
  81. pathArray = directory.split(path.sep),
  82. folder = pathArray[pathArray.length - 1],
  83. nextDirectory = path.join(directory, path.sep, '..')
  84. ;
  85. if( folder == 'bower_components') {
  86. return {
  87. name: 'Bower',
  88. root: nextDirectory
  89. };
  90. }
  91. else if(folder == 'node_modules') {
  92. return {
  93. name: 'NPM',
  94. root: nextDirectory
  95. };
  96. }
  97. else if(folder == 'composer') {
  98. return {
  99. name: 'Composer',
  100. root: nextDirectory
  101. };
  102. }
  103. if(path.resolve(directory) == path.resolve(nextDirectory)) {
  104. return false;
  105. }
  106. // recurse downward
  107. return walk(nextDirectory);
  108. }
  109. ;
  110. // start walk from current directory if none specified
  111. directory = directory || (__dirname + path.sep);
  112. return walk(directory);
  113. },
  114. // checks if files is PMed submodule
  115. isSubModule: function(directory) {
  116. var
  117. moduleFolders = 0,
  118. walk = function(directory) {
  119. var
  120. pathArray = directory.split(path.sep),
  121. folder = pathArray[pathArray.length - 2],
  122. nextDirectory = path.join(directory, path.sep, '..')
  123. ;
  124. if( folder == 'bower_components') {
  125. moduleFolders++;
  126. }
  127. else if(folder == 'node_modules') {
  128. moduleFolders++;
  129. }
  130. else if(folder == 'composer') {
  131. moduleFolders++;
  132. }
  133. if(path.resolve(directory) == path.resolve(nextDirectory)) {
  134. return (moduleFolders > 1);
  135. }
  136. // recurse downward
  137. return walk(nextDirectory);
  138. }
  139. ;
  140. // start walk from current directory if none specified
  141. directory = directory || (__dirname + path.sep);
  142. return walk(directory);
  143. },
  144. createJSON: function(answers) {
  145. var
  146. json = {
  147. paths: {
  148. source: {},
  149. output: {}
  150. }
  151. }
  152. ;
  153. // add components
  154. if(answers.components) {
  155. json.components = answers.components;
  156. }
  157. // add rtl choice
  158. if(answers.rtl) {
  159. json.rtl = answers.rtl;
  160. }
  161. // add permissions
  162. if(answers.permission) {
  163. json.permission = answers.permission;
  164. }
  165. // add path to semantic
  166. if(answers.semanticRoot) {
  167. json.base = path.normalize(answers.semanticRoot);
  168. }
  169. // record version number to avoid re-installing on same version
  170. json.version = release.version;
  171. // add dist folder paths
  172. if(answers.dist) {
  173. answers.dist = path.normalize(answers.dist);
  174. json.paths.output = {
  175. packaged : path.normalize(answers.dist + '/'),
  176. uncompressed : path.normalize(answers.dist + '/components/'),
  177. compressed : path.normalize(answers.dist + '/components/'),
  178. themes : path.normalize(answers.dist + '/themes/')
  179. };
  180. }
  181. // add site path
  182. if(answers.site) {
  183. json.paths.source.site = path.normalize(answers.site + '/');
  184. }
  185. if(answers.packaged) {
  186. json.paths.output.packaged = path.normalize(answers.packaged + '/');
  187. }
  188. if(answers.compressed) {
  189. json.paths.output.compressed = path.normalize(answers.compressed + '/');
  190. }
  191. if(answers.uncompressed) {
  192. json.paths.output.uncompressed = path.normalize(answers.uncompressed + '/');
  193. }
  194. return json;
  195. },
  196. // files cleaned up after install
  197. setupFiles: [
  198. './src/theme.config.example',
  199. './semantic.json.example',
  200. './src/_site'
  201. ],
  202. regExp: {
  203. // used to match siteFolder variable in theme.less
  204. siteVariable: /@siteFolder .*\'(.*)/mg
  205. },
  206. // source paths (when installing)
  207. source: {
  208. config : './semantic.json.example',
  209. definitions : './src/definitions',
  210. gulpFile : './gulpfile.js',
  211. lessImport : './src/semantic.less',
  212. site : './src/_site',
  213. tasks : './tasks',
  214. themeConfig : './src/theme.config.example',
  215. themeImport : './src/theme.less',
  216. themes : './src/themes',
  217. defaultTheme : './src/themes/default',
  218. userGulpFile : './tasks/config/npm/gulpfile.js'
  219. },
  220. // expected final filenames
  221. files: {
  222. config : 'semantic.json',
  223. lessImport : 'src/semantic.less',
  224. site : 'src/site',
  225. themeConfig : 'src/theme.config',
  226. themeImport : 'src/theme.less'
  227. },
  228. // folder paths to files relative to root
  229. folders: {
  230. config : './',
  231. definitions : 'src/definitions/',
  232. lessImport : 'src/',
  233. modules : 'node_modules/',
  234. site : 'src/site/',
  235. tasks : 'tasks/',
  236. themeConfig : 'src/',
  237. themeImport : 'src/',
  238. themes : 'src/themes/',
  239. defaultTheme : 'default/' // only path that is relative to another directory and not root
  240. },
  241. // questions asked during install
  242. questions: {
  243. root: [
  244. {
  245. type : 'list',
  246. name : 'useRoot',
  247. message :
  248. ' \n' +
  249. ' {packageMessage} \n' +
  250. ' \n' +
  251. ' Is this your project folder?\n' +
  252. ' \x1b[92m{root}\x1b[0m \n' +
  253. ' \n ' +
  254. '\n',
  255. choices: [
  256. {
  257. name : 'Yes',
  258. value : true
  259. },
  260. {
  261. name : 'No, let me specify',
  262. value : false
  263. }
  264. ]
  265. },
  266. {
  267. type : 'input',
  268. name : 'customRoot',
  269. message : 'Please enter the absolute path to your project root',
  270. default : '/my/project/path',
  271. when : when.changeRoot
  272. },
  273. {
  274. type : 'input',
  275. name : 'semanticRoot',
  276. message : 'Where should we put Semantic UI inside your project?',
  277. default : 'semantic/'
  278. }
  279. ],
  280. setup: [
  281. {
  282. type: 'list',
  283. name: 'overwrite',
  284. message: 'It looks like you have a semantic.json file already.',
  285. when: when.hasConfig,
  286. choices: [
  287. {
  288. name: 'Yes, extend my current settings.',
  289. value: 'yes'
  290. },
  291. {
  292. name: 'Skip install',
  293. value: 'no'
  294. }
  295. ]
  296. },
  297. {
  298. type: 'list',
  299. name: 'install',
  300. message: 'Set-up Semantic UI',
  301. when: when.allowOverwrite,
  302. choices: [
  303. {
  304. name: 'Automatic (Use default locations and all components)',
  305. value: 'auto'
  306. },
  307. {
  308. name: 'Express (Set components and output folder)',
  309. value: 'express'
  310. },
  311. {
  312. name: 'Custom (Customize all src/dist values)',
  313. value: 'custom'
  314. }
  315. ]
  316. },
  317. {
  318. type: 'checkbox',
  319. name: 'components',
  320. message: 'What components should we include in the package?',
  321. // duplicated manually from tasks/defaults.js with additional property
  322. choices: [
  323. { name: "reset", checked: true },
  324. { name: "site", checked: true },
  325. { name: "button", checked: true },
  326. { name: "container", checked: true },
  327. { name: "divider", checked: true },
  328. { name: "flag", checked: true },
  329. { name: "header", checked: true },
  330. { name: "icon", checked: true },
  331. { name: "image", checked: true },
  332. { name: "input", checked: true },
  333. { name: "label", checked: true },
  334. { name: "list", checked: true },
  335. { name: "loader", checked: true },
  336. { name: "rail", checked: true },
  337. { name: "reveal", checked: true },
  338. { name: "segment", checked: true },
  339. { name: "step", checked: true },
  340. { name: "breadcrumb", checked: true },
  341. { name: "form", checked: true },
  342. { name: "grid", checked: true },
  343. { name: "menu", checked: true },
  344. { name: "message", checked: true },
  345. { name: "table", checked: true },
  346. { name: "ad", checked: true },
  347. { name: "card", checked: true },
  348. { name: "comment", checked: true },
  349. { name: "feed", checked: true },
  350. { name: "item", checked: true },
  351. { name: "statistic", checked: true },
  352. { name: "accordion", checked: true },
  353. { name: "checkbox", checked: true },
  354. { name: "dimmer", checked: true },
  355. { name: "dropdown", checked: true },
  356. { name: "embed", checked: true },
  357. { name: "modal", checked: true },
  358. { name: "nag", checked: true },
  359. { name: "popup", checked: true },
  360. { name: "progress", checked: true },
  361. { name: "rating", checked: true },
  362. { name: "search", checked: true },
  363. { name: "shape", checked: true },
  364. { name: "sidebar", checked: true },
  365. { name: "sticky", checked: true },
  366. { name: "tab", checked: true },
  367. { name: "transition", checked: true },
  368. { name: "api", checked: true },
  369. { name: "form", checked: true },
  370. { name: "state", checked: true },
  371. { name: "visibility", checked: true }
  372. ],
  373. when: when.notAuto
  374. },
  375. {
  376. type: 'list',
  377. name: 'changePermissions',
  378. when: when.notAuto,
  379. message: 'Should we set permissions on outputted files?',
  380. choices: [
  381. {
  382. name: 'No',
  383. value: false
  384. },
  385. {
  386. name: 'Yes',
  387. value: true
  388. }
  389. ]
  390. },
  391. {
  392. type: 'input',
  393. name: 'permission',
  394. message: 'What octal file permission should outputted files receive?',
  395. default: defaults.permission,
  396. when: when.changePermissions
  397. },
  398. {
  399. type: 'list',
  400. name: 'rtl',
  401. message: 'Do you use a RTL (Right-To-Left) language?',
  402. when: when.notAuto,
  403. choices: [
  404. {
  405. name: 'No',
  406. value: false
  407. },
  408. {
  409. name: 'Yes',
  410. value: true
  411. },
  412. {
  413. name: 'Build Both',
  414. value: 'both'
  415. }
  416. ]
  417. },
  418. {
  419. type: 'input',
  420. name: 'dist',
  421. message: 'Where should we output Semantic UI?',
  422. default: defaults.paths.output.packaged,
  423. filter: filter.removeTrailingSlash,
  424. when: when.express
  425. },
  426. {
  427. type: 'input',
  428. name: 'site',
  429. message: 'Where should we put your site folder?',
  430. default: defaults.paths.source.site,
  431. filter: filter.removeTrailingSlash,
  432. when: when.custom
  433. },
  434. {
  435. type: 'input',
  436. name: 'packaged',
  437. message: 'Where should we output a packaged version?',
  438. default: defaults.paths.output.packaged,
  439. filter: filter.removeTrailingSlash,
  440. when: when.custom
  441. },
  442. {
  443. type: 'input',
  444. name: 'compressed',
  445. message: 'Where should we output compressed components?',
  446. default: defaults.paths.output.compressed,
  447. filter: filter.removeTrailingSlash,
  448. when: when.custom
  449. },
  450. {
  451. type: 'input',
  452. name: 'uncompressed',
  453. message: 'Where should we output uncompressed components?',
  454. default: defaults.paths.output.uncompressed,
  455. filter: filter.removeTrailingSlash,
  456. when: when.custom
  457. }
  458. ],
  459. cleanup: [
  460. {
  461. type: 'list',
  462. name: 'cleanup',
  463. message: 'Should we remove set-up files?',
  464. choices: [
  465. {
  466. name: 'Yes (re-install will require redownloading semantic).',
  467. value: 'yes'
  468. },
  469. {
  470. name: 'No Thanks',
  471. value: 'no'
  472. }
  473. ]
  474. },
  475. {
  476. type: 'list',
  477. name: 'build',
  478. message: 'Do you want to build Semantic now?',
  479. choices: [
  480. {
  481. name: 'Yes',
  482. value: 'yes'
  483. },
  484. {
  485. name: 'No',
  486. value: 'no'
  487. }
  488. ]
  489. },
  490. ],
  491. site: [
  492. {
  493. type: 'list',
  494. name: 'customize',
  495. message: 'You have not yet customized your site, can we help you do that?',
  496. choices: [
  497. {
  498. name: 'Yes, ask me a few questions',
  499. value: true
  500. },
  501. {
  502. name: 'No I\'ll do it myself',
  503. value: false
  504. }
  505. ]
  506. },
  507. {
  508. type: 'list',
  509. name: 'headerFont',
  510. message: 'Select your header font',
  511. choices: [
  512. {
  513. name: 'Helvetica Neue, Arial, sans-serif',
  514. value: 'Helvetica Neue, Arial, sans-serif;'
  515. },
  516. {
  517. name: 'Lato (Google Fonts)',
  518. value: 'Lato'
  519. },
  520. {
  521. name: 'Open Sans (Google Fonts)',
  522. value: 'Open Sans'
  523. },
  524. {
  525. name: 'Source Sans Pro (Google Fonts)',
  526. value: 'Source Sans Pro'
  527. },
  528. {
  529. name: 'Droid (Google Fonts)',
  530. value: 'Droid'
  531. },
  532. {
  533. name: 'I\'ll choose on my own',
  534. value: false
  535. }
  536. ],
  537. when: when.customize
  538. },
  539. {
  540. type: 'list',
  541. name: 'pageFont',
  542. message: 'Select your page font',
  543. choices: [
  544. {
  545. name: 'Helvetica Neue, Arial, sans-serif',
  546. value: 'Helvetica Neue, Arial, sans-serif;'
  547. },
  548. {
  549. name: 'Lato (Import from Google Fonts)',
  550. value: 'Lato'
  551. },
  552. {
  553. name: 'Open Sans (Import from Google Fonts)',
  554. value: 'Open Sans'
  555. },
  556. {
  557. name: 'Source Sans Pro (Import from Google Fonts)',
  558. value: 'Source Sans Pro'
  559. },
  560. {
  561. name: 'Droid (Google Fonts)',
  562. value: 'Droid'
  563. },
  564. {
  565. name: 'I\'ll choose on my own',
  566. value: false
  567. }
  568. ],
  569. when: when.customize
  570. },
  571. {
  572. type: 'list',
  573. name: 'fontSize',
  574. message: 'Select your base font size',
  575. default: '14px',
  576. choices: [
  577. {
  578. name: '12px',
  579. },
  580. {
  581. name: '13px',
  582. },
  583. {
  584. name: '14px (Recommended)',
  585. value: '14px'
  586. },
  587. {
  588. name: '15px',
  589. },
  590. {
  591. name: '16px',
  592. },
  593. {
  594. name: 'I\'ll choose on my own',
  595. value: false
  596. }
  597. ],
  598. when: when.customize
  599. },
  600. {
  601. type: 'list',
  602. name: 'primaryColor',
  603. message: 'Select the closest name for your primary brand color',
  604. default: '14px',
  605. choices: [
  606. {
  607. name: 'Blue'
  608. },
  609. {
  610. name: 'Green'
  611. },
  612. {
  613. name: 'Orange'
  614. },
  615. {
  616. name: 'Pink'
  617. },
  618. {
  619. name: 'Purple'
  620. },
  621. {
  622. name: 'Red'
  623. },
  624. {
  625. name: 'Teal'
  626. },
  627. {
  628. name: 'Yellow'
  629. },
  630. {
  631. name: 'Black'
  632. },
  633. {
  634. name: 'I\'ll choose on my own',
  635. value: false
  636. }
  637. ],
  638. when: when.customize
  639. },
  640. {
  641. type: 'input',
  642. name: 'PrimaryHex',
  643. message: 'Enter a hexcode for your primary brand color',
  644. when: when.primaryColor
  645. },
  646. {
  647. type: 'list',
  648. name: 'secondaryColor',
  649. message: 'Select the closest name for your secondary brand color',
  650. default: '14px',
  651. choices: [
  652. {
  653. name: 'Blue'
  654. },
  655. {
  656. name: 'Green'
  657. },
  658. {
  659. name: 'Orange'
  660. },
  661. {
  662. name: 'Pink'
  663. },
  664. {
  665. name: 'Purple'
  666. },
  667. {
  668. name: 'Red'
  669. },
  670. {
  671. name: 'Teal'
  672. },
  673. {
  674. name: 'Yellow'
  675. },
  676. {
  677. name: 'Black'
  678. },
  679. {
  680. name: 'I\'ll choose on my own',
  681. value: false
  682. }
  683. ],
  684. when: when.customize
  685. },
  686. {
  687. type: 'input',
  688. name: 'secondaryHex',
  689. message: 'Enter a hexcode for your secondary brand color',
  690. when: when.secondaryColor
  691. }
  692. ]
  693. },
  694. settings: {
  695. /* Rename Files */
  696. rename: {
  697. json : { extname : '.json' }
  698. },
  699. /* Copy Install Folders */
  700. wrench: {
  701. // overwrite existing files update & install (default theme / definition)
  702. overwrite: {
  703. forceDelete : true,
  704. excludeHiddenUnix : true,
  705. preserveFiles : false
  706. },
  707. // only create files that don't exist (site theme update)
  708. merge: {
  709. forceDelete : false,
  710. excludeHiddenUnix : true,
  711. preserveFiles : true
  712. }
  713. }
  714. }
  715. };