├── src
├── init.client.js
├── addons
│ ├── tardis
│ │ ├── bundles
│ │ │ └── .gitignore
│ │ ├── routes.jsx
│ │ ├── components
│ │ │ ├── Tardis.jsx
│ │ │ └── TardisGallery.jsx
│ │ ├── settings.default.js
│ │ ├── styles.css
│ │ └── TardisGallery.server.js
│ └── utilities.js
├── nuclearDefinitions
│ ├── stores
│ │ ├── index.js
│ │ ├── settings.js
│ │ └── routerState.js
│ ├── getters.js
│ ├── actions.js
│ └── index.js
├── babelBlacklist.js
├── defaults
│ ├── styles
│ │ ├── fonts.css
│ │ ├── init.css
│ │ └── react-native.css
│ └── Scaffold.jsx
├── init.common.js
├── mixins
│ ├── Settings.jsx
│ ├── NuclearContext.jsx
│ ├── SettingsContext.jsx
│ ├── Title.jsx
│ ├── Nuclear.jsx
│ └── TitleContext.jsx
├── init.server.js
├── createHandlerWithAmbidexContext.jsx
├── render.client.js
├── createWebpackSettings.js
└── Ambidex.server.js
├── .gitignore
├── .editorconfig
├── LICENSE.md
├── package.json
├── CHANGELOG.md
├── README.md
└── STYLE_GUIDE.md
/src/init.client.js:
--------------------------------------------------------------------------------
1 | module.exports = require("./init.common.js");
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | cache/
3 | .sass-cache/
4 | *.sublime-*
5 | npm-debug.log
6 |
--------------------------------------------------------------------------------
/src/addons/tardis/bundles/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything in this directory
2 | *
3 | # Except this file
4 | !.gitignore
5 |
--------------------------------------------------------------------------------
/src/nuclearDefinitions/stores/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "routerState": require("./routerState"),
3 | "settings": require("./settings"),
4 | };
5 |
--------------------------------------------------------------------------------
/src/nuclearDefinitions/getters.js:
--------------------------------------------------------------------------------
1 | var routerState = ["routerState"];
2 | var settings = ["settings"];
3 |
4 | module.exports = {
5 | routerState,
6 | settings
7 | };
8 |
--------------------------------------------------------------------------------
/src/nuclearDefinitions/actions.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "simple": [
3 | "loadSettings",
4 | "routerStateChanged",
5 | ]
6 | };
7 |
--------------------------------------------------------------------------------
/src/nuclearDefinitions/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "simpleActions": require("./actions").simple,
3 | "stores": require("./stores"),
4 | "getters": require("./getters"),
5 | };
6 |
--------------------------------------------------------------------------------
/src/addons/tardis/routes.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react/addons");
2 | var ReactRouter = require("react-router");
3 |
4 | var Route = ReactRouter.Route;
5 |
6 | module.exports = ;
10 |
--------------------------------------------------------------------------------
/src/babelBlacklist.js:
--------------------------------------------------------------------------------
1 | // Don't transpile the dependencies we know work with ES5. Manually
2 | // blacklisting isn't super scalable, but it does allow developers to break
3 | // their codebases into modules easily without sweating JS versions.
4 |
5 | module.exports = /node_modules\/(mach|react|babel|immutable|lazy\.js|webpack|socket.io|[\w\-]+\-loader\/)/;
6 |
--------------------------------------------------------------------------------
/src/defaults/styles/fonts.css:
--------------------------------------------------------------------------------
1 | /* Make Roboto (the font of Material Design and the Android UI) our default
2 | * font. */
3 |
4 | @import url("https://fonts.googleapis.com/css?family=Roboto:500,500italic,400,400italic,300,300italic|Roboto+Condensed:400");
5 |
6 | body {
7 | font-family: "Roboto", sans-serif;
8 | }
9 |
--------------------------------------------------------------------------------
/src/nuclearDefinitions/stores/settings.js:
--------------------------------------------------------------------------------
1 | var Immutable = require("immutable");
2 |
3 | var settings = {
4 | "initialize": function () {
5 | this.on(
6 | this.actions.loadSettings,
7 |
8 | (lastValue, { settings }) => settings
9 | );
10 | }
11 | };
12 |
13 | module.exports = settings;
14 |
--------------------------------------------------------------------------------
/src/init.common.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "addons": {
3 | "utilities": require("./addons/utilities.js"),
4 | },
5 |
6 | "mixins": {
7 | "Nuclear": require("./mixins/Nuclear.jsx"),
8 | "Settings": require("./mixins/Settings.jsx"),
9 | "Title": require("./mixins/Title.jsx"),
10 | },
11 | };
12 |
--------------------------------------------------------------------------------
/src/nuclearDefinitions/stores/routerState.js:
--------------------------------------------------------------------------------
1 | var Immutable = require("immutable");
2 |
3 | var routerState = {
4 | "initialize": function () {
5 | this.on(
6 | this.actions.routerStateChanged,
7 |
8 | (lastValue, { routerState }) => Immutable.fromJS(routerState)
9 | );
10 | }
11 | };
12 |
13 | module.exports = routerState;
14 |
--------------------------------------------------------------------------------
/src/defaults/styles/init.css:
--------------------------------------------------------------------------------
1 | /* React Native utilizes inline styles with flexbox for layout. In the
2 | * spirit of "Learn Once, Write Everywhere", Ambidex sets the same
3 | * defaults as React Native for authors who use inline styles.
4 | *
5 | * If you'd like to opt-out, just set settings.FILESYSTEM_PATHS["STYLES"]
6 | * to a file you control. */
7 |
8 | @import url("./fonts.css");
9 | @import url("./react-native.css");
10 |
--------------------------------------------------------------------------------
/src/mixins/Settings.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react/addons");
2 |
3 | var SettingsMixin = {
4 | "contextTypes": {
5 | "ambidexSettings": React.PropTypes.object.isRequired,
6 | },
7 |
8 | "getAmbidexSettings": function () {
9 | return this.context.ambidexSettings;
10 | }
11 | };
12 |
13 | module.exports = SettingsMixin;
14 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
23 | [*.jsx]
24 | trim_trailing_whitespace = false
25 |
--------------------------------------------------------------------------------
/src/addons/tardis/components/Tardis.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react/addons");
2 |
3 | var Tardis = React.createClass(
4 | {
5 | "render": function () {
6 | // iOS will ignore the width/height of an iframe,
7 | // but we can wrap it in an explicitly-sized div
8 | return
9 |
10 |
;
11 | }
12 | }
13 | );
14 |
15 | module.exports = Tardis;
16 |
--------------------------------------------------------------------------------
/src/mixins/NuclearContext.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react/addons");
2 |
3 | var NuclearMixin = require("./Nuclear.jsx");
4 |
5 | var NuclearContextMixin = {
6 | "propTypes": {
7 | "reactor": React.PropTypes.object.isRequired,
8 | },
9 |
10 | "childContextTypes": NuclearMixin.contextTypes,
11 |
12 | "getChildContext": function () {
13 | return {
14 | "ambidexReactor": this.props.reactor,
15 | }
16 | },
17 | };
18 |
19 | module.exports = NuclearContextMixin;
20 |
--------------------------------------------------------------------------------
/src/mixins/SettingsContext.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react/addons");
2 |
3 | var SettingsMixin = require("./Settings.jsx");
4 |
5 | var SettingsContextMixin = {
6 | "propTypes": {
7 | "settings": React.PropTypes.object.isRequired,
8 | },
9 |
10 | "childContextTypes": SettingsMixin.contextTypes,
11 |
12 | "getChildContext": function () {
13 | return {
14 | "ambidexSettings": this.props.settings,
15 | }
16 | },
17 | };
18 |
19 | module.exports = SettingsContextMixin;
20 |
--------------------------------------------------------------------------------
/src/init.server.js:
--------------------------------------------------------------------------------
1 | var babelBlacklist = require("./babelBlacklist");
2 |
3 | require("babel/register")(
4 | {
5 | "stage": 1, // Allow experimental transforms (specifically, ...rest in objects)
6 | "ignore": babelBlacklist,
7 | }
8 | );
9 |
10 | require("make-node-env-global")();
11 |
12 | var recursiveCloneWithDefaults = require("./addons/utilities.js").recursiveCloneWithDefaults;
13 |
14 | module.exports = Object.assign(
15 | require("./Ambidex.server.js"),
16 |
17 | recursiveCloneWithDefaults(
18 | {
19 | "addons": {
20 | "TardisGallery": require("./addons/tardis/TardisGallery.server.js"),
21 | }
22 | },
23 |
24 | require("./init.common.js")
25 | )
26 | );
27 |
--------------------------------------------------------------------------------
/src/createHandlerWithAmbidexContext.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react/addons");
2 |
3 | var NuclearContextMixin = require("./mixins/NuclearContext.jsx");
4 | var SettingsContextMixin = require("./mixins/SettingsContext.jsx");
5 | var TitleContextMixin = require("./mixins/TitleContext.jsx");
6 |
7 | var createHandlerWithAmbidexContext = function (optionalFeatures) {
8 | var mixins = [
9 | SettingsContextMixin,
10 | TitleContextMixin,
11 | ];
12 |
13 | if (optionalFeatures.nuclear)
14 | mixins.push(NuclearContextMixin);
15 |
16 | return React.createClass(
17 | {
18 | "mixins": mixins,
19 |
20 | "render": function () {
21 | return ;
22 | }
23 | }
24 | );
25 | };
26 |
27 | module.exports = createHandlerWithAmbidexContext;
28 |
--------------------------------------------------------------------------------
/src/addons/tardis/settings.default.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "NAME": "TARDIS Gallery",
3 | "SHORT_NAME": "tardis_gallery",
4 | "HOST": "tardis.local",
5 | "PORT": "8080",
6 | "WEBPACK_PORT": "8078",
7 |
8 | "ENABLE_HOT_MODULE_REPLACEMENT": NODE_ENV === "local",
9 |
10 | "FILESYSTEM_PATHS": {
11 | "ROUTES": `${ __dirname }/routes.jsx`,
12 | "STYLES": `${ __dirname }/styles.css`,
13 | "BUNDLES": `${ __dirname }/bundles/`,
14 | },
15 |
16 | "SERVER_ONLY_MODULE_NAMES": [
17 | ],
18 |
19 | // TARDIS-specific settings:
20 |
21 | "APPEND_PATH_TO_URLS": true
22 | };
23 |
--------------------------------------------------------------------------------
/src/addons/tardis/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #313E4B;
3 | margin: 8px;
4 | }
5 |
6 | .TardisGallery {
7 | display: flex;
8 | justify-content: space-between;
9 | flex-wrap: wrap;
10 | }
11 |
12 | .Tardis {
13 | position: relative;
14 | display: inline-block;
15 | border: 1px solid hsla(0, 100%, 100%, 0.5);
16 |
17 | /* iOS will ignore the size of an iframe so wrap it in a size-constrained div */
18 | width: 320px;
19 | height: 568px;
20 | margin: 7px;
21 |
22 | -webkit-overflow-scrolling: touch;
23 | overflow-y: scroll;
24 | }
25 |
26 | .Tardis iframe {
27 | position: relative;
28 |
29 | width: 100%;
30 | height: 100%;
31 |
32 | border: none;
33 | }
34 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 eBay Software Foundation
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/src/defaults/styles/react-native.css:
--------------------------------------------------------------------------------
1 | /* Sets all block level elements (https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements#Elements)
2 | * to the React Native defaults (https://github.com/facebook/css-layout/blob/ecc61dae85407148c3fb0077e16bb718592e4e8d/README.md#default-values)
3 | */
4 |
5 | * {
6 | position: relative;
7 | box-sizing: border-box;
8 | border: 0 solid black;
9 | margin: 0;
10 | padding: 0;
11 | -webkit-tap-highlight-color: transparent;
12 | }
13 |
14 | address,
15 | article,
16 | aside,
17 | blockquote,
18 | dd,
19 | div,
20 | dl,
21 | fieldset,
22 | figcaption,
23 | figure,
24 | footer,
25 | form,
26 | h1,
27 | h2,
28 | h3,
29 | h4,
30 | h5,
31 | h6,
32 | header,
33 | hgroup,
34 | main,
35 | nav,
36 | noscript,
37 | ol,
38 | output,
39 | p,
40 | section,
41 | ul {
42 | /* TODO: figure out why autoprefixer-loader isn't working here */
43 | display: -webkit-flex;
44 | display: flex;
45 |
46 | -webkit-flex-direction: column;
47 | flex-direction: column;
48 |
49 | -webkit-flex-shrink: 0;
50 | flex-shrink: 0;
51 |
52 | -webkit-align-items: stretch;
53 | align-items: stretch;
54 | }
55 |
--------------------------------------------------------------------------------
/src/mixins/Title.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react/addons");
2 |
3 | /* Since React components are designed to be idempotent, calling
4 | * componentWillMount/DidUpdate repeatedly needs to be harmless.
5 | *
6 | * If we pushed a primative (like a string) directly onto the stack,
7 | * we'd have to worry about duplicates. Therefore, we simply register
8 | * that this component implements sectionTitle and let Ambidex look up
9 | * its title when Ambidex needs to know it.
10 | */
11 |
12 | var TitleMixin = {
13 | "contextTypes": {
14 | "ambidexRegisterTitledComponent": React.PropTypes.func.isRequired,
15 | },
16 |
17 | "_registerAsTitledComponent": function () {
18 | if (this.sectionTitle || this.getSectionTitle) {
19 | this.context.ambidexRegisterTitledComponent(this);
20 | }
21 | },
22 |
23 | "componentWillMount": function () {
24 | this._registerAsTitledComponent();
25 | },
26 |
27 | "componentWillReceiveProps": function () {
28 | this._registerAsTitledComponent();
29 | },
30 | };
31 |
32 | module.exports = TitleMixin;
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ambidex",
3 | "version": "0.2.0-beta5",
4 | "description": "Effortlessly host your React app on both the client and the server. Some call it isomorphic JavaScript - we call it Ambidex.",
5 | "contributors": [
6 | "Brenton Simpson (http://appsforartists.com)"
7 | ],
8 | "license": "MIT",
9 | "files": [
10 | "src"
11 | ],
12 | "browser": "src/init.client.js",
13 | "main": "src/init.server.js",
14 | "optionalDependencies": {
15 | "react-hot-loader": "1.1.4",
16 | "webpack-dev-server": "1.7.0"
17 | },
18 | "peerDependencies": {
19 | "react": "0.13",
20 | "experimental-nuclear-js": "~0.0.0",
21 | "react-tap-event-plugin": "0.1.3",
22 | "react-router": "0.13"
23 | },
24 | "dependencies": {
25 | "autoprefixer-loader": "1.0",
26 | "css-loader": "0.9.0",
27 | "immutable": "3",
28 | "babel": "5",
29 | "babel-loader": "5",
30 | "lazy.js": "0.4.0",
31 | "mach": "1.2.0",
32 | "make-node-env-global": "1.0.0",
33 | "prfun": "1.0.2",
34 | "style-loader": "0.8.3",
35 | "to-camel-case": "0.2.1",
36 | "webpack": "1.5.3"
37 | },
38 | "repository": {
39 | "type": "git",
40 | "url": "git@github.com:appsforartists/ambidex.git"
41 | },
42 | "bugs": {
43 | "url": "https://github.com/appsforartists/ambidex/issues"
44 | },
45 | "homepage": "https://github.com/appsforartists/ambidex",
46 | "devDependencies": {
47 | "rf-release": "^0.4.0"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/mixins/Nuclear.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react/addons");
2 |
3 | var NuclearMixin = {
4 | "contextTypes": {
5 | "ambidexReactor": React.PropTypes.object.isRequired,
6 | },
7 |
8 | "getInitialState": function () {
9 | // this.reactor shouldn't ever change, so keeping it simple
10 | // and not worrying about componentWillUpdate
11 | this.reactor = this.context.ambidexReactor;
12 |
13 | return this.getDataBindings === undefined
14 | ? {}
15 | : this.reactor.ReactMixin.getInitialState.apply(this);
16 | },
17 |
18 | // Should probably be more intropective about this for futureproofness;
19 | // but for now, this does the job
20 | "componentDidMount": function () {
21 | if (this.getDataBindings !== undefined) {
22 | return this.reactor.ReactMixin.componentDidMount.apply(this, arguments);
23 | }
24 | },
25 |
26 | "componentWillUnmount": function () {
27 | if (this.getDataBindings !== undefined) {
28 | return this.reactor.ReactMixin.componentWillUnmount.apply(this, arguments);
29 | }
30 | },
31 | };
32 |
33 | module.exports = NuclearMixin;
34 |
--------------------------------------------------------------------------------
/src/addons/tardis/components/TardisGallery.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react/addons");
2 | var ReactRouter = require("react-router");
3 | var Ambidex = require("ambidex");
4 |
5 | var Tardis = require("./Tardis.jsx");
6 |
7 | var TardisGallery = React.createClass(
8 | {
9 | "mixins": [
10 | Ambidex.mixins.Settings,
11 | Ambidex.mixins.Title,
12 | ReactRouter.State
13 | ],
14 |
15 | "getSectionTitle": function () {
16 | return this.getAmbidexSettings().NAME;
17 | },
18 |
19 | "render": function () {
20 | var settings = this.getAmbidexSettings();
21 |
22 | // forward any path the user types into the gallery on to each TARDIS
23 | var path = settings.APPEND_PATH_TO_URLS
24 | ? this.getPath()
25 | : "";
26 |
27 | var tardises = settings.URLS.map(
28 | (url) => {
29 | url = url.indexOf("//") === -1
30 | ? "//" + url
31 | : url;
32 |
33 | return
37 | }
38 | );
39 |
40 | return
41 | { tardises }
42 |
43 | }
44 | }
45 | );
46 |
47 | module.exports = TardisGallery;
48 |
--------------------------------------------------------------------------------
/src/addons/utilities.js:
--------------------------------------------------------------------------------
1 | var Lazy = require("lazy.js");
2 |
3 | var utilities = {
4 | "noOp": function () {
5 | },
6 |
7 | "echo": function () {
8 | switch (arguments.length) {
9 | case 0:
10 | return undefined;
11 |
12 | case 1:
13 | return arguments[0];
14 |
15 | default:
16 | return Array.prototype.slice.call(arguments);
17 | };
18 | },
19 |
20 | "echoFirst": function () {
21 | return arguments[0];
22 | },
23 |
24 | "hasValue": function (value) {
25 | return value !== null && value !== undefined;
26 | },
27 |
28 | "hasContent": function (value) {
29 | try {
30 | return Boolean(Object.keys(value).length)
31 |
32 | } catch (error) {
33 | return utilities.hasValue(value);
34 | }
35 | },
36 |
37 | "recursiveCloneWithDefaults": function (instance, defaults) {
38 | instance = instance || {};
39 |
40 | return Lazy(instance).defaults(defaults).map(
41 | (value, key) => value.constructor === Object
42 | ? [key, utilities.recursiveCloneWithDefaults(instance[key] || {}, defaults[key] || {})]
43 | : [key, value]
44 | ).toObject();
45 | },
46 | };
47 |
48 | module.exports = utilities;
49 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | v0.2.0-beta5 - Tue, 28 Apr 2015 03:37:14 GMT
2 | --------------------------------------------
3 |
4 | - [657c396](../../commit/657c396) [changed] JSX compilers from react-tools to jstransform@11.0.0
5 | - [5309a18](../../commit/5309a18) [added] XSS protection to Funx deserialization
6 | - [d43473c](../../commit/d43473c) [fixed] warning when passing Buffers to Scaffold
7 |
8 |
9 | v0.2.0-beta4 - Fri, 10 Apr 2015 23:18:06 GMT
10 | --------------------------------------------
11 |
12 | - [5309a18](../../commit/5309a18) [added] XSS protection to Funx deserialization
13 | - [d43473c](../../commit/d43473c) [fixed] warning when passing Buffers to Scaffold
14 |
15 |
16 | v0.2.0-beta3 - Fri, 27 Mar 2015 22:46:38 GMT
17 | --------------------------------------------
18 |
19 | - [6b5fb17](../../commit/6b5fb17) [changed] flux implementation from isomorphic-reflux to funx
20 | - [956a4ce](../../commit/956a4ce) [removed] SASS styles from TARDIS
21 |
22 |
23 | v0.2.0-beta2 - Sun, 01 Mar 2015 17:18:20 GMT
24 | --------------------------------------------
25 |
26 | - [9de0db9](../../commit/9de0db9) [fixed] isomorphic-reflux version
27 | - [b7063dd](../../commit/b7063dd) [added] Scaffold.propTypes
28 | - [6ead0f7](../../commit/6ead0f7) [fixed] mixed content warning on HTTPS pages
29 | - [44103ed](../../commit/44103ed) [added] manual -webkit prefixes to default styles
30 |
31 |
32 | v0.2.0-beta1 - Tue, 24 Feb 2015 01:56:04 GMT
33 | --------------------------------------------
34 |
35 | - [64be9ea](../../commit/64be9ea) [added] warning if undefined action or store requested
36 | - [eb4bc82](../../commit/eb4bc82) [added] default stylesheet
37 | - [5c5bd36](../../commit/5c5bd36) **[removed] sass-loader**
38 | - [b82ab33](../../commit/b82ab33) [changed] dependencies to latest versions
39 |
40 |
41 | v0.1.4 - Tue, 24 Feb 2015 01:34:18 GMT
42 | --------------------------------------
43 |
44 | - [653165a](../../commit/653165a) [fixed] connectStoresToLocalState's arguments assertion
45 | - [150a295](../../commit/150a295) [fixed] `undefined` error in UglifyJS
46 |
47 |
48 | v0.1.3 - Thu, 05 Feb 2015 01:49:27 GMT
49 | --------------------------------------
50 |
51 | - [48f7cbd](../../commit/48f7cbd) [added] missing polyfill for "".includes
52 |
53 |
54 | v0.1.2 - Thu, 05 Feb 2015 01:47:02 GMT
55 | --------------------------------------
56 |
57 | - _(This release was redacted. [rf-release](https://github.com/ryanflorence/rf-release) committed some files that didn't belong in this tag, so I stashed those and committed the rest as 0.1.3.)_
58 |
59 |
60 | v0.1.1 - Mon, 2 Feb 2014 08:16:25 PST
61 | ---------------------------------------
62 |
63 | - [473794d](../../commit/473794d) [changed] Webpack's resolve root to match `ambidex` on npm
64 |
65 |
66 | v0.1.0 - Wed, 28 Nov 2014 22:24:53 PST
67 | ---------------------------------------
68 |
69 | - [e418368](../../commit/e418368) Releasing developer preview for 2015 React Conf
70 |
--------------------------------------------------------------------------------
/src/addons/tardis/TardisGallery.server.js:
--------------------------------------------------------------------------------
1 | var mach = require("mach");
2 |
3 | var Ambidex = require("../../Ambidex.server.js");
4 | var utilities = require("../utilities.js");
5 |
6 | function TardisGallery(
7 | {
8 | ambidexPromises,
9 | settings
10 | }
11 | ) {
12 | var customSettings = settings;
13 |
14 | settings = utilities.recursiveCloneWithDefaults(
15 | customSettings,
16 | require("./settings.default.js")
17 | );
18 |
19 | // Introspect URLS from ambidexes, if they aren't already set
20 | if (settings.URLS) {
21 | settings.APPEND_PATH_TO_URLS = customSettings.APPEND_PATH_TO_URLS || false;
22 |
23 | if (ambidexPromises) {
24 | var { HOST, PORT } = ambidexPromises[0].ambidexInProgress._get("settings");
25 |
26 | settings.URLS = settings.URLS.map(
27 | URL => URL.startsWith("/") && !URL.startsWith("//")
28 | ? `//${ HOST }:${ PORT }${ URL }`
29 | : URL
30 | );
31 | }
32 | } else {
33 | settings.URLS = ambidexPromises.map(
34 | promise => {
35 | var { HOST, PORT } = promise.ambidexInProgress._get("settings");
36 |
37 | return `//${ HOST }:${ PORT }`
38 | }
39 | );
40 | }
41 |
42 | var self = new Ambidex({ settings });
43 | self.mapper = mach.mapper();
44 |
45 | ambidexPromises = ambidexPromises.concat(self);
46 |
47 | ambidexPromises.forEach(
48 | promise => promise.ambidexInProgress._isBeingServedExternally = true
49 | );
50 |
51 | // Make sure the Ambidexes are dormant, then serve them by HOST with mapper
52 | return Promise.all(
53 | ambidexPromises
54 | ).then(
55 | ambidexes => Promise.all(
56 | ambidexes.map(
57 | ambidex => {
58 | if (ambidex.stack.nodeServer && ambidex.stack.nodeServer.address()) {
59 | return new Promise(
60 | (resolve, reject) => {
61 | ambidex.stack.nodeServer.close(
62 | () => resolve(ambidex)
63 | );
64 | }
65 | );
66 |
67 | } else {
68 | return ambidex;
69 | }
70 | }
71 | )
72 | )
73 | ).then(
74 | ambidexes => {
75 | ambidexes.forEach(
76 | ambidex => {
77 | var { HOST, BASE_URL } = ambidex._get("settings");
78 |
79 | if (!BASE_URL)
80 | BASE_URL = "/"
81 |
82 | if (!BASE_URL.startsWith("/"))
83 | BASE_URL = "/" + BASE_URL
84 |
85 | if (!BASE_URL.endsWith("/"))
86 | BASE_URL = BASE_URL + "/"
87 |
88 | self.mapper.map(
89 | ambidex === self.ambidexInProgress
90 | ? "/"
91 | : `http://${ HOST }${ BASE_URL }`,
92 | ambidex.stack
93 | );
94 | }
95 | );
96 |
97 | return ambidexes;
98 | }
99 | ).then(
100 | ambidexes => {
101 | ambidexes.forEach(
102 | ambidex => console.info(`Starting mach for ${ ambidex._get("settings").NAME } on ${ settings.HOST }:${ settings.PORT }…`)
103 | );
104 |
105 | self.mapper.nodeServer = mach.serve(
106 | self.mapper,
107 | settings.VM_PORT || settings.PORT
108 | );
109 |
110 | return self;
111 | }
112 | ).catch(
113 | error => console.error(error.stack)
114 | );
115 | }
116 |
117 | module.exports = TardisGallery;
118 |
119 |
--------------------------------------------------------------------------------
/src/mixins/TitleContext.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react/addons");
2 |
3 | var utilities = require("../addons/utilities.js");
4 | var TitleMixin = require("./Title.jsx");
5 |
6 | var TitleContextMixin = {
7 | "propTypes": {
8 | "settings": React.PropTypes.object.isRequired,
9 | "setTitle": React.PropTypes.func.isRequired,
10 |
11 |
12 | // Since there are no post-render lifecycle hooks that fire on the
13 | // server, we fake one here.
14 |
15 | "listenForServerDidRender": React.PropTypes.func,
16 | },
17 |
18 | "getInitialState": function () {
19 | return {
20 | "titledComponents": []
21 | }
22 | },
23 |
24 | "childContextTypes": TitleMixin.contextTypes,
25 |
26 | "getChildContext": function () {
27 | return {
28 | "ambidexRegisterTitledComponent": this._registerTitledComponent,
29 | }
30 | },
31 |
32 | "componentWillReceiveProps": function () {
33 | // clear titledComponents between renders
34 | this.setState(
35 | {
36 | "titledComponents": []
37 | }
38 | );
39 | },
40 |
41 | "componentWillMount": function () {
42 | if (this.props.listenForServerDidRender) {
43 | this.props.listenForServerDidRender(this._updateTitle);
44 | }
45 | },
46 |
47 | "componentDidMount": function () {
48 | this._updateTitle();
49 | },
50 |
51 | "componentDidUpdate": function () {
52 | this._updateTitle();
53 | },
54 |
55 | "_registerTitledComponent": function (newTitledComponent) {
56 | var titledComponents = this.state.titledComponents;
57 |
58 | if (titledComponents.indexOf(newTitledComponent) === -1)
59 | titledComponents.push(newTitledComponent);
60 | },
61 |
62 | "_updateTitle": function () {
63 | this.props.setTitle(
64 | this.state.titledComponents.map(
65 | titledComponent => titledComponent.sectionTitle || titledComponent.getSectionTitle()
66 |
67 | ).filter(
68 | potentialSectionTitle => Boolean(potentialSectionTitle) || potentialSectionTitle === 0
69 |
70 | ).join(
71 | this.props.settings.TITLE_SEPARATOR || " "
72 | )
73 | );
74 | }
75 | };
76 |
77 | module.exports = TitleContextMixin;
78 |
--------------------------------------------------------------------------------
/src/render.client.js:
--------------------------------------------------------------------------------
1 | require("babel/polyfill");
2 |
3 | var Ambidex = require("ambidex");
4 | var React = require("react/addons");
5 | var ReactRouter = require("react-router");
6 | var Immutable = require("immutable");
7 | var injectTapEventPlugin = require("react-tap-event-plugin");
8 |
9 | var {
10 | Reactor
11 | } = require("experimental-nuclear-js");
12 |
13 | var createHandlerWithAmbidexContext = require("./createHandlerWithAmbidexContext.jsx");
14 |
15 |
16 | injectTapEventPlugin();
17 |
18 | var containerSelector = "body";
19 |
20 | var HandlerWithAmbidexContext = createHandlerWithAmbidexContext(
21 | // enable/disable features based on what settings the developer has passed in
22 | {
23 | "nuclear": Boolean(__ambidexPaths.nuclearDefinitions)
24 | }
25 | );
26 |
27 | var reactor;
28 | var router = ReactRouter.create(
29 | {
30 | "routes": require(__ambidexPaths.routes),
31 | "location": ReactRouter.HistoryLocation,
32 | }
33 | );
34 |
35 | if (__ambidexPaths.nuclearDefinitions) {
36 | function createReactor () {
37 | // Anything that changes here needs to change in Ambidex.server.js too
38 |
39 | var result = new Reactor(
40 | {
41 | "debug": NODE_ENV !== "production",
42 | "ambidex": require("./nuclearDefinitions"),
43 |
44 | ...require(__ambidexPaths.nuclearDefinitions),
45 | }
46 | );
47 |
48 | result.ambidex.actions.redirect = function (
49 | {
50 | path,
51 | routeName,
52 | params = {},
53 | query = {},
54 | clearReactor = false,
55 | }
56 | ) {
57 | path = path || router.makePath(
58 | routeName,
59 | params,
60 | query
61 | );
62 |
63 | if (path === router.getCurrentPath())
64 | return;
65 |
66 | if (clearReactor) {
67 | reactor.dispatch = Ambidex.addons.utilities.noOp;
68 | reactor = createReactor();
69 | }
70 |
71 | router.transitionTo(path);
72 | };
73 |
74 | result.ambidex.actions.requireAuthentication = function (
75 | {
76 | routeName = "login",
77 | params = {},
78 | query = {},
79 | next,
80 | } = {}
81 | ) {
82 | if (next || !query.next)
83 | query.next = router.getCurrentPath();
84 |
85 |
86 | result.ambidex.actions.redirect(
87 | {
88 | routeName,
89 | params,
90 | query,
91 | "clearReactor": true,
92 | }
93 | );
94 | };
95 |
96 | result.ambidex.actions.loadSettings(
97 | {
98 | "settings": __ambidexSettings
99 | }
100 | );
101 |
102 | return result;
103 | };
104 |
105 | reactor = createReactor();
106 | }
107 |
108 | var initialRenderComplete;
109 |
110 | var mountReact = function() {
111 | var container = document.querySelector(containerSelector);
112 |
113 | if (!container) {
114 | return initialRenderComplete = false;
115 |
116 | } else {
117 | router.run(
118 | (Handler, routerState) => {
119 | if (reactor) {
120 | if (initialRenderComplete) {
121 | reactor.ambidex.actions.routerStateChanged(
122 | {
123 | routerState
124 | }
125 | );
126 |
127 | } else {
128 | reactor.deserialize(window.__ambidexStoreStateByName);
129 | }
130 | }
131 |
132 | // Anything that changes here needs to change in Ambidex.server.js too
133 | React.render(
134 | {
138 | document.title = title
139 | }
140 | }
141 |
142 | { ...{Handler, reactor} }
143 | />,
144 |
145 | container
146 | );
147 | }
148 | );
149 |
150 | return initialRenderComplete = true;
151 | }
152 | };
153 |
154 | if (!mountReact()) {
155 | window.addEventListener(
156 | "DOMContentLoaded",
157 | mountReact
158 | );
159 | }
160 |
--------------------------------------------------------------------------------
/src/createWebpackSettings.js:
--------------------------------------------------------------------------------
1 | var Webpack = require("webpack");
2 | var Lazy = require("lazy.js");
3 |
4 | var babelBlacklist = require("./babelBlacklist");
5 |
6 | function getSettings (options) {
7 | var settings = {
8 | "entry": {},
9 |
10 | "resolve": {
11 | "extensions": [
12 | "",
13 | ".js",
14 | ".jsx",
15 | ".scss"
16 | ]
17 | },
18 |
19 | "module": {
20 | "loaders": [
21 | {
22 | "test": /\.jsx?$/,
23 | "loader": "babel-loader?stage=1",
24 | "exclude": babelBlacklist
25 | },
26 | {
27 | "test": /\.css$/,
28 | "loader": "style-loader!css-loader!autoprefixer-loader"
29 | }
30 | ]
31 | },
32 |
33 | "output": {
34 | "filename": "[name].js",
35 | "chunkFilename": "chunk_[id].js",
36 | "publicPath": "/bundles/",
37 | "pathinfo": true
38 | },
39 |
40 | "plugins": [
41 | new Webpack.optimize.DedupePlugin(),
42 | new Webpack.optimize.OccurenceOrderPlugin(),
43 | ],
44 | };
45 |
46 | // `settings.context` tells Webpack where to look up its relative paths.
47 | // This includes things you wouldn't expect, like the NPM modules for each
48 | // loader.
49 | //
50 | // Since `options.paths` already gives us absolute paths to our external
51 | // dependencies, we set `context` to our own `__dirname`. This makes sure
52 | // the dependencies are always loaded, even if our caller doesn't have them
53 | // installed.
54 |
55 | settings.context = __dirname;
56 | settings.resolveLoader = settings.resolveLoader || {};
57 | settings.resolveLoader.root = __dirname.replace("/ambidex/src", "/ambidex/node_modules");
58 |
59 | if (options.hasOwnProperty("paths")) {
60 |
61 | if (options.paths.hasOwnProperty("BUNDLES"))
62 | settings.output.path = options.paths["BUNDLES"];
63 |
64 |
65 | Lazy(options.paths).each(
66 | (value, key) => {
67 | key = key.toLowerCase();
68 |
69 | if (!value.endsWith("/")) {
70 | settings.entry[key] = Array.isArray(settings.entry[key])
71 | ? settings.entry[key]
72 | : [];
73 |
74 | settings.entry[key].push(value);
75 | }
76 | }
77 | );
78 | }
79 |
80 | if (options.hasOwnProperty("devServerOrigin")) {
81 |
82 | // Add the HMR client to each exported bundle
83 | for (var bundleName in settings.entry) {
84 | settings.entry[bundleName].push(
85 | "webpack-dev-server/client?" + options.devServerOrigin, // e.g. localhost:8081
86 | "webpack/hot/dev-server"
87 | );
88 | }
89 |
90 | settings.output.publicPath = options.devServerOrigin + settings.output.publicPath;
91 |
92 |
93 | // react-hot-loader will keep the components updated when HMR happens
94 | var jsxLoaderSettings = settings.module.loaders.filter(
95 | function (loader, i, loaders) {
96 | return loader.test.exec(".jsx");
97 | }
98 | )[0];
99 |
100 | jsxLoaderSettings.loader = "react-hot-loader!" + jsxLoaderSettings.loader;
101 |
102 | settings.plugins.push(
103 | new Webpack.HotModuleReplacementPlugin()
104 | );
105 | }
106 |
107 | if (options.hasOwnProperty("constants")) {
108 | settings.plugins.push(
109 | new Webpack.DefinePlugin(options.constants)
110 | );
111 | }
112 |
113 | if (options.hasOwnProperty("ignoredModuleNames") && options.ignoredModuleNames.length) {
114 | settings.plugins.push(
115 | new Webpack.IgnorePlugin(
116 | RegExp(options.ignoredModuleNames.join("|"))
117 | )
118 | );
119 | }
120 |
121 | if (options.hasOwnProperty("minimizeFileSize") && options.minimizeFileSize) {
122 | settings.plugins.push(
123 | new Webpack.optimize.UglifyJsPlugin(
124 | {
125 | "output": {
126 | "inline_script": true
127 | }
128 | }
129 | )
130 | );
131 | }
132 |
133 | return settings;
134 | }
135 |
136 | module.exports = getSettings;
137 |
--------------------------------------------------------------------------------
/src/defaults/Scaffold.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react");
2 |
3 | var Scaffold = React.createClass(
4 | {
5 | "propTypes": {
6 | "title": React.PropTypes.string.isRequired,
7 |
8 | "style": React.PropTypes.shape(
9 | {
10 | "src": React.PropTypes.string,
11 | "__html": React.PropTypes.string,
12 | }
13 | ).isRequired,
14 |
15 | "script": React.PropTypes.shape(
16 | {
17 | "src": React.PropTypes.string,
18 | "__html": React.PropTypes.string,
19 | }
20 | ).isRequired,
21 |
22 | "body": React.PropTypes.shape(
23 | {
24 | "__html": React.PropTypes.string.isRequired,
25 | }
26 | ).isRequired,
27 |
28 | "favIconSrc": React.PropTypes.string,
29 | "storeStateByName": React.PropTypes.object,
30 | },
31 |
32 | "getDefaultProps": function () {
33 | return {
34 | "storeStateByName": {},
35 | }
36 | },
37 |
38 | "render": function () {
39 | // use Webpack's bundles for dev, but inline for production
40 | var styleTag = this.props.style.hasOwnProperty("src")
41 | ?
44 | : ;
47 |
48 | var scriptTag = this.props.script.hasOwnProperty("src")
49 | ?
53 | : ;
56 |
57 | return
58 |
59 |
60 | { this.props.title }
61 |
62 |
63 | {/* This is the magic viewport. Don't touch it!
64 | * It opts us in to Chrome's GPU accelerated fast-path:
65 | * https://www.youtube.com/watch?v=3Bq521dIjCM&feature=youtu.be&t=23m50s
66 | */}
67 |
76 |
77 |
81 |
82 | { styleTag }
83 |
84 |
85 |
88 |
89 |