├── .gitignore ├── README.ja.md ├── README.md ├── app ├── assets │ └── index.html ├── scripts │ ├── components │ │ ├── .gitignore │ │ ├── bar.jsx │ │ ├── baz.cjsx │ │ ├── foo.coffee │ │ └── root.js │ ├── main.js │ └── util │ │ └── say-hello.js ├── styles │ ├── components │ │ ├── bar.less │ │ ├── baz.less │ │ ├── foo.less │ │ └── root.less │ ├── main.less │ └── variables.less └── templates │ └── components │ ├── foo.rt │ └── root.rt ├── bower.json ├── gulpfile.coffee ├── package.json ├── src ├── package.xml ├── pages │ ├── MyAppPage.page │ └── MyAppPage.page-meta.xml └── staticresources │ ├── .gitignore │ ├── MyApp.resource-meta.xml │ └── MyAppLib.resource-meta.xml └── test ├── e2e └── app001.test.js └── unit ├── components └── foo.test.coffee └── util └── say-hello.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.dat 5 | *.out 6 | *.pid 7 | *.gz 8 | 9 | pids 10 | logs 11 | results 12 | 13 | build 14 | 15 | node_modules 16 | npm-debug.log 17 | 18 | bower_components 19 | 20 | .npmignore 21 | *.sublime-* 22 | -------------------------------------------------------------------------------- /README.ja.md: -------------------------------------------------------------------------------- 1 | # Salesforce ReactJS SPA Starter 2 | 3 | Salesforce上でReactJSベースのSPA(Single Page Application)を作成するためのテンプレートです。 4 | Gulp.jsを利用して自動ビルド・デプロイが可能です。 5 | 6 | ビルドシステム内にソースコードを変換する仕組みが組み入れられているため、コードをCoffeeScriptで書くことも、ES6 JavaScriptの文法を用いることも、もちろん通常のJavaScript(ES5)そのままでの利用もできます。 7 | 8 | ReactJSのコンポーネントのマークアップを記述する方式として、コード内にJSXを用いて開発することもできますが、[react-templates](http://wix.github.io/react-templates/) 形式のテンプレートファイルに分離することもできるようになっています。 9 | 10 | ## セットアップ 11 | 12 | Node.jsの0.10かそれ以上がインストールされていることを確認して下さい。 13 | 14 | ``` 15 | $ node --version 16 | ``` 17 | 18 | 確認したら、以下のコマンドをプロジェクトディレクトリのルートで実行します。 19 | 20 | ``` 21 | $ npm install 22 | $ bower install 23 | ``` 24 | 25 | `gulp` コマンドがインストールされていない場合は、以下のコマンドを実行します 26 | 27 | ``` 28 | $ npm install -g gulp 29 | ``` 30 | 31 | ## ファイルのビルド 32 | 33 | `gulp` コマンドを実行してソースコードから実行用のコードを生成します。 34 | 35 | ``` 36 | $ gulp 37 | ``` 38 | 39 | `watch` タスクオプションを指定してgulpを実行すると、ソースコードファイルの変更を監視して自動的にビルドを実行します。 40 | 41 | ``` 42 | $ gulp watch 43 | ``` 44 | 45 | ## プレビュー 46 | 47 | 以下のコマンドでWebアプリケーションサーバを起動できます。 48 | 49 | ``` 50 | $ gulp dev 51 | ``` 52 | 53 | `http://localhost:8000` にアクセスしてアプリのプレビューを表示します。 54 | このコマンドでソースコードの変更の監視も同時におこないます。 55 | 56 | 57 | ## テスト 58 | 59 | 単体テストを実行するには以下のコマンドを実行します。 60 | 61 | ``` 62 | $ gulp test 63 | ``` 64 | 65 | ソースコードファイル変更時に自動的にテストを実行するには以下のコマンドを実行します。 66 | 67 | ``` 68 | $ gulp test:watch 69 | ``` 70 | 71 | 72 | ## デプロイ (Salesforce) 73 | 74 | Salesforceに接続するユーザ名・パスワード(セキュリティトークンを含む)を環境変数に指定して、`gulp deploy` コマンドを実行します。 75 | 76 | ``` 77 | $ SF_USERNAME=yourname@example.org SF_PASSWORD=password gulp deploy 78 | ``` 79 | 80 | あるいは、プロジェクト内に `.env` というファイルを用意し、中身に環境変数を `KEY=VALUE` の形式で記述しておき、 81 | 82 | ``` 83 | SF_USERNAME=yourname@example.org 84 | SF_PASSWORD=password 85 | ``` 86 | 87 | その後 `foreman` を経由して `gulp deploy` を実行します。 88 | 89 | ``` 90 | $ nf run gulp deploy 91 | ``` 92 | 93 | この`nf`コマンドは `npm install -g foreman` でインストール可能です。 94 | 95 | 96 | ## プロジェクト・ディレクトリ構成 97 | 98 | ``` 99 | ├── app # ソースコードディレクトリ 100 | │   ├── assets # HTML、画像、フォントなどの静的ファイル 101 | │   │   ├── index.html # エントリポイントとなるHTMLファイル 102 | │   │   ├── ... 103 | │   │ 104 | │   ├── scripts # スクリプトファイル(JSへコンパイル) 105 | │   │   ├── components # ReactJS コンポーネントのスクリプトコード 106 | │   │   │   ├── bar.jsx # ES6 JS : .js および .jsx ファイル(JSX記述可能) 107 | │   │   │   ├── baz.cjsx # CoffeeScript : .coffee および .cjsx ファイル(JSX記述可能) 108 | │   │   │   ├── foo.coffee 109 | │   │   │   ├── root.js 110 | │   │   │   ├── ... 111 | │   │   │  112 | │   │   ├── main.js # エントリポイントとなるスクリプト 113 | │   │   ├── ... 114 | │   │ 115 | │   ├── styles # LESSスタイルシートファイル(CSSへコンパイル) 116 | │   │   ├── components # ReactJS コンポーネントのスタイルシート 117 | │   │   │   ├── bar.less 118 | │   │   │   ├── baz.less 119 | │   │   │   ├── foo.less 120 | │   │   │   ├── ... 121 | │   │   │  122 | │   │   ├── main.less # エントリポイントとなるスタイルシート 123 | │   │   ├── ... 124 | │   │ 125 | │   └── templates # React-templates 形式のファイルを格納 126 | │   └── components 127 | │   ├── foo.rt # app/scripts/components/foo.coffee に対応 128 | │   ├── root.rt # app/scripts/components/root.js に対応 129 | │   ├── ... 130 | │ 131 | ├── build # Gulpによって生成されるビルドファイルを格納するディレクトリ 132 | │   ├── app 133 | │   │   ├── index.html 134 | │   │   ├── scripts 135 | │   │   ├── styles 136 | │   │   ├── ... 137 | │   │ 138 | │   └── test 139 | │      ├── ... 140 | │ 141 | ├── bower.json # 依存ライブラリの設定 142 | ├── gulpfile.coffee # Gulp ビルドスクリプト 143 | ├── package.json # プロジェクトの各種設定 144 | │ 145 | ├── src # Force.com プロジェクトのソースコード 146 | │   ├── package.xml 147 | │   ├── pages 148 | │   │   ├── MyAppPage.page 149 | │   │   └── MyAppPage.page-meta.xml 150 | │   └── staticresources 151 | │   ├── MyApp.resource # ビルドされたファイルを含むZIPファイル (gulpによって生成) 152 | │   ├── MyApp.resource-meta.xml 153 | │   ├── MyAppLib.resource # Bowerのライブラリを含むZIPファイル (gulpによって生成) 154 | │   └── MyAppLib.resource-meta.xml 155 | │ 156 | └── test # テストコードディレクトリ 157 | ├── e2e # End-to-End テストのためのコード(protractorを想定) 158 |    │   ├── app001.test.js 159 |    │   ├── ... 160 |    │ 161 | └── unit # 単体テスト 162 | ├── components # ReactJS コンポーネントの単体テスト 163 |     │   ├── bar.test.js 164 |     │   ├── baz.test.js 165 |     │   ├── foo.test.js 166 |       │   ├── ... 167 |       ├── ... 168 | ``` 169 | 170 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Salesforce ReactJS SPA Starter 2 | 3 | A template project to create ReactJS-based single page application on Salesforce, with automatic build script (Gulp.js) 4 | 5 | Because it includes source code transpilation in the build system, you can write the code in CoffeeScript, use some of ES6 JavaScript grammer, or of course vanilla JavaScript (ES5). 6 | 7 | As we support [react-templates](http://wix.github.io/react-templates/), you don't have to write ReactJS component markup in JSX within your source code. It can be separated to solo template file. 8 | 9 | ## Setup 10 | 11 | Make sure you are installing Node.js 0.10.x or later. 12 | 13 | ``` 14 | $ node --version 15 | ``` 16 | 17 | Then execute following commands in project directory root: 18 | 19 | ``` 20 | $ npm install 21 | $ bower install 22 | ``` 23 | 24 | If you have not installed the `gulp` command yet, install it by following command : 25 | 26 | ``` 27 | $ npm install -g gulp 28 | ``` 29 | 30 | ## Build Files 31 | 32 | Run the `gulp` command to build runnable codes from source code : 33 | 34 | ``` 35 | $ gulp 36 | ``` 37 | 38 | for automatic building you can specify `watch` task option for gulp 39 | 40 | ``` 41 | $ gulp watch 42 | ``` 43 | 44 | ## Preview 45 | 46 | Start web server by following command : 47 | 48 | ``` 49 | $ gulp dev 50 | ``` 51 | 52 | Then access to `http://localhost:8000`. 53 | 54 | The command watches source code update and builds automatically. 55 | 56 | 57 | ## Test 58 | 59 | Run unit tests 60 | 61 | ``` 62 | $ gulp test 63 | ``` 64 | 65 | For automatic test execution: (update watch) 66 | 67 | ``` 68 | $ gulp test:watch 69 | ``` 70 | 71 | 72 | ## Deploy (Salesforce) 73 | 74 | With username and password (may include security token) as environment variables to connect to Salesforce, 75 | execute `gulp deploy` command : 76 | 77 | ``` 78 | $ SF_USERNAME=yourname@example.org SF_PASSWORD=password gulp deploy 79 | ``` 80 | or prepare `.env` file in `KEY=VALUE` format: 81 | 82 | ``` 83 | SF_USERNAME=yourname@example.org 84 | SF_PASSWORD=password 85 | ``` 86 | 87 | Then execute `gulp deploy` using `foreman` : 88 | 89 | ``` 90 | $ nf run gulp deploy 91 | ``` 92 | 93 | The `nf` command can be installed by `npm install -g foreman`. 94 | 95 | 96 | ## Project Directory Structure 97 | 98 | ``` 99 | ├── app # Source code directory 100 | │   ├── assets # HTML, images, fonts, or other static files 101 | │   │   ├── index.html # Entry point HTML 102 | │   │   ├── ... 103 | │   │ 104 | │   ├── scripts # Script files that can be compiled to JS 105 | │   │   ├── components # Scripts for ReactJS components 106 | │   │   │   ├── bar.jsx # ES6 JS (.js or .jsx), which can include JSX 107 | │   │   │   ├── baz.cjsx # CoffeeScript (.cjsx or .coffee), which can include JSX 108 | │   │   │   ├── foo.coffee 109 | │   │   │   ├── foo.coffee 110 | │   │   │   ├── ... 111 | │   │   │  112 | │   │   ├── main.js # Entry point of app script 113 | │   │   ├── ... 114 | │   │ 115 | │   ├── styles # Stylesheet files to be compiled to CSS 116 | │   │   ├── components # Stylesheets for ReactJS components 117 | │   │   │   ├── bar.less 118 | │   │   │   ├── baz.less 119 | │   │   │   ├── foo.less 120 | │   │   │   ├── ... 121 | │   │   │  122 | │   │   ├── main.less # Entry point of CSS 123 | │   │   ├── ... 124 | │   │ 125 | │   └── templates # ReactJS Templates 126 | │   └── components 127 | │   ├── foo.rt # matching to app/scripts/components/foo.coffee 128 | │   ├── root.rt # matching to app/scripts/components/root.js 129 | │   ├── ... 130 | │ 131 | ├── build # Directory which includes all built files generated by gulp script 132 | │   ├── app 133 | │   │   ├── index.html 134 | │   │   ├── scripts 135 | │   │   ├── styles 136 | │   │   ├── ... 137 | │   │ 138 | │   └── test 139 | │      ├── ... 140 | │ 141 | ├── bower.json # Depending library setting 142 | ├── gulpfile.coffee # Gulp build script 143 | ├── package.json # Project settting 144 | │ 145 | ├── src # Force.com project 146 | │   ├── package.xml 147 | │   ├── pages 148 | │   │   ├── MyAppPage.page 149 | │   │   └── MyAppPage.page-meta.xml 150 | │   └── staticresources 151 | │   ├── MyApp.resource # Zip file containing built static files (generated by gulp) 152 | │   ├── MyApp.resource-meta.xml 153 | │   ├── MyAppLib.resource # Zip file containing bower libs (generated by gulp) 154 | │   └── MyAppLib.resource-meta.xml 155 | │ 156 | └── test # Test code directory 157 | ├── e2e # End-to-End test 158 |    │   ├── app001.test.js 159 |    │   ├── ... 160 |    │ 161 | └── unit # Unit test 162 | ├── components # ReactJS component unit test 163 |     │   ├── bar.test.js 164 |     │   ├── baz.test.js 165 |     │   ├── foo.test.js 166 |       │   ├── ... 167 |       ├── ... 168 | ``` 169 | -------------------------------------------------------------------------------- /app/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/scripts/components/.gitignore: -------------------------------------------------------------------------------- 1 | *.rt.js 2 | -------------------------------------------------------------------------------- /app/scripts/components/bar.jsx: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var React = require("react"); 4 | 5 | module.exports = React.createClass({ 6 | render: function() { 7 | return
Bar: {this.props.children}
8 | } 9 | }) 10 | -------------------------------------------------------------------------------- /app/scripts/components/baz.cjsx: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | React = require "react" 4 | 5 | module.exports = React.createClass 6 | render: -> 7 |
Baz: {this.props.children}
8 | -------------------------------------------------------------------------------- /app/scripts/components/foo.coffee: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | React = require "react" 4 | FooTmpl = require "./foo.rt" 5 | 6 | sayHello = require "../util/say-hello" 7 | 8 | module.exports = React.createClass 9 | onClick: -> 10 | window.alert sayHello(@props.children) 11 | 12 | render: -> 13 | FooTmpl.apply(@) 14 | -------------------------------------------------------------------------------- /app/scripts/components/root.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var React = require("react"); 4 | var RootTmpl = require("./root.rt"); 5 | 6 | module.exports = React.createClass({ 7 | render: function() { 8 | return RootTmpl.apply(this); 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /app/scripts/main.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var React = require("react"); 4 | var Root = require("./components/root"); 5 | 6 | document.addEventListener("DOMContentLoaded", function() { 7 | React.render(React.createElement(Root, {}), document.body); 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/util/say-hello.js: -------------------------------------------------------------------------------- 1 | module.exports = function sayHello(name) { 2 | return "Hello, " + name; 3 | }; -------------------------------------------------------------------------------- /app/styles/components/bar.less: -------------------------------------------------------------------------------- 1 | .bar { 2 | background-color: @appcolor2; 3 | } -------------------------------------------------------------------------------- /app/styles/components/baz.less: -------------------------------------------------------------------------------- 1 | .baz { 2 | background-color: @appcolor3; 3 | } -------------------------------------------------------------------------------- /app/styles/components/foo.less: -------------------------------------------------------------------------------- 1 | .foo { 2 | background-color: @appcolor1; 3 | a { 4 | color: #ff0000; 5 | text-decoration: underline; 6 | } 7 | } -------------------------------------------------------------------------------- /app/styles/components/root.less: -------------------------------------------------------------------------------- 1 | .root { 2 | .elem { 3 | border-radius: 4px; 4 | padding: 15px; 5 | color: #ffffff; 6 | text-shadow: 2px 2px 2px #000000; 7 | } 8 | } -------------------------------------------------------------------------------- /app/styles/main.less: -------------------------------------------------------------------------------- 1 | @import "./variables.less"; 2 | @import "./components/root.less"; 3 | @import "./components/foo.less"; 4 | @import "./components/bar.less"; 5 | @import "./components/baz.less"; 6 | -------------------------------------------------------------------------------- /app/styles/variables.less: -------------------------------------------------------------------------------- 1 | @appcolor1: #3261AB; 2 | @appcolor2: #99CFE5; 3 | @appcolor3: #D1F1CC; -------------------------------------------------------------------------------- /app/templates/components/foo.rt: -------------------------------------------------------------------------------- 1 |
2 | {this.props.children} 3 |
-------------------------------------------------------------------------------- /app/templates/components/root.rt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | John 11 | 12 | 13 | Jane 14 | 15 | 16 | Jack 17 | 18 | 19 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "salesforce-reactjs-spa-starter", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "bootstrap": "~3.3.2", 6 | "jquery": "~2.1.3" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /gulpfile.coffee: -------------------------------------------------------------------------------- 1 | gulp = require "gulp" 2 | gutil = require "gulp-util" 3 | gulpif = require "gulp-if" 4 | gulpignore = require "gulp-ignore" 5 | plumber = require "gulp-plumber" 6 | changed = require "gulp-changed" 7 | through = require "through2" 8 | glob = require "glob" 9 | 10 | rt = require "gulp-react-templates" 11 | rename = require "gulp-rename" 12 | 13 | coffee = require "gulp-coffee" 14 | cjsx = require "gulp-cjsx" 15 | babel = require "gulp-babel" 16 | 17 | browserify = require "browserify" 18 | espowerify = require "espowerify" 19 | uglify = require "gulp-uglify" 20 | source = require "vinyl-source-stream" 21 | watchify = require "watchify" 22 | notify = require "gulp-notify" 23 | transform = require "vinyl-transform" 24 | 25 | less = require "gulp-less" 26 | streamify = require "gulp-streamify" 27 | minify = require "gulp-minify-css" 28 | 29 | del = require "del" 30 | 31 | zip = require "gulp-zip" 32 | forceDeploy = require "gulp-jsforce-deploy" 33 | 34 | mocha = require "gulp-mocha" 35 | karma = require("karma").server; 36 | require "intelli-espower-loader" 37 | 38 | express = require "express" 39 | port = 8000 40 | 41 | # 42 | paths = 43 | src: 44 | app: "./app" 45 | test: "./test" 46 | assets: "./app/assets" 47 | scripts: "./app/scripts" 48 | styles: "./app/styles" 49 | templates: "./app/templates" 50 | build: 51 | app: "./build/app" 52 | test: "./build/test" 53 | assets: "./build/app" 54 | scripts: "./build/app/scripts" 55 | styles: "./build/app/styles" 56 | lib: "./bower_components" 57 | force: "./src" 58 | 59 | # 60 | appName = "MyApp" 61 | 62 | # 63 | debug = (gutil.env.type != 'production') 64 | 65 | 66 | ### 67 | # Build Tasks 68 | ### 69 | 70 | # Copy all static files in src directory to temporary build directory 71 | gulp.task "build:assets", -> 72 | gulp.src "#{paths.src.assets}/**", base: "#{paths.src.assets}" 73 | .pipe plumber() 74 | .pipe changed(paths.build.assets) 75 | .pipe gulp.dest "#{paths.build.assets}" 76 | .pipe notify("Assets copied") 77 | 78 | # Building CSS files from LESS source 79 | gulp.task "build:styles", -> 80 | gulp.src "#{paths.src.styles}/main.less" 81 | .pipe plumber() 82 | .pipe less() 83 | .pipe gulp.dest paths.build.styles 84 | .pipe notify("Style compiled : <%= file.relative %>") 85 | .pipe gulpignore.exclude(debug) # do not minify if in debug 86 | .pipe minify() 87 | .pipe rename(extname: ".min.css") 88 | .pipe gulp.dest paths.build.styles 89 | .pipe notify("Minified style created : <%= file.relative %>") 90 | 91 | # 92 | compileScript = -> 93 | gulpif(/\.coffee$/, coffee(bare: true), # Compile CoffeeScript 94 | gulpif(/\.cjsx$/, cjsx(bare: true), # Compile CoffeeScript JSX 95 | gulpif(/\.jsx?$/, babel() ))) # Compile ES6 JavaScript (optionally with JSX) 96 | 97 | # Compile script files 98 | gulp.task "build:scripts", -> 99 | gulp.src "#{paths.src.scripts}/**/*.{js,jsx,coffee,cjsx}" 100 | .pipe plumber() 101 | .pipe changed(paths.build.scripts, extension: ".js") 102 | .pipe notify("Compiling : <%= file.relative %>") 103 | .pipe compileScript() 104 | .pipe rename(extname: ".js") 105 | .pipe gulp.dest paths.build.scripts 106 | .pipe notify("Script compiled : <%= file.relative %>") 107 | 108 | # Compile React Template file 109 | gulp.task "build:templates", -> 110 | gulp.src "#{paths.src.templates}/**/*.rt", base: "#{paths.src.templates}" 111 | .pipe plumber() 112 | .pipe changed(paths.build.scripts, extension: ".rt.js") 113 | .pipe notify("Compiling : <%= file.relative %>") 114 | .pipe rt(modules: "commonjs") 115 | .pipe gulp.dest paths.build.scripts 116 | .pipe notify("Template compiled : <%= file.relative %>") 117 | 118 | # build src files 119 | gulp.task "build:src", [ "build:assets", "build:styles", "build:scripts", "build:templates" ] 120 | 121 | # Compile test script files 122 | gulp.task "build:test:scripts", -> 123 | gulp.src "#{paths.src.test}/**/*.{js,jsx,coffee,cjsx}" 124 | .pipe plumber() 125 | .pipe changed(paths.build.test, extension: ".js") 126 | .pipe notify("Compiling : <%= file.relative %>") 127 | .pipe compileScript() 128 | .pipe rename(extname: ".js") 129 | .pipe gulp.dest paths.build.test 130 | .pipe notify("Script compiled : <%= file.relative %>") 131 | 132 | # Build bundle file using browserify 133 | buildBundle = (opts) -> 134 | { src, dest, watch } = opts 135 | path = require "path" 136 | dest = path.dirname(src) unless dest 137 | bundleName = null 138 | if path.extname(dest) == ".js" 139 | bundleName = path.basename(dest) 140 | dest = path.dirname(dest) 141 | else 142 | bundleName = path.basename(src, path.extname(src)) + "-bundle.js" 143 | bundle = -> 144 | b = bundler.bundle() 145 | .on "error", -> 146 | args = Array.prototype.slice.call(arguments) 147 | notify.onError(title: "Compile Error", message: "<%= error.message %>").apply(@, args); 148 | @emit "end" 149 | .pipe source(bundleName) 150 | .pipe gulp.dest dest 151 | .pipe notify("Bundle created : <%= file.relative %>") 152 | .pipe gulpignore.exclude(debug) # do not minify if in debug 153 | .pipe streamify uglify() 154 | .pipe rename(extname: ".min.js") 155 | .pipe gulp.dest dest 156 | through.obj (file, enc, callback) -> 157 | b.on "end", -> callback() 158 | bundler = 159 | browserify src, 160 | transform: [ espowerify ] 161 | cache: {} 162 | packageCache: {} 163 | fullPaths: true 164 | debug: true 165 | bundler = watchify(bundler).on "update", bundle if watch 166 | bundle() 167 | 168 | # Bundle files into one runnable script file using browserify 169 | gulp.task "build:bundle", [ "build:src" ], -> 170 | buildBundle 171 | src: "#{paths.build.scripts}/main.js" 172 | 173 | # Build bundle with watch option 174 | gulp.task "build:bundle:watch", [ "build:src" ], -> 175 | buildBundle 176 | src: "#{paths.build.scripts}/main.js" 177 | watch: true 178 | 179 | # All build tasks 180 | gulp.task "build", [ "build:bundle" ] 181 | 182 | # Build test scripts 183 | gulp.task "build:test", [ "build:src", "build:test:scripts" ] 184 | 185 | # Bundle test scripts for component testing 186 | gulp.task "build:test:component", [ "build:test" ], (done) -> 187 | gulp.src "#{paths.build.test}/unit/components/**/*.test.js" 188 | .pipe transform (filename) -> 189 | buildBundle 190 | src: filename 191 | 192 | # Build test scripts for component testing, with watch option 193 | gulp.task "build:test:component:watch", [ "build:test" ], -> 194 | gulp.src "#{paths.build.test}/unit/components/**/*.test.js" 195 | .pipe transform (filename) -> 196 | buildBundle 197 | src: filename 198 | watch: true 199 | 200 | # All build tasks 201 | gulp.task "build:all", [ "build", "build:test", "build:test:component" ] 202 | 203 | 204 | # Cleanup built files 205 | gulp.task "clean", -> 206 | del [ paths.build.app, paths.build.test ] 207 | 208 | 209 | ### 210 | # Deploy Tasks 211 | ### 212 | 213 | # Zip all built files as a static resource file 214 | gulp.task "archive:app", -> 215 | gulp.src "#{paths.build.app}/**" 216 | .pipe plumber() 217 | .pipe gulpignore (f) -> f.path.match /\.js$/ && !!f.path.match /\-bundle\.js$/ 218 | .pipe zip("#{appName}.resource") 219 | .pipe gulp.dest "#{paths.force}/staticresources" 220 | .pipe notify("Zip file created : <%= file.relative %>") 221 | 222 | # Zip bower installed libralies 223 | gulp.task "archive:lib", -> 224 | gulp.src "#{paths.lib}/**" 225 | .pipe plumber() 226 | .pipe zip("#{appName}Lib.resource") 227 | .pipe gulp.dest "#{paths.force}/staticresources" 228 | .pipe notify("Zip file created : <%= file.relative %>") 229 | 230 | # Zip all static resources 231 | gulp.task "archive", [ "archive:lib", "archive:app" ] 232 | 233 | # 234 | gulp.task "deploy:execute", -> 235 | gulp.src "#{paths.force}/**/*", base: "." 236 | .pipe plumber() 237 | .pipe zip("pkg.zip") 238 | .pipe forceDeploy 239 | username: process.env.SF_USERNAME 240 | password: process.env.SF_PASSWORD 241 | # loginUrl: "https://test.salesforce.com" 242 | # pollTimeout: 120*1000 243 | # pollInterval: 10*1000 244 | # version: '33.0' 245 | 246 | # Deploying package to Salesforce 247 | gulp.task "deploy", [ "archive" ], -> gulp.start "deploy:execute" 248 | 249 | 250 | ### 251 | # Watch Tasks 252 | ### 253 | 254 | # 255 | createOnChangeHandler = (label) -> 256 | (e) -> 257 | gutil.log("File #{e.path} was #{e.type}, running '#{label}' task...") 258 | 259 | # 260 | gulp.task "watch:common", [ "build:templates", "build:scripts", "build:styles", "build:assets" ], -> 261 | gulp.watch "#{paths.src.templates}/**", [ "build:templates" ] 262 | .on "change", createOnChangeHandler("build:templates") 263 | gulp.watch "#{paths.src.scripts}/**", [ "build:scripts" ] 264 | .on "change", createOnChangeHandler("build:scripts") 265 | gulp.watch "#{paths.src.styles}/**", [ "build:styles" ] 266 | .on "change", createOnChangeHandler("build:styles") 267 | gulp.watch "#{paths.src.assets}/**", [ "build:assets" ] 268 | .on "change", createOnChangeHandler("build:assets") 269 | 270 | # 271 | gulp.task "watch:test:scripts", [ "build:test:scripts" ], -> 272 | gulp.watch "#{paths.src.test}/**", [ "build:test:scripts" ] 273 | .on "change", createOnChangeHandler("test:scripts") 274 | 275 | # 276 | gulp.task "watch:build", [ "watch:common", "build:bundle:watch" ] 277 | 278 | # 279 | gulp.task "watch:test", [ "watch:common", "watch:test:scripts", "build:test:component:watch" ] 280 | 281 | # 282 | gulp.task "watch", [ "watch:build", "watch:test" ] 283 | 284 | # 285 | gulp.task "watch:deploy", -> 286 | gulp.watch "#{paths.build.app}/**", [ "archive:app" ] 287 | gulp.watch "#{paths.lib}/**", [ "archive:lib" ] 288 | gulp.watch "#{paths.force}/**", [ "deploy:execute" ] 289 | 290 | # 291 | gulp.task "watch:all", [ "watch", "watch:deploy" ] 292 | 293 | 294 | ### 295 | # Test Tasks 296 | ### 297 | 298 | # Unit test 299 | gulp.task "test:unit", [ "build:test" ], -> gulp.start "test:unit:run" 300 | 301 | # Run unit test using mocha (excluding DOM related test) 302 | gulp.task "test:unit:run", -> 303 | gulp.src [ 304 | "#{paths.build.test}/unit/**/*.test.js" 305 | "!#{paths.build.test}/unit/components/*.test.js" 306 | ] 307 | .pipe plumber() 308 | .pipe notify("Start Test : <%= file.relative %>") 309 | .pipe mocha reporter: "spec" 310 | .pipe notify("Unit Test Completed") 311 | 312 | # Unit test with watching update 313 | gulp.task "test:unit:watch", [ "watch:test" ], -> 314 | gulp.watch [ 315 | "#{paths.build.test}/unit/**/*.test.js" 316 | "!#{paths.build.test}/unit/components/*.test.js" 317 | ], [ "test:unit:run" ] 318 | .on "change", createOnChangeHandler("test:unit:run") 319 | gulp.start "test:unit:run" 320 | 321 | 322 | # Component (i.e. DOM related) unit test 323 | gulp.task "test:component", [ "build:test:component" ], -> gulp.start "test:component:run" 324 | 325 | # Run component unit test using Karma 326 | gulp.task "test:component:run", (done) -> 327 | files = glob.sync "#{paths.build.test}/unit/components/**/*.test-bundle.js" 328 | if files.length > 0 329 | karma.start 330 | files: [ 331 | "#{paths.build.test}/unit/components/**/*.test-bundle.js" 332 | ] 333 | frameworks: [ "mocha" ] 334 | reporters: [ "mocha" ] 335 | browsers: [ "Chrome" ] 336 | singleRun: true 337 | , done 338 | else 339 | done() 340 | 341 | # Component unit test with watching update 342 | gulp.task "test:component:watch", [ "watch:test" ], (done) -> 343 | karma.start 344 | files: [ 345 | "#{paths.build.test}/unit/components/**/*.test-bundle.js" 346 | ] 347 | frameworks: [ "mocha" ] 348 | reporters: [ "mocha" ] 349 | browsers: [ "Chrome" ] 350 | autoWatch: true 351 | singleRun: false 352 | , done 353 | 354 | # Start Test 355 | gulp.task "test", [ "test:unit", "test:component" ] 356 | 357 | # Test with watch option 358 | gulp.task "test:watch", [ "test:unit:watch", "test:component:watch" ] 359 | 360 | 361 | ### 362 | # Others 363 | ### 364 | 365 | # Start HTTP server 366 | gulp.task "serve", (done) -> 367 | server = express() 368 | server.use express.static(paths.build.app) 369 | server.use express.static(paths.lib) 370 | server.listen port, -> 371 | gutil.log "App server started : http://localhost:#{port}" 372 | done() 373 | 374 | # for development 375 | gulp.task "dev", [ "serve", "watch:build" ] 376 | 377 | # 378 | gulp.task "default", [ "build" ] 379 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "salesforce-reactjs-spa-starter", 3 | "description": "", 4 | "version": "1.0.0", 5 | "author": "", 6 | "private": true, 7 | "scripts": { 8 | "postinstall": "webdriver-manager update", 9 | "test": "gulp test", 10 | "test:watch": "gulp test:watch" 11 | }, 12 | "directories": { 13 | "test": "build/test" 14 | }, 15 | "devDependencies": { 16 | "browserify": "^9.0.3", 17 | "coffee-script": "^1.9.1", 18 | "del": "^1.1.1", 19 | "espower-loader": "^0.10.0", 20 | "espowerify": "^0.10.0", 21 | "express": "^4.11.2", 22 | "glob": "^4.4.0", 23 | "gulp": "^3.8.11", 24 | "gulp-babel": "^4.0.0", 25 | "gulp-changed": "^1.1.1", 26 | "gulp-cjsx": "^2.0.0", 27 | "gulp-coffee": "^2.3.1", 28 | "gulp-espower": "^0.10.0", 29 | "gulp-if": "^1.2.5", 30 | "gulp-ignore": "^1.2.1", 31 | "gulp-jsforce-deploy": "^1.0.0", 32 | "gulp-less": "^3.0.0", 33 | "gulp-minify-css": "^0.4.6", 34 | "gulp-mocha": "^2.0.0", 35 | "gulp-notify": "^2.2.0", 36 | "gulp-plumber": "^0.6.6", 37 | "gulp-protractor": "0.0.12", 38 | "gulp-react-templates": "0.0.5", 39 | "gulp-rename": "^1.2.0", 40 | "gulp-streamify": "0.0.5", 41 | "gulp-uglify": "^1.1.0", 42 | "gulp-util": "^3.0.3", 43 | "gulp-zip": "^2.0.2", 44 | "intelli-espower-loader": "^0.6.0", 45 | "karma": "^0.12.31", 46 | "karma-chrome-launcher": "^0.1.7", 47 | "karma-mocha": "^0.1.10", 48 | "karma-mocha-reporter": "^1.0.0", 49 | "karma-phantomjs-launcher": "^0.1.4", 50 | "mocha": "^2.1.0", 51 | "power-assert": "^0.10.2", 52 | "protractor": "^1.7.0", 53 | "through2": "^0.6.3", 54 | "vinyl-source-stream": "^1.0.0", 55 | "vinyl-transform": "^1.0.0", 56 | "watchify": "^2.4.0" 57 | }, 58 | "dependencies": { 59 | "lodash": "^3.3.0", 60 | "react": "^0.12.2", 61 | "react-bootstrap": "^0.15.1" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MyAppPage 5 | ApexPage 6 | 7 | 8 | MyApp 9 | MyAppLib 10 | StaticResource 11 | 12 | 32.0 13 | 14 | -------------------------------------------------------------------------------- /src/pages/MyAppPage.page: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/pages/MyAppPage.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32.0 4 | false 5 | false 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/staticresources/.gitignore: -------------------------------------------------------------------------------- 1 | *.resource 2 | -------------------------------------------------------------------------------- /src/staticresources/MyApp.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Private 4 | application/octet-stream 5 | 6 | -------------------------------------------------------------------------------- /src/staticresources/MyAppLib.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Private 4 | application/octet-stream 5 | 6 | -------------------------------------------------------------------------------- /test/e2e/app001.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var assert = require("power-assert"); 3 | 4 | describe("apptest001", function() { 5 | it("should raise", function() { 6 | }); 7 | }); -------------------------------------------------------------------------------- /test/unit/components/foo.test.coffee: -------------------------------------------------------------------------------- 1 | assert = require "power-assert" 2 | 3 | _ = require "lodash" 4 | React = require "react" 5 | Foo = require "../../../app/scripts/components/foo" 6 | 7 | # 8 | describe "foo", -> 9 | it "should create a element", -> 10 | assert _.isObject(Foo) 11 | el = React.createElement(Foo, {}) 12 | assert _.isObject(el) 13 | -------------------------------------------------------------------------------- /test/unit/util/say-hello.test.js: -------------------------------------------------------------------------------- 1 | var assert = require("power-assert"); 2 | var sayHello = require("../../../app/scripts/util/say-hello"); 3 | 4 | describe("say hello", function() { 5 | it("should match message", function() { 6 | var msg = sayHello("John"); 7 | assert(msg === "Hello, John"); 8 | }); 9 | }); --------------------------------------------------------------------------------