├── .editorconfig ├── .gitattributes ├── .gitignore ├── .idea ├── .name ├── encodings.xml ├── generator-meteor-react.iml ├── jsLibraryMappings.xml ├── libraries │ └── generator_meteor_react_node_modules.xml ├── misc.xml ├── modules.xml ├── scopes │ └── scope_settings.xml ├── vcs.xml └── workspace.xml ├── .jshintrc ├── .travis.yml ├── .yo-rc.json ├── README.md ├── app ├── index.js └── templates │ ├── _main.html │ ├── client │ ├── lib │ │ ├── layout │ │ │ └── layoutFull.html │ │ └── meteor │ │ │ ├── router.coffee │ │ │ ├── security.coffee │ │ │ └── startup.coffee │ └── views │ │ ├── 404 │ │ └── 404.html │ │ ├── about │ │ ├── about.coffee │ │ ├── about.html │ │ └── about.styl │ │ ├── home │ │ ├── home.coffee │ │ ├── home.html │ │ └── home.styl │ │ └── loading │ │ ├── loading.html │ │ └── loading.styl │ ├── collections │ └── modals │ │ └── navigation.js │ ├── components │ └── topnav │ │ └── topnav.jsx │ ├── config │ └── settings.json │ ├── server │ ├── lib │ │ └── startup.coffee │ └── publish │ │ └── navigation.js │ └── startup.sh ├── collection └── index.js ├── component └── index.js ├── package.json ├── templates ├── collection │ ├── modal.txt │ └── publish.txt ├── component │ ├── test.html │ └── test.txt └── view │ ├── view.coffee │ ├── view.html │ └── view.styl ├── test └── test-app.js └── view └── index.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | generator-meteor-react -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/generator-meteor-react.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/libraries/generator_meteor_react_node_modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 111 | 112 | 113 | true 114 | 115 | 116 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | CoffeeScript 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 170 | 171 | 172 | 173 | 176 | 177 | 180 | 181 | 182 | 183 | 186 | 187 | 190 | 191 | 194 | 195 | 196 | 197 | 200 | 201 | 204 | 205 | 208 | 209 | 210 | 211 | 214 | 215 | 218 | 219 | 222 | 223 | 224 | 225 | 228 | 229 | 232 | 233 | 236 | 237 | 238 | 239 | 242 | 243 | 246 | 247 | 250 | 251 | 254 | 255 | 256 | 257 | 260 | 261 | 264 | 265 | 268 | 269 | 272 | 273 | 276 | 277 | 280 | 281 | 282 | 283 | 286 | 287 | 290 | 291 | 294 | 295 | 298 | 299 | 302 | 303 | 306 | 307 | 308 | 309 | 312 | 313 | 316 | 317 | 320 | 321 | 324 | 325 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 405 | 406 | 407 | 408 | 409 | 410 | true 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 1426088973770 431 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 486 | 489 | 490 | 491 | 493 | 494 | 497 | 498 | 499 | 500 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": true, 5 | "camelcase": true, 6 | "curly": true, 7 | "eqeqeq": true, 8 | "immed": true, 9 | "indent": 2, 10 | "latedef": true, 11 | "newcap": true, 12 | "noarg": true, 13 | "quotmark": "single", 14 | "undef": true, 15 | "unused": true, 16 | "strict": true 17 | } 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - 'iojs' 5 | - '0.12' 6 | - '0.10' 7 | -------------------------------------------------------------------------------- /.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-generator": {} 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # generator-meteor-react 3 | 4 | > [Yeoman](http://yeoman.io) generator 5 | 6 | 7 | ## Getting Started 8 | 9 | ### What is Yeoman? 10 | 11 | Trick question. It's not a thing. It's this guy: 12 | 13 | ![](http://i.imgur.com/JHaAlBJ.png) 14 | 15 | Basically, he wears a top hat, lives in your computer, and waits for you to tell him what kind of application you wish to create. 16 | 17 | Not every new computer comes with a Yeoman pre-installed. He lives in the [npm](https://npmjs.org) package repository. You only have to ask for him once, then he packs up and moves into your hard drive. *Make sure you clean up, he likes new and shiny things.* 18 | 19 | ```bash 20 | npm install -g yo 21 | ``` 22 | 23 | ### Yeoman Generators 24 | 25 | Yeoman travels light. He didn't pack any generators when he moved in. You can think of a generator like a plug-in. You get to choose what type of application you wish to create, such as a Backbone application or even a Chrome extension. 26 | 27 | To install generator-meteor-react from npm, run: 28 | 29 | ```bash 30 | npm install -g generator-meteor-react 31 | ``` 32 | 33 | Finally, initiate the generator: 34 | 35 | ```bash 36 | meteor create myAmazingApp 37 | cd myAmazingApp 38 | 39 | you can delete the default .html, js & css files 40 | 41 | yo meteor-react 42 | sh startup.sh 43 | ``` 44 | 45 | 46 | 47 | To add a View: 48 | 49 | ```bash 50 | yo meteor-react:view [yourViewName] 51 | ``` 52 | 53 | To add a Collection: 54 | 55 | ```bash 56 | yo meteor-react:collection [yourCollectionName] 57 | ``` 58 | 59 | To add a React component: 60 | 61 | ```bash 62 | yo meteor-react:component [yourComponentName] 63 | ``` 64 | 65 | 66 | ### Why React components with Meteor? 67 | 68 | Because React.js is cool and worth learning 69 | 70 | 71 | Because it's easier to port a Meteor prototype to an enterprise CMS such as Sitecore, AEM or Backbase using React components.. React.js provides a common separation of the View that can be almost dropped onto another implementation 72 | 73 | 74 | ### Some example React magic comes with the default setup 75 | 76 | You will find a sample component "Topnav" comes with the default setup. This is just to demonstrate how a basic react component is set up and implemented. It's directly wired up to pull from the 'navigation' collection. And is added via the master layout. 77 | 78 | 79 | 80 | ### Getting To Know Yeoman 81 | 82 | Yeoman has a heart of gold. He's a person with feelings and opinions, but he's very easy to work with. If you think he's too opinionated, he can be easily convinced. 83 | 84 | If you'd like to get to know Yeoman better and meet some of his friends, [Grunt](http://gruntjs.com) and [Bower](http://bower.io), check out the complete [Getting Started Guide](https://github.com/yeoman/yeoman/wiki/Getting-Started). 85 | 86 | 87 | ## License 88 | 89 | MIT 90 | ======= 91 | # meteor-react 92 | Yeoman generator for Meteor and React.js views 93 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var yeoman = require('yeoman-generator'); 3 | var chalk = require('chalk'); 4 | var yosay = require('yosay'); 5 | 6 | module.exports = yeoman.generators.Base.extend({ 7 | 8 | prompting: function () { 9 | var done = this.async(); 10 | 11 | // Have Yeoman greet the user. 12 | this.log(yosay( 13 | 'Welcome to the good ' + chalk.red('MeteorReact') + ' generator!' 14 | )); 15 | 16 | var prompts = [{ 17 | type: 'confirm', 18 | name: 'someOption', 19 | message: 'Would you like to enable this option?', 20 | default: true 21 | }]; 22 | 23 | this.prompt(prompts, function (props) { 24 | this.someOption = props.someOption; 25 | 26 | done(); 27 | }.bind(this)); 28 | }, 29 | 30 | writing: { 31 | app: function () { 32 | this.mkdir("config"); 33 | this.mkdir("client"); 34 | this.mkdir("client/lib"); 35 | this.mkdir("client/lib/layout"); 36 | this.mkdir("client/lib/meteor"); 37 | this.mkdir("client/lib/compatibility"); 38 | this.mkdir("client/lib/meteor"); 39 | this.mkdir("client/views"); 40 | this.mkdir("client/views/404"); 41 | this.mkdir("client/views/loading"); 42 | this.mkdir("client/views/home"); 43 | this.mkdir("client/views/about"); 44 | this.mkdir("client/components"); 45 | this.mkdir("client/components/topnav"); 46 | this.mkdir("collections"); 47 | this.mkdir("collections/modals"); 48 | this.mkdir("server"); 49 | this.mkdir("server/lib"); 50 | this.mkdir("server/modules"); 51 | this.mkdir("server/publish"); 52 | }, 53 | 54 | projectfiles: function () { 55 | this.copy('startup.sh', 'startup.sh'); 56 | this.copy('config/settings.json', 'config/settings.json'); 57 | this.copy('client/lib/layout/layoutFull.html', 'client/lib/layout/layoutFull.html'); 58 | this.copy('client/lib/meteor/router.coffee', 'client/lib/meteor/router.coffee'); 59 | this.copy('client/lib/meteor/security.coffee', 'client/lib/meteor/security.coffee'); 60 | this.copy('client/lib/meteor/startup.coffee', 'client/lib/meteor/startup.coffee'); 61 | this.copy('client/views/404/404.html', 'client/views/404/404.html'); 62 | this.copy('client/views/loading/loading.html', 'client/views/loading/loading.html'); 63 | this.copy('client/views/loading/loading.styl', 'client/views/loading/loading.styl'); 64 | this.copy('client/views/home/home.coffee', 'client/views/home/home.coffee'); 65 | this.copy('client/views/home/home.html', 'client/views/home/home.html'); 66 | this.copy('client/views/home/home.styl', 'client/views/home/home.styl'); 67 | this.copy('client/views/about/about.coffee', 'client/views/about/about.coffee'); 68 | this.copy('client/views/about/about.html', 'client/views/about/about.html'); 69 | this.copy('client/views/about/about.styl', 'client/views/about/about.styl'); 70 | this.copy('collections/modals/navigation.js', 'collections/modals/navigation.js'); 71 | this.copy('server/lib/startup.coffee', 'server/lib/startup.coffee'); 72 | this.copy('server/publish/navigation.js', 'server/publish/navigation.js'); 73 | this.copy('components/topnav/topnav.jsx', 'components/topnav/topnav.jsx'); 74 | }, 75 | 76 | mainFile: function(){ 77 | var context = { 78 | title: "some test", 79 | id: this._.classify("Demo Section") 80 | } 81 | 82 | 83 | this.fs.copyTpl( 84 | this.templatePath('_main.html'), 85 | this.destinationPath('main.html'), 86 | {title: 'Some Great Application'} 87 | ); 88 | 89 | }, 90 | 91 | standardPackages: function(){ 92 | var packages = [ 93 | 'meteor-platform', 94 | 'coffeescript', 95 | 'underscore', 96 | 'iron:router', 97 | 'http', 98 | 'sacha:spin', 99 | 'stylus', 100 | 'raix:handlebar-helpers', 101 | 'alanning:roles', 102 | 'momentjs:moment', 103 | 'random', 104 | 'zimme:iron-router-active', 105 | 'aslagle:reactive-table', 106 | 'semantic:ui-css', 107 | 'reactjs:react' 108 | ]; 109 | 110 | this.write('.meteor/packages', packages.join('\n')); 111 | } 112 | }, 113 | 114 | end: function() { 115 | this.log('\nYeehaw! All went well!\n\nStart your Meteor app on the command line with:\n'+ 116 | chalk.green('meteor')+'\n\n'); 117 | } 118 | }); 119 | -------------------------------------------------------------------------------- /app/templates/_main.html: -------------------------------------------------------------------------------- 1 | 2 | <%= title %> 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/templates/client/lib/layout/layoutFull.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /app/templates/client/lib/meteor/router.coffee: -------------------------------------------------------------------------------- 1 | if Meteor.isClient 2 | 3 | Router.configure 4 | notFoundTemplate: '404' 5 | layoutTemplate: 'layoutFull' 6 | loadingTemplate: 'loading' 7 | 8 | 9 | Router.onBeforeAction -> 10 | $('body').scrollTop 0 #scroll to the top of the page on route 11 | this.next() 12 | return 13 | 14 | -------------------------------------------------------------------------------- /app/templates/client/lib/meteor/security.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | Filters are used in every controller to manage which roles have access to the route. 3 | Security can and should also be controlled in server publish 4 | ### 5 | 6 | filters = 7 | security_isUser: -> 8 | #check for a user 9 | if !Meteor.loggingIn() && !Meteor.user() #make sure we have a valid user 10 | Router.go('home') #go back home if we have a problem 11 | else 12 | this.next() 13 | return 14 | 15 | security_isAdmin: -> 16 | #check for an admin role 17 | if Meteor.user() 18 | if !Roles.userIsInRole Meteor.user()._id, ['admin'] 19 | console.log "Security: No access..." 20 | Router.go('home') #go back home if we have a problem 21 | else 22 | this.next() 23 | return 24 | 25 | 26 | security_isContent: -> 27 | #chck for a content role... maybe you require a content administrator? :) 28 | if Meteor.user() 29 | if !Roles.userIsInRole Meteor.user()._id, ['content'] 30 | console.log "Security: No access..." 31 | Router.go('home') #go back home if we have a problem 32 | else 33 | this.next() 34 | return 35 | 36 | @filters = filters #make global var 37 | -------------------------------------------------------------------------------- /app/templates/client/lib/meteor/startup.coffee: -------------------------------------------------------------------------------- 1 | if Meteor.isClient 2 | 3 | Meteor.startup -> 4 | console.log "Start up client.." 5 | 6 | return -------------------------------------------------------------------------------- /app/templates/client/views/404/404.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/templates/client/views/about/about.coffee: -------------------------------------------------------------------------------- 1 | #define routes... 2 | Router.map -> 3 | @route 'about', 4 | path:"/about" 5 | controller: "aboutController" 6 | 7 | #the route controller for this view 8 | class @aboutController extends RouteController 9 | 10 | #template.......................... 11 | _.extend Template.about, 12 | 13 | rendered: -> 14 | console.log "about is rendered" 15 | return 16 | -------------------------------------------------------------------------------- /app/templates/client/views/about/about.html: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /app/templates/client/views/about/about.styl: -------------------------------------------------------------------------------- 1 | @import nib 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/templates/client/views/home/home.coffee: -------------------------------------------------------------------------------- 1 | #define routes... 2 | Router.map -> 3 | @route 'home', 4 | path:"/" 5 | controller: "homeController" 6 | 7 | #the route controller for this view 8 | class @homeController extends RouteController 9 | 10 | #template.......................... 11 | _.extend Template.home, 12 | 13 | rendered: -> 14 | console.log "home is rendered" 15 | return 16 | -------------------------------------------------------------------------------- /app/templates/client/views/home/home.html: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /app/templates/client/views/home/home.styl: -------------------------------------------------------------------------------- 1 | @import nib 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/templates/client/views/loading/loading.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/templates/client/views/loading/loading.styl: -------------------------------------------------------------------------------- 1 | #spinner 2 | width 100% 3 | height 100% 4 | margin-top 60px 5 | color #ffffff -------------------------------------------------------------------------------- /app/templates/collections/modals/navigation.js: -------------------------------------------------------------------------------- 1 | Navigation = new Meteor.Collection('navigation'); 2 | -------------------------------------------------------------------------------- /app/templates/components/topnav/topnav.jsx: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This directive is necessary to enable prepossessing of JSX tags: 4 | * @jsx React.DOM 5 | */ 6 | 7 | 8 | var Topnav = ReactMeteor.createClass({ 9 | 10 | // Specifying a templateName property allows the component to be 11 | // interpolated into a Blaze template just like any other template: 12 | // {{> Widget x=1 y=2}}. This corresponds to the JSX expression 13 | // . 14 | templateName: "Topnav", 15 | 16 | getInitialState: function(){ 17 | //better to start with something... and wait for the subscription 18 | return { 19 | navItems: [] 20 | } 21 | }, 22 | 23 | startMeteorSubscriptions: function() { 24 | Meteor.subscribe("navigation"); 25 | }, 26 | 27 | getMeteorState: function() { 28 | //load up the new meteor state 29 | return { 30 | navItems: Navigation.find({}).fetch() 31 | }; 32 | }, 33 | 34 | render: function() { 35 | return ( 36 |
38 | 39 |
40 | ); 41 | } 42 | }); 43 | 44 | 45 | var Navitems = React.createClass({ 46 | 47 | goRoute: function(someRoute) { 48 | Router.go(someRoute) 49 | }, 50 | 51 | renderItem: function(someItem) { 52 | return( 53 | 57 | {someItem.title} 58 | 59 | ) 60 | }, 61 | 62 | render: function() { 63 | //load up the props for use 64 | var { items, ...rest } = this.props; 65 | return
{items.map(this.renderItem)}
; 66 | } 67 | }); 68 | -------------------------------------------------------------------------------- /app/templates/config/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "public":{ 3 | "someThing": "someID" 4 | } 5 | } 6 | 7 | -------------------------------------------------------------------------------- /app/templates/server/lib/startup.coffee: -------------------------------------------------------------------------------- 1 | if Meteor.isServer 2 | 3 | Meteor.startup -> 4 | console.log "Server Starting up..." 5 | #add your navigation items here... 6 | 7 | Navigation.remove({}) #reset the collection.. 8 | 9 | Navigation.insert 10 | title: "home" 11 | route: "home" 12 | 13 | Navigation.insert 14 | title: "about" 15 | route: "about" 16 | 17 | return 18 | -------------------------------------------------------------------------------- /app/templates/server/publish/navigation.js: -------------------------------------------------------------------------------- 1 | Meteor.publish('navigation', function() { 2 | return Navigation.find(); 3 | }) 4 | 5 | -------------------------------------------------------------------------------- /app/templates/startup.sh: -------------------------------------------------------------------------------- 1 | # export MONGO_URL='mongodb://' 2 | # export MAIL_URL='smtp://' 3 | 4 | meteor --settings "config/settings.json" 5 | 6 | #start your app with --> sh startup.sh -------------------------------------------------------------------------------- /collection/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | var path = require('path'); 3 | var util = require('util'); 4 | var yeoman = require('yeoman-generator'); 5 | 6 | var CollectionGenerator = module.exports = function CollectionGenerator(args, options, config) { 7 | // calling NamedBase allows us to retrieve the name argument associated with 8 | // yo meteor:component name, and set it to this.name *magic* 9 | yeoman.generators.NamedBase.apply(this, arguments); 10 | 11 | this.sourceRoot(path.join(__dirname, '../templates/collection')); 12 | //set the new src template path 13 | 14 | console.log('Creating a new Collection for ' + this.name + ''); 15 | }; 16 | 17 | util.inherits(CollectionGenerator, yeoman.generators.NamedBase); 18 | 19 | 20 | CollectionGenerator.prototype.createCollectionFiles = function createCollectionFiles() { 21 | 22 | var context = { 23 | fileName: this.name.toLowerCase(), 24 | classedName: this._.classify(this.name), 25 | cameledName: this._.camelize(this.name) 26 | } 27 | 28 | 29 | this.fs.copyTpl( 30 | this.templatePath('modal.txt'), 31 | this.destinationPath( 32 | path.join( 33 | 'collections/modals', 34 | this.name.toLowerCase() + '.js') 35 | ), 36 | context 37 | ); 38 | 39 | this.fs.copyTpl( 40 | this.templatePath('publish.txt'), 41 | this.destinationPath( 42 | path.join( 43 | 'server/publish', 44 | this.name.toLowerCase() + '.js') 45 | ), 46 | context 47 | ); 48 | 49 | 50 | }; 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /component/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | var path = require('path'); 3 | var util = require('util'); 4 | var yeoman = require('yeoman-generator'); 5 | 6 | var ComponentGenerator = module.exports = function ComponentGenerator(args, options, config) { 7 | // calling NamedBase allows us to retrieve the name argument associated with 8 | // yo meteor:component name, and set it to this.name *magic* 9 | yeoman.generators.NamedBase.apply(this, arguments); 10 | 11 | this.sourceRoot(path.join(__dirname, '../templates/component')); 12 | //set the new src template path 13 | 14 | 15 | this.cameledName = this._.camelize(this.name); 16 | 17 | 18 | console.log('Creating a new React Component for ' + this.name + ''); 19 | }; 20 | 21 | util.inherits(ComponentGenerator, yeoman.generators.NamedBase); 22 | 23 | 24 | ComponentGenerator.prototype.createComponentFiles = function createComponentFiles() { 25 | 26 | var context = { 27 | fileName: this.name.toLowerCase(), 28 | classedName: this._.classify(this.name) 29 | } 30 | 31 | 32 | 33 | this.fs.copyTpl( 34 | this.templatePath('test.txt'), 35 | this.destinationPath( 36 | path.join( 37 | 'client/components', 38 | this.name.toLowerCase(), 39 | this.name.toLowerCase() + '.jsx') 40 | ), 41 | context 42 | ); 43 | 44 | 45 | }; 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-meteor-react", 3 | "version": "0.1.05", 4 | "description": "Yeoman generator for Meteor with some Angular.js views", 5 | "keywords": [ 6 | "yeoman-generator" 7 | ], 8 | "license": "MIT", 9 | "main": "app/index.js", 10 | "repository": "payner35/generator-meteor-react", 11 | "author": { 12 | "name": "Gavin Payne", 13 | "email": "gavinapayne@gmail.com", 14 | "url": "https://github.com/payner35" 15 | }, 16 | "engines": { 17 | "node": ">=0.10.0" 18 | }, 19 | "scripts": { 20 | "test": "mocha" 21 | }, 22 | "files": [ 23 | "app", 24 | "view", 25 | "collection", 26 | "component", 27 | "templates" 28 | ], 29 | "dependencies": { 30 | "chalk": "^1.0.0", 31 | "yeoman-generator": "^0.18.10", 32 | "yosay": "^1.0.2" 33 | }, 34 | "devDependencies": { 35 | "mocha": "*" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /templates/collection/modal.txt: -------------------------------------------------------------------------------- 1 | <%= classedName %> = new Meteor.Collection('<%= fileName %>'); 2 | -------------------------------------------------------------------------------- /templates/collection/publish.txt: -------------------------------------------------------------------------------- 1 | Meteor.publish('<%= fileName %>', function() { 2 | return <%= classedName %>.find(); 3 | }) 4 | -------------------------------------------------------------------------------- /templates/component/test.html: -------------------------------------------------------------------------------- 1 |

<%= fileName %>

2 | 3 | -------------------------------------------------------------------------------- /templates/component/test.txt: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This directive is necessary to enable prepossessing of JSX tags: 4 | * @jsx React.DOM 5 | */ 6 | 7 | 8 | var <%= classedName %> = ReactMeteor.createClass({ 9 | 10 | // Specifying a templateName property allows the component to be 11 | // interpolated into a Blaze template just like any other template: 12 | // {{> <%= classedName %> x=1 y=2}}. This corresponds to the JSX expression 13 | // <<%= classedName %> x={1} y={2} />. 14 | templateName: "<%= classedName %>", 15 | 16 | getInitialState: function() { 17 | return {}; 18 | }, 19 | 20 | startMeteorSubscriptions: function() { 21 | //Meteor.subscribe("someCollection"); 22 | }, 23 | 24 | getMeteorState: function() { 25 | return {}; 26 | }, 27 | 28 | render: function() { 29 | return ( 30 |
31 | Hello, world! I am a "<%= classedName %>" component. 32 |
33 | ); 34 | } 35 | 36 | 37 | }); 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /templates/view/view.coffee: -------------------------------------------------------------------------------- 1 | #define route... 2 | Router.map -> 3 | @route '<%= fileName %>', 4 | path:"/<%= fileName %>" 5 | controller: "<%= fileName %>Controller" 6 | 7 | 8 | #the route controller for this view 9 | class @<%= fileName %>Controller extends RouteController 10 | #onBeforeAction: [filters.security_isUser] decide on your access level here. 11 | onRun: -> 12 | #a nice place for any page level analytics 13 | this.next() 14 | return 15 | 16 | 17 | #template.......................... 18 | _.extend Template.<%= fileName %>, 19 | 20 | rendered: -> 21 | #called when the dom is rendered.. 22 | console.log "<%= fileName %> is rendered" 23 | return 24 | -------------------------------------------------------------------------------- /templates/view/view.html: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /templates/view/view.styl: -------------------------------------------------------------------------------- 1 | @import nib 2 | 3 | -------------------------------------------------------------------------------- /test/test-app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var assert = require('yeoman-generator').assert; 5 | var helpers = require('yeoman-generator').test; 6 | var os = require('os'); 7 | 8 | describe('meteor-react:app', function () { 9 | before(function (done) { 10 | helpers.run(path.join(__dirname, '../app')) 11 | .inDir(path.join(os.tmpdir(), './temp-test')) 12 | .withOptions({ 'skip-install': true }) 13 | .withPrompt({ 14 | someOption: true 15 | }) 16 | .on('end', done); 17 | }); 18 | 19 | it('creates files', function () { 20 | assert.file([ 21 | 'bower.json', 22 | 'package.json', 23 | '.editorconfig', 24 | '.jshintrc' 25 | ]); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /view/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | var path = require('path'); 3 | var util = require('util'); 4 | var yeoman = require('yeoman-generator'); 5 | 6 | var ViewGenerator = module.exports = function ViewGenerator(args, options, config) { 7 | // calling NamedBase allows us to retrieve the name argument associated with 8 | // yo meteor:component name, and set it to this.name *magic* 9 | yeoman.generators.NamedBase.apply(this, arguments); 10 | 11 | this.sourceRoot(path.join(__dirname, '../templates/view')); 12 | //set the new src template path 13 | 14 | console.log('Creating a new View for ' + this.name + ''); 15 | }; 16 | 17 | util.inherits(ViewGenerator, yeoman.generators.NamedBase); 18 | 19 | 20 | ViewGenerator.prototype.createViewFiles = function createViewFiles() { 21 | 22 | var context = { 23 | fileName: this.name.toLowerCase(), 24 | classedName: this._.classify(this.name), 25 | cameledName: this._.camelize(this.name) 26 | } 27 | 28 | 29 | this.fs.copyTpl( 30 | this.templatePath('view.coffee'), 31 | this.destinationPath( 32 | path.join( 33 | 'client/views', 34 | this.name.toLowerCase(), 35 | this.name.toLowerCase() + '.coffee') 36 | ), 37 | context 38 | ); 39 | 40 | this.fs.copyTpl( 41 | this.templatePath('view.html'), 42 | this.destinationPath( 43 | path.join( 44 | 'client/views', 45 | this.name.toLowerCase(), 46 | this.name.toLowerCase() + '.html') 47 | ), 48 | context 49 | ); 50 | 51 | this.fs.copyTpl( 52 | this.templatePath('view.styl'), 53 | this.destinationPath( 54 | path.join( 55 | 'client/views', 56 | this.name.toLowerCase(), 57 | this.name.toLowerCase() + '.styl') 58 | ), 59 | context 60 | ); 61 | 62 | 63 | }; 64 | 65 | 66 | 67 | 68 | --------------------------------------------------------------------------------