├── .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 |
--------------------------------------------------------------------------------
/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 | });
--------------------------------------------------------------------------------