Repository: kadirahq/react-mounter Branch: master Commit: 1101b3ea1dff Files: 19 Total size: 20.1 KB Directory structure: gitextract_56tdgamd/ ├── .babelrc ├── .eslintrc ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── lib/ │ ├── __tests__/ │ │ ├── client.js │ │ ├── index.js │ │ ├── server.js │ │ └── utils.js │ ├── client.js │ ├── index.js │ ├── server.js │ └── utils.js ├── package.json └── scripts/ ├── mocha_runner.js └── prepublish.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "presets": ["es2015", "stage-2", "react"], "plugins": ["react-require"] } ================================================ FILE: .eslintrc ================================================ { "parser": "babel-eslint", "plugins": [ "babel", "react" ], "env": { "es6": true, "node": true }, "ecmaFeatures": { "arrowFunctions": true, "binaryLiterals": true, "blockBindings": true, "classes": true, "defaultParams": true, "destructuring": true, "experimentalObjectRestSpread": true, "forOf": true, "generators": true, "globalReturn": true, "jsx": true, "modules": true, "objectLiteralComputedProperties": true, "objectLiteralDuplicateProperties": true, "objectLiteralShorthandMethods": true, "objectLiteralShorthandProperties": true, "octalLiterals": true, "regexUFlag": true, "regexYFlag": true, "restParams": true, "spread": true, "superInFunctions": true, "templateStrings": true, "unicodeCodePointEscapes": true }, "rules": { "babel/arrow-parens": [2, "as-needed"], "array-bracket-spacing": [2, "always"], "arrow-spacing": 2, "block-scoped-var": 0, "brace-style": [2, "1tbs", {"allowSingleLine": true}], "callback-return": 2, "camelcase": [2, {"properties": "always"}], "comma-dangle": 0, "comma-spacing": 0, "comma-style": [2, "last"], "complexity": 0, "computed-property-spacing": [2, "never"], "consistent-return": 0, "consistent-this": 0, "curly": [2, "all"], "default-case": 0, "dot-location": [2, "property"], "dot-notation": 0, "eol-last": 2, "eqeqeq": 2, "func-names": 0, "func-style": 0, "generator-star-spacing": [0, {"before": true, "after": false}], "guard-for-in": 2, "handle-callback-err": [2, "error"], "id-length": 0, "id-match": [2, "^(?:_?[a-zA-Z0-9]*)|[_A-Z0-9]+$"], "indent": [2, 2, {"SwitchCase": 1}], "init-declarations": 0, "key-spacing": [2, {"beforeColon": false, "afterColon": true}], "linebreak-style": 2, "lines-around-comment": 0, "max-depth": 0, "max-len": [2, 80, 4], "max-nested-callbacks": 0, "max-params": 0, "max-statements": 0, "new-cap": 0, "new-parens": 2, "newline-after-var": 0, "no-alert": 2, "no-array-constructor": 2, "no-bitwise": 0, "no-caller": 2, "no-catch-shadow": 0, "no-class-assign": 2, "no-cond-assign": 2, "no-console": 1, "no-const-assign": 2, "no-constant-condition": 2, "no-continue": 0, "no-control-regex": 0, "no-debugger": 1, "no-delete-var": 2, "no-div-regex": 2, "no-dupe-args": 2, "no-dupe-keys": 2, "no-duplicate-case": 2, "no-else-return": 2, "no-empty": 2, "no-empty-character-class": 2, "no-empty-label": 2, "no-eq-null": 0, "no-eval": 2, "no-ex-assign": 2, "no-extend-native": 2, "no-extra-bind": 2, "no-extra-boolean-cast": 2, "no-extra-parens": 0, "no-extra-semi": 2, "no-fallthrough": 2, "no-floating-decimal": 2, "no-func-assign": 2, "no-implicit-coercion": 2, "no-implied-eval": 2, "no-inline-comments": 0, "no-inner-declarations": [2, "functions"], "no-invalid-regexp": 2, "no-invalid-this": 0, "no-irregular-whitespace": 2, "no-iterator": 2, "no-label-var": 2, "no-labels": 0, "no-lone-blocks": 2, "no-lonely-if": 2, "no-loop-func": 0, "no-mixed-requires": [2, true], "no-mixed-spaces-and-tabs": 2, "no-multi-spaces": 2, "no-multi-str": 2, "no-multiple-empty-lines": 0, "no-native-reassign": 0, "no-negated-in-lhs": 2, "no-nested-ternary": 0, "no-new": 2, "no-new-func": 0, "no-new-object": 2, "no-new-require": 2, "no-new-wrappers": 2, "no-obj-calls": 2, "no-octal": 2, "no-octal-escape": 2, "no-param-reassign": 2, "no-path-concat": 2, "no-plusplus": 0, "no-process-env": 0, "no-process-exit": 0, "no-proto": 2, "no-redeclare": 2, "no-regex-spaces": 2, "no-restricted-modules": 0, "no-return-assign": 2, "no-script-url": 2, "no-self-compare": 0, "no-sequences": 2, "no-shadow": 2, "no-shadow-restricted-names": 2, "no-spaced-func": 2, "no-sparse-arrays": 2, "no-sync": 2, "no-ternary": 0, "no-this-before-super": 2, "no-throw-literal": 2, "no-trailing-spaces": 2, "no-undef": 2, "no-undef-init": 2, "no-undefined": 0, "no-underscore-dangle": 0, "no-unexpected-multiline": 2, "no-unneeded-ternary": 2, "no-unreachable": 2, "no-unused-expressions": 2, "no-unused-vars": [2, {"vars": "all", "args": "after-used"}], "no-use-before-define": 0, "no-useless-call": 2, "no-var": 0, "no-void": 2, "no-warning-comments": 0, "no-with": 2, "object-curly-spacing": [0, "always"], "object-shorthand": [2, "always"], "one-var": [2, "never"], "operator-assignment": [2, "always"], "operator-linebreak": [2, "after"], "padded-blocks": 0, "prefer-const": 0, "prefer-reflect": 0, "prefer-spread": 0, "quote-props": [2, "as-needed"], "quotes": [2, "single"], "radix": 2, "require-yield": 2, "semi": [2, "always"], "semi-spacing": [2, {"before": false, "after": true}], "sort-vars": 0, "space-after-keywords": [2, "always"], "space-before-blocks": [2, "always"], "space-before-function-paren": [2, {"anonymous": "always", "named": "never"}], "space-in-parens": 0, "space-infix-ops": [2, {"int32Hint": false}], "space-return-throw-case": 2, "space-unary-ops": [2, {"words": true, "nonwords": false}], "spaced-comment": [2, "always"], "strict": 0, "use-isnan": 2, "valid-jsdoc": 0, "valid-typeof": 2, "vars-on-top": 0, "wrap-iife": 2, "wrap-regex": 0, "yoda": [2, "never", {"exceptRange": true}] } } ================================================ FILE: .gitignore ================================================ *.swp *~ *.iml .*.haste_cache.* .DS_Store .idea npm-debug.log node_modules dist ================================================ FILE: .npmignore ================================================ *.swp *~ *.iml .*.haste_cache.* .DS_Store .idea .babelrc .eslintrc npm-debug.log lib ================================================ FILE: CHANGELOG.md ================================================ # Change Log ### v1.2.0 2015-April-09 * Add support for React v15.0.0 ### v1.1.0 2016-Feb-18 * Add SSR support with FlowRouter's SSR apis. ### v1.0.0 Initial Release ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2016 Kadira Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # React Mounter React Mounter lets you mount React components to DOM easily. > React Mounter supports Server Side Rendering when used with [FlowRouter](https://github.com/kadirahq/flow-router). Normally, when you are rendering a React Component to the DOM, you need to do following things basically, * Create a root DOM node as the root node for React * Wait for the DOM to load properly * Then render the component React Mounter does all these for you. You just ask it to render a component. Additionally, React Mounter can work as a simple Layout Manager where you can use with [Flow Router](https://github.com/kadirahq/flow-router). ## Basic Usage Install with: ``` npm i --save react-mounter react react-dom ``` > `react` and `react-dom` are peerDependencies of `react-mounter`. So, you need to install them into your app manually. Then let's mount a component. ```js import React from 'react'; import {mount} from 'react-mounter'; const WelcomeComponent = ({name}) => (

Hello, {name}

); mount(WelcomeComponent, {name: 'Arunoda'}); ``` ## Using as a Layout Manager You can user `react-mounter` as a layout Manager for Flow Router. Here's how to do it. Let's say we've a layout called MainLayout. ```js const MainLayout = ({content}) => (
This is our header
{content}
); ``` Now let's try render to our `WelcomeComponent` into the `MainLayout`. ```js mount(MainLayout, { content: }); ``` That's it. ### To use the React Context In order to use the React context, you need to render the `content` component inside the layout. So we need to pass a function instead of the React element. Here's how to do it. ```js const MainLayout = ({content}) => (
This is our header
{content()}
); ``` > See, now content is a function. Then, we can pass the Welcome component like this: ```js mount(MainLayout, { content: () => () }); ``` ## Configure Root DOM node By default React Mounter render our components into a DOM node called `react-root`. But, you can configure if by like below: ```js const {mount, withOptions} from `react-mounter`; const mount2 = withOptions({ rootId: 'the-root', rootProps: {'className': 'some-class-name'} }, mount); mount2(WelcomeComponent, {name: 'Arunoda'}); ``` ## Server Side Rendering (SSR) SSR is supported when used with [FlowRouter SSR](https://github.com/kadirahq/flow-router/tree/ssr). Checkout this [sample app](https://github.com/kadira-samples/meteor-data-and-react). ================================================ FILE: index.js ================================================ module.exports = require('./dist/index'); ================================================ FILE: lib/__tests__/client.js ================================================ import {describe, it} from 'mocha'; import {expect} from 'chai'; import mock from 'mock-require'; import {stub} from 'sinon'; import ReactDOM from 'react-dom/server'; mock('domready', cb => { setTimeout(cb, 100); }); let renderArgs; mock('react-dom', { render: (...args) => { renderArgs = args; } }); const { _ready, _getRootNode, mounter } = require('../client'); describe('client', () => { describe('_ready', () => { describe('dom is not loaded', () => { it('should wait and fire callback after ready', done => { const start = Date.now(); _ready(() => { const diff = Date.now() - start; expect(diff >= 110).to.be.equal(true); done(); }); }); }); describe('dom is already loaded', () => { it('should fire callback immediately', () => { let cnt = 0; _ready(() => { cnt = 100; }); expect(cnt).to.be.equal(100); }); }); }); describe('_getRootNode', () => { describe('root node is already in the dom', () => { it('should just return it', () => { const node = {aa: 10}; global.document = {getElementById: stub()}; global.document.getElementById.returns(node); const returnedNode = _getRootNode('the-id'); expect(returnedNode).to.be.equal(node); const args = global.document.getElementById.args[0]; expect(args).to.be.deep.equal([ 'the-id' ]); }); }); describe('root node is not in the dom', () => { it('should create and return the dom node', () => { const node = {aa: 10}; const doc = { getElementsByTagName: stub(), getElementById: stub() }; const body = { insertAdjacentHTML: stub() }; doc.getElementsByTagName.returns([ body ]); doc.getElementById .onFirstCall().returns(null) .onSecondCall().returns(node); global.document = doc; const returnedNode = _getRootNode('the-id'); expect(returnedNode).to.be.equal(node); expect(body.insertAdjacentHTML.args[0]).to.be.deep.equal( [ 'beforeend', '
' ] ); }); }); }); describe('mounter', () => { it('should render the given layout and regions to the DOM', () => { const node = {aa: 10}; global.document = {getElementById: stub()}; global.document.getElementById.returns(node); const Layout = ({c}) => c; const Item = () => (

Hello

); mounter(Layout, {c: }, {rootId: 'amazing'}); const html = ReactDOM.renderToString(renderArgs[0]); expect(html).to.match(/Hello/); const rootId = global.document.getElementById.args[0][0]; expect(rootId).to.be.equal('amazing'); expect(renderArgs[1]).to.be.equal(node); }); }); }); ================================================ FILE: lib/__tests__/index.js ================================================ import {describe, it} from 'mocha'; import {expect} from 'chai'; import mock from 'mock-require'; mock('domready', cb => { setTimeout(cb, 100); }); mock('react-dom', { render: () => {} }); const { withOptions } = require('../'); describe('index', () => { describe('withOptions', () => { it('should call the given function with options', done => { const fn = (a, options) => { expect(a).to.be.equal('abc'); expect(options).to.deep.equal({aa: 10}); done(); }; withOptions({aa: 10}, fn)('abc'); }); }); }); ================================================ FILE: lib/__tests__/server.js ================================================ import { describe, it } from 'mocha'; import { expect } from 'chai'; import { mounter } from '../server'; describe('server', () => { describe('mounter', () => { describe('with FlowRouter', () => { it('should set push rendered HTML string to FlowRouter', done => { withFlowRouter(({getSSRedHTML}) => { const Layout = ({c}) => c; const Item = () => (

Hello

); mounter(Layout, {c: }, {rootId: 'amazing'}); expect(getSSRedHTML()).to.match(/Hello/); setTimeout(done, 0); }); }); }); describe('without FlowRouter', () => { it('should throw an Error', () => { const run = () => { const Layout = ({c}) => c; const Item = () => (

Hello

); mounter(Layout, {c: }, {rootId: 'amazing'}); }; global.Package = {}; expect(run).to.throw(/FlowRouter SSR is required/); delete global.Package; }); }); describe('outside of Meteor', () => { it('should throw an Error', () => { const run = () => { const Layout = ({c}) => c; const Item = () => (

Hello

); mounter(Layout, {c: }, {rootId: 'amazing'}); }; expect(run).to.throw(/only available with Meteor/); }); }); }); }); function withFlowRouter(run) { let SSRedHTML = null; const Package = { 'kadira:flow-router-ssr': { FlowRouter: { ssrContext: { get() { return { setHtml(html) { SSRedHTML = html; } }; } } } } }; const context = { getSSRedHTML() { return SSRedHTML; } }; global.Package = Package; run(context); delete global.Package; } ================================================ FILE: lib/__tests__/utils.js ================================================ import {describe, it} from 'mocha'; import {expect} from 'chai'; import { buildRootNode } from '../utils'; describe('utils', () => { describe('_buildRootNode', () => { it('should build the rootNode with given id and props', () => { const html = buildRootNode('the-id', {aa: 10}); expect(html).to.be.equal('
'); }); it('should change className prop to class attribute', () => { const html = buildRootNode('the-id', {aa: 10, className: 'abc'}); expect(html).to.be.equal('
'); }); }); }); ================================================ FILE: lib/client.js ================================================ /* global document*/ import React from 'react'; import ReactDOM from 'react-dom'; import domready from 'domready'; import { buildRootNode } from './utils'; export let _isDomReady = false; export function _ready(cb) { if (_isDomReady) { return cb(); } domready(() => { _isDomReady = true; setTimeout(cb, 10); }); } export function _getRootNode(rootId, rootProps) { const rootNode = document.getElementById(rootId); if (rootNode) { return rootNode; } const rootNodeHtml = buildRootNode(rootId, rootProps); const body = document.getElementsByTagName('body')[0]; body.insertAdjacentHTML('beforeend', rootNodeHtml); return document.getElementById(rootId); } export function mounter(layoutClass, regions, options) { _ready(() => { const {rootId, rootProps} = options; const rootNode = _getRootNode(rootId, rootProps); const el = React.createElement(layoutClass, regions); ReactDOM.render(el, rootNode); }); } ================================================ FILE: lib/index.js ================================================ let mounter = null; if (typeof window !== 'undefined') { // now we are in the server mounter = require('./client').mounter; } else { mounter = require('./server').mounter; } export function mount(layoutClass, regions, options = {}) { options.rootId = options.rootId || 'react-root'; options.rootProps = options.rootProps || {}; mounter(layoutClass, regions, options); } export function withOptions(options, fn) { return function (...args) { const newArgs = [ ...args, options ]; return fn(...newArgs); }; } ================================================ FILE: lib/server.js ================================================ /* global Package */ import React from 'react'; import ReactDOMServer from 'react-dom/server'; import { buildRootNode } from './utils'; export function mounter(layoutClass, regions, options) { const el = React.createElement(layoutClass, regions); const elHtml = ReactDOMServer.renderToString(el); const {rootId, rootProps} = options; var rootNodeHtml = buildRootNode(rootId, rootProps); var html = rootNodeHtml.replace('', elHtml + ''); if (typeof Package === 'undefined') { const error = 'Server side mounting in only available with Meteor.'; throw new Error(error); } if (!Package['kadira:flow-router-ssr']) { const error = 'FlowRouter SSR is required to mount components in the server.'; throw new Error(error); } var FlowRouter = Package['kadira:flow-router-ssr'].FlowRouter; var ssrContext = FlowRouter.ssrContext.get(); ssrContext.setHtml(html); } ================================================ FILE: lib/utils.js ================================================ export function buildRootNode(rootId, rootProps) { const props = {...rootProps}; props.id = rootId; if (props.className) { props.class = props.className; delete props.className; } let propsString = ''; for (const key in props) { if (!(props.hasOwnProperty(key))) { continue; } const value = props[key]; propsString += ` ${key}="${value}"`; } return ``; } ================================================ FILE: package.json ================================================ { "name": "react-mounter", "version": "1.2.0", "description": "React Mounter lets you mount React components to DOM easily.", "repository": { "type": "git", "url": "https://github.com/kadirahq/react-mounter.git" }, "license": "MIT", "options": { "mocha": "--require scripts/mocha_runner lib/**/__tests__/**/*.js" }, "scripts": { "prepublish": ". ./scripts/prepublish.sh", "lint": "eslint ./lib", "lintfix": "eslint ./lib --fix", "testonly": "mocha $npm_package_options_mocha", "test": "npm run lint && npm run testonly" }, "devDependencies": { "nodemon": "1.7.x", "mocha": "2.x.x", "chai": "3.x.x", "eslint": "1.7.x", "babel-eslint": "4.x.x", "eslint-plugin-babel": "2.x.x", "babel-cli": "6.x.x", "babel-core": "6.x.x", "babel-polyfill": "6.x.x", "babel-preset-es2015": "6.x.x", "babel-preset-stage-2": "6.x.x", "babel-plugin-transform-runtime": "6.x.x", "babel-preset-react": "6.x.x", "babel-plugin-react-require": "2.x.x", "react": "^15.0.0", "react-dom": "^15.0.0", "mock-require": "1.x.x", "sinon": "1.17.x", "eslint-plugin-react": "3.x.x" }, "peerDependencies": { "react": "0.14.x || 15.x.x", "react-dom": "0.14.x || 15.x.x" }, "dependencies": { "babel-runtime": "6.x.x", "domready": "1.x.x" } } ================================================ FILE: scripts/mocha_runner.js ================================================ require('babel-core/register'); require('babel-polyfill'); process.on('unhandledRejection', function (error) { console.error('Unhandled Promise Rejection:'); console.error(error && error.stack || error); }); ================================================ FILE: scripts/prepublish.sh ================================================ echo "> Start transpiling ES2015" echo "" ./node_modules/.bin/babel --plugins "transform-runtime" lib --ignore __tests__ --out-dir ./dist echo "" echo "> Complete transpiling ES2015"