├── fixtures └── packaging │ ├── rjs │ ├── .gitignore │ ├── index.html │ ├── input.js │ ├── package.json │ └── config.js │ ├── webpack │ ├── .gitignore │ ├── index.html │ ├── input.js │ ├── package.json │ └── config.js │ ├── systemjs-builder │ ├── .gitignore │ ├── index.html │ ├── config.js │ ├── input.js │ ├── package.json │ └── build.js │ ├── webpack-alias │ ├── .gitignore │ ├── index.html │ ├── input.js │ ├── package.json │ └── config.js │ ├── brunch │ ├── .gitignore │ ├── index.html │ ├── config.js │ ├── input.js │ ├── app │ │ └── initialize.js │ └── package.json │ ├── browserify │ ├── .gitignore │ ├── index.html │ ├── package.json │ └── input.js │ ├── globals.html │ ├── requirejs.html │ ├── systemjs.html │ ├── build-all.js │ └── README.md ├── packages ├── inferno-router │ ├── lib │ │ ├── Link.js │ │ ├── Route.js │ │ ├── match.js │ │ ├── IndexLink.js │ │ ├── Redirect.js │ │ ├── IndexRedirect.js │ │ ├── IndexRoute.js │ │ ├── RouterContext.js │ │ └── Router.js │ ├── index.js │ ├── src │ │ ├── IndexRoute.ts │ │ ├── Redirect.ts │ │ ├── IndexLink.ts │ │ ├── RouterContext.ts │ │ ├── index.ts │ │ ├── Link.ts │ │ ├── Router.ts │ │ ├── Route.ts │ │ ├── createRoutes.ts │ │ └── utils.ts │ ├── __tests__ │ │ ├── utils.spec.jsx │ │ └── createRoutes.spec.jsx │ └── package.json ├── inferno-devtools │ ├── src │ │ ├── index.ts │ │ └── init.ts │ ├── index.js │ ├── package.json │ └── README.md ├── inferno-compat │ ├── lib │ │ ├── ReactTransitionGroup.js │ │ ├── ReactCSSTransitionGroup.js │ │ ├── ReactMount.js │ │ ├── EventPropagators.js │ │ ├── SyntheticUIEvent.js │ │ ├── EventPluginHub.js │ │ ├── ViewportMetrics.js │ │ ├── shallowCompare.js │ │ ├── EventPluginUtils.js │ │ ├── ReactFragment.js │ │ ├── ReactTransitionEvents.js │ │ └── EventConstants.js │ ├── index.js │ ├── prop-types.js │ ├── src │ │ ├── isValidElement.ts │ │ └── PropTypes.ts │ ├── package.json │ ├── __tests__ │ │ └── svg.spec.jsx │ └── README.md ├── inferno │ ├── src │ │ ├── core │ │ │ ├── structures.ts │ │ │ └── options.ts │ │ ├── DOM │ │ │ ├── events │ │ │ │ └── linkEvent.ts │ │ │ ├── wrappers │ │ │ │ ├── processElement.ts │ │ │ │ ├── TextareaWrapper.ts │ │ │ │ └── SelectWrapper.ts │ │ │ ├── constants.ts │ │ │ └── recycling.ts │ │ ├── index.ts │ │ └── test │ │ │ └── utils.ts │ ├── index.js │ ├── __benchmarks__ │ │ └── rendering.js │ ├── package.json │ └── __tests__ │ │ ├── rendering.spec.js │ │ └── patching.spec.js ├── inferno-shared │ ├── index.js │ ├── package.json │ └── src │ │ └── index.ts ├── inferno-mobx │ ├── index.js │ ├── __tests__ │ │ ├── tracking.spec.js │ │ └── eventemitter.spec.js │ ├── src │ │ ├── index.ts │ │ ├── EventEmitter.ts │ │ ├── connect.ts │ │ ├── Provider.ts │ │ └── inject.ts │ ├── package.json │ └── README.md ├── inferno-component │ ├── index.js │ ├── README.md │ ├── package.json │ └── __tests__ │ │ └── state.spec.jsx ├── inferno-redux │ ├── index.js │ ├── src │ │ ├── index.ts │ │ ├── helpers.ts │ │ ├── Provider.ts │ │ └── utils.ts │ ├── package.json │ ├── __tests__ │ │ └── utils.spec.jsx │ └── README.md ├── inferno-server │ ├── index.js │ ├── README.md │ ├── src │ │ ├── index.ts │ │ ├── utils.ts │ │ └── prop-renderers.ts │ ├── package.json │ └── __tests__ │ │ └── creation-stream.spec.js ├── inferno-create-class │ ├── index.js │ ├── README.md │ └── package.json ├── inferno-create-element │ ├── index.js │ ├── __tests__ │ │ ├── data │ │ │ └── common-render │ │ │ │ ├── parentfirstcommon.jsx │ │ │ │ ├── parentsecondcommon.jsx │ │ │ │ ├── parentbase.jsx │ │ │ │ └── child.jsx │ │ ├── clonenode.spec.jsx │ │ ├── normalizeProps.spec.jsx │ │ ├── findDOMNodes.spec.jsx │ │ ├── functional.spec.jsx │ │ └── svgXlink.spec.jsx │ ├── readme.md │ ├── package.json │ ├── __benchmarks__ │ │ ├── functionalcomponents.js │ │ └── classcomponents.js │ └── src │ │ └── index.ts ├── inferno-hyperscript │ ├── index.js │ ├── package.json │ └── README.md ├── inferno-test-utils │ ├── index.js │ ├── __tests__ │ │ └── testutilevents.spec.jsx │ ├── package.json │ └── src │ │ └── jest.ts └── inferno-vnode-flags │ ├── index.js │ ├── src │ └── index.ts │ ├── package.json │ └── README.md ├── .eslintignore ├── .npmignore ├── benchmarks ├── README.md ├── dbmonster │ ├── index.html │ ├── style.css │ └── app.js ├── uibench │ └── index.html ├── uibench-nonkeyed │ └── index.html ├── async-render │ ├── index.html │ └── app.js └── vdom-bench │ └── index.html ├── test ├── mocha.opts ├── data │ └── separate-render │ │ ├── parentfirstseparate.tsx │ │ ├── parentsecondseparate.tsx │ │ └── childseparate.tsx ├── lint │ └── letter-only-props.js ├── karma │ ├── karma.bench.conf.js │ ├── sauce.js │ ├── karma.unit.conf.js │ ├── bench.js │ └── karma.base.conf.js └── mocha │ └── DOM.js ├── .gitattributes ├── .editorconfig ├── examples ├── playground │ ├── index.html │ └── playground.js ├── routing │ ├── index.html │ └── routing.js ├── attributes │ └── index.html └── devtools │ └── index.html ├── browser └── jsfiddle-integration-babel.js ├── lerna.json ├── tsconfig.json ├── .gitignore ├── .github ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE.md ├── .travis.yml ├── tslint.json ├── bin └── link ├── LICENSE.md ├── .babelrc └── config ├── index.html └── webpack.dev.conf.js /fixtures/packaging/rjs/.gitignore: -------------------------------------------------------------------------------- 1 | output.js 2 | node_modules 3 | -------------------------------------------------------------------------------- /fixtures/packaging/webpack/.gitignore: -------------------------------------------------------------------------------- 1 | output.js 2 | node_modules 3 | -------------------------------------------------------------------------------- /fixtures/packaging/systemjs-builder/.gitignore: -------------------------------------------------------------------------------- 1 | output.js 2 | node_modules 3 | -------------------------------------------------------------------------------- /fixtures/packaging/webpack-alias/.gitignore: -------------------------------------------------------------------------------- 1 | output.js 2 | node_modules 3 | -------------------------------------------------------------------------------- /packages/inferno-router/lib/Link.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../').Link; 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*.min.js 2 | packages/inferno/dist/* 3 | examples/**/dist/* 4 | -------------------------------------------------------------------------------- /packages/inferno-router/lib/Route.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../').Route; 2 | -------------------------------------------------------------------------------- /packages/inferno-router/lib/match.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../').match; 2 | -------------------------------------------------------------------------------- /fixtures/packaging/brunch/.gitignore: -------------------------------------------------------------------------------- 1 | output.js 2 | output.js.map 3 | node_modules 4 | -------------------------------------------------------------------------------- /packages/inferno-router/lib/IndexLink.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../').IndexLink; 2 | -------------------------------------------------------------------------------- /packages/inferno-router/lib/Redirect.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../').Redirect; 2 | -------------------------------------------------------------------------------- /fixtures/packaging/browserify/.gitignore: -------------------------------------------------------------------------------- 1 | output.js 2 | node_modules 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /packages/inferno-router/lib/IndexRedirect.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../').Redirect; 2 | -------------------------------------------------------------------------------- /packages/inferno-router/lib/IndexRoute.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../').IndexRoute; 2 | -------------------------------------------------------------------------------- /packages/inferno-router/lib/RouterContext.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../').RouterContext; 2 | -------------------------------------------------------------------------------- /packages/inferno-devtools/src/index.ts: -------------------------------------------------------------------------------- 1 | import initDevTools from './init'; 2 | 3 | initDevTools(); 4 | -------------------------------------------------------------------------------- /packages/inferno-compat/lib/ReactTransitionGroup.js: -------------------------------------------------------------------------------- 1 | module.exports = require('inferno-transition-group'); -------------------------------------------------------------------------------- /packages/inferno/src/core/structures.ts: -------------------------------------------------------------------------------- 1 | export interface Styles { 2 | [key: string]: number | string; 3 | } 4 | -------------------------------------------------------------------------------- /packages/inferno-compat/lib/ReactCSSTransitionGroup.js: -------------------------------------------------------------------------------- 1 | module.exports = require('rc-css-transition-group-modern'); 2 | -------------------------------------------------------------------------------- /packages/inferno-compat/lib/ReactMount.js: -------------------------------------------------------------------------------- 1 | exports.unmountComponentAtNode = require('inferno-compat').unmountComponentAtNode; -------------------------------------------------------------------------------- /packages/inferno-shared/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist'); 2 | module.exports.default = module.exports; 3 | 4 | -------------------------------------------------------------------------------- /packages/inferno/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist').default; 2 | module.exports.default = module.exports; 3 | 4 | -------------------------------------------------------------------------------- /packages/inferno-compat/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist').default; 2 | module.exports.default = module.exports; 3 | -------------------------------------------------------------------------------- /packages/inferno-devtools/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist'); 2 | module.exports.default = module.exports; 3 | 4 | -------------------------------------------------------------------------------- /packages/inferno-mobx/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist').default; 2 | module.exports.default = module.exports; 3 | 4 | -------------------------------------------------------------------------------- /packages/inferno-component/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist').default; 2 | module.exports.default = module.exports; 3 | 4 | -------------------------------------------------------------------------------- /packages/inferno-redux/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist').default; 2 | module.exports.default = module.exports; 3 | 4 | -------------------------------------------------------------------------------- /packages/inferno-router/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist').default; 2 | module.exports.default = module.exports; 3 | 4 | -------------------------------------------------------------------------------- /packages/inferno-server/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist').default; 2 | module.exports.default = module.exports; 3 | 4 | -------------------------------------------------------------------------------- /packages/inferno-create-class/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist').default; 2 | module.exports.default = module.exports; 3 | 4 | -------------------------------------------------------------------------------- /packages/inferno-create-element/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist').default; 2 | module.exports.default = module.exports; 3 | 4 | -------------------------------------------------------------------------------- /packages/inferno-hyperscript/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist').default; 2 | module.exports.default = module.exports; 3 | 4 | -------------------------------------------------------------------------------- /packages/inferno-test-utils/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist').default; 2 | module.exports.default = module.exports; 3 | 4 | -------------------------------------------------------------------------------- /packages/inferno-vnode-flags/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist').default; 2 | module.exports.default = module.exports; 3 | 4 | -------------------------------------------------------------------------------- /packages/inferno-compat/prop-types.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist/PropTypes').default; 2 | module.exports.default = module.exports; 3 | -------------------------------------------------------------------------------- /fixtures/packaging/webpack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | -------------------------------------------------------------------------------- /fixtures/packaging/systemjs-builder/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | -------------------------------------------------------------------------------- /fixtures/packaging/webpack-alias/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/inferno-router/lib/Router.js: -------------------------------------------------------------------------------- 1 | const InfernoRouter = require('../'); 2 | 3 | module.exports = InfernoRouter.Router; 4 | module.exports.getRoutes = InfernoRouter.IndexRoute; 5 | -------------------------------------------------------------------------------- /fixtures/packaging/systemjs-builder/config.js: -------------------------------------------------------------------------------- 1 | System.config({ 2 | paths: { 3 | react: '../../../build/react.js', 4 | 'react-dom': '../../../build/react-dom.js', 5 | }, 6 | }); 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .* 2 | *.log 3 | *.swp 4 | *.yml 5 | bower.json 6 | coverage 7 | config 8 | dist/*.map 9 | test 10 | __tests__ 11 | 12 | tsconfig.json 13 | tsconfig.*.json 14 | npm-debug.log* 15 | -------------------------------------------------------------------------------- /benchmarks/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | | Various examples to show the power of Inferno 3 | 4 | ## How to use? 5 | 6 | Run Http-server at the root of Repository and Open up the .html file in your browser. 7 | -------------------------------------------------------------------------------- /fixtures/packaging/brunch/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /fixtures/packaging/brunch/config.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | paths: { 3 | public: '.', 4 | }, 5 | files: { 6 | javascripts: { 7 | joinTo: 'output.js', 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/inferno-compat/lib/EventPropagators.js: -------------------------------------------------------------------------------- 1 | // so generates no-ops for now, we probably need to make this actually work? 2 | module.exports = { 3 | accumulateTwoPhaseDispatches: function() { 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /packages/inferno-compat/lib/SyntheticUIEvent.js: -------------------------------------------------------------------------------- 1 | // so generates no-ops for now, we probably need to make this actually work? 2 | module.exports = { 3 | SyntheticUIEvent: function() { 4 | return {}; 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --compilers js:babel-register 2 | --require ./test/mocha/DOM.js 3 | --full-trace 4 | --reporter spec 5 | --ui bdd 6 | --recursive 7 | --timeout 15000 8 | 9 | packages/*/__tests__/**/*.js* 10 | -------------------------------------------------------------------------------- /fixtures/packaging/browserify/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /fixtures/packaging/rjs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /fixtures/packaging/rjs/input.js: -------------------------------------------------------------------------------- 1 | require(['react', 'react-dom'], function(React, ReactDOM) { 2 | ReactDOM.render( 3 | React.createElement('h1', null, 'Hello World!'), 4 | document.getElementById('container') 5 | ); 6 | }); 7 | -------------------------------------------------------------------------------- /packages/inferno-compat/lib/EventPluginHub.js: -------------------------------------------------------------------------------- 1 | // so generates no-ops for now, we probably need to make this actually work? 2 | module.exports = { 3 | injection: { 4 | injectEventPluginsByName: function() { 5 | } 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /packages/inferno-redux/src/index.ts: -------------------------------------------------------------------------------- 1 | import connect from './connect'; 2 | import Provider from './Provider'; 3 | 4 | export default { 5 | Provider, 6 | connect 7 | }; 8 | 9 | export { 10 | Provider, 11 | connect 12 | }; 13 | -------------------------------------------------------------------------------- /fixtures/packaging/brunch/input.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ReactDOM = require('react-dom'); 3 | 4 | ReactDOM.render( 5 | React.createElement('h1', null, 'Hello World!'), 6 | document.getElementById('container') 7 | ); 8 | -------------------------------------------------------------------------------- /fixtures/packaging/webpack/input.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ReactDOM = require('react-dom'); 3 | 4 | ReactDOM.render( 5 | React.createElement('h1', null, 'Hello World!'), 6 | document.getElementById('container') 7 | ); 8 | -------------------------------------------------------------------------------- /fixtures/packaging/systemjs-builder/input.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | ReactDOM.render( 5 | React.createElement('h1', null, 'Hello World!'), 6 | document.getElementById('container') 7 | ); 8 | -------------------------------------------------------------------------------- /fixtures/packaging/webpack-alias/input.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ReactDOM = require('react-dom'); 3 | 4 | ReactDOM.render( 5 | React.createElement('h1', null, 'Hello World!'), 6 | document.getElementById('container') 7 | ); 8 | -------------------------------------------------------------------------------- /packages/inferno-router/src/IndexRoute.ts: -------------------------------------------------------------------------------- 1 | import Route from './Route'; 2 | 3 | export default class IndexRoute extends Route { 4 | constructor(props?: any, context?: any) { 5 | super(props, context); 6 | props.path = '/'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /fixtures/packaging/brunch/app/initialize.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ReactDOM = require('react-dom'); 3 | 4 | ReactDOM.render( 5 | React.createElement('h1', null, 'Hello World!'), 6 | document.getElementById('container') 7 | ); 8 | -------------------------------------------------------------------------------- /fixtures/packaging/rjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rjs-test", 3 | "private": true, 4 | "dependencies": { 5 | "requirejs": "^2.3.2" 6 | }, 7 | "scripts": { 8 | "build": "rm -f output.js && r.js -o config.js" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /fixtures/packaging/webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-test", 3 | "private": true, 4 | "dependencies": { 5 | "webpack": "^1.14.0" 6 | }, 7 | "scripts": { 8 | "build": "rm -f output.js && webpack --config config.js" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /fixtures/packaging/rjs/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | baseUrl: '.', 3 | name: 'input', 4 | out: 'output.js', 5 | optimize: 'none', 6 | paths: { 7 | react: '../../../build/react', 8 | 'react-dom': '../../../build/react-dom', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /fixtures/packaging/webpack-alias/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-test", 3 | "private": true, 4 | "dependencies": { 5 | "webpack": "^1.14.0" 6 | }, 7 | "scripts": { 8 | "build": "rm -f output.js && webpack --config config.js" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /fixtures/packaging/webpack/config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | module.exports = { 4 | entry: './input', 5 | output: { 6 | filename: 'output.js', 7 | }, 8 | resolve: { 9 | root: path.resolve('../../../build/packages/'), 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /packages/inferno-router/src/Redirect.ts: -------------------------------------------------------------------------------- 1 | import Route from './Route'; 2 | 3 | export default class Redirect extends Route { 4 | constructor(props?: any, context?: any) { 5 | super(props, context); 6 | if (!props.to) { 7 | props.to = '/'; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /fixtures/packaging/systemjs-builder/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "systemjs-builder-test", 3 | "private": true, 4 | "dependencies": { 5 | "systemjs-builder": "^0.15.34" 6 | }, 7 | "scripts": { 8 | "build": "rm -f output.js && node build.js" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/inferno-create-element/__tests__/data/common-render/parentfirstcommon.jsx: -------------------------------------------------------------------------------- 1 | import { ParentBaseCommon } from './parentbase'; 2 | 3 | export class ParentFirstCommon extends ParentBaseCommon { 4 | constructor(props) { 5 | super(props); 6 | 7 | this.foo = 'First'; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/inferno-create-element/__tests__/data/common-render/parentsecondcommon.jsx: -------------------------------------------------------------------------------- 1 | import { ParentBaseCommon } from './parentbase'; 2 | 3 | export class ParentSecondCommon extends ParentBaseCommon { 4 | constructor(props) { 5 | super(props); 6 | 7 | this.foo = 'Second'; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.css text eol=lf 4 | *.js text eol=lf 5 | *.jsx text eol=lf 6 | *.ts text eol=lf linguist-language=JavaScript 7 | *.tsx text eol=lf linguist-language=JavaScript 8 | *.json text eol=lf 9 | *.html text eol=lf 10 | *.md text eol=lf 11 | .gitattributes text eol=lf 12 | -------------------------------------------------------------------------------- /packages/inferno-router/src/IndexLink.ts: -------------------------------------------------------------------------------- 1 | import { createVNode, VNode } from 'inferno'; 2 | import VNodeFlags from 'inferno-vnode-flags'; 3 | import Link from './Link'; 4 | 5 | export default function IndexLink(props): VNode { 6 | props.to = '/'; 7 | return createVNode(VNodeFlags.ComponentFunction, Link, null, null, props); 8 | } 9 | -------------------------------------------------------------------------------- /packages/inferno-create-element/__tests__/data/common-render/parentbase.jsx: -------------------------------------------------------------------------------- 1 | import Component from 'inferno-component'; 2 | import { ChildCommon } from './child'; 3 | 4 | export class ParentBaseCommon extends Component { 5 | render() { 6 | return ( 7 |
8 | 9 |
10 | ); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | end_of_line = lf 6 | insert_final_newline = true 7 | quote_type = single 8 | 9 | # Tab indentation 10 | [*.{js,ts,jsx,tsx,json}] 11 | indent_style = tab 12 | indent_size = 2 13 | 14 | # Space indentation 15 | [package.json] 16 | indent_style = space 17 | indent_size = 2 18 | -------------------------------------------------------------------------------- /fixtures/packaging/brunch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "brunch-test", 3 | "devDependencies": { 4 | "brunch": "^2.9.1", 5 | "javascript-brunch": "^2.0.0" 6 | }, 7 | "scripts": { 8 | "build": "rm -rf public && ln -fs ../../../../build/packages/react node_modules/react && ln -fs ../../../../build/packages/react-dom node_modules/react-dom && brunch build" 9 | } 10 | } -------------------------------------------------------------------------------- /fixtures/packaging/systemjs-builder/build.js: -------------------------------------------------------------------------------- 1 | var Builder = require('systemjs-builder'); 2 | 3 | var builder = new Builder('/', './config.js'); 4 | builder 5 | .buildStatic('./input.js', './output.js') 6 | .then(function() { 7 | console.log('Build complete'); 8 | }) 9 | .catch(function(err) { 10 | console.log('Build error'); 11 | console.log(err); 12 | }); 13 | -------------------------------------------------------------------------------- /fixtures/packaging/webpack-alias/config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | module.exports = { 4 | entry: './input', 5 | output: { 6 | filename: 'output.js', 7 | }, 8 | resolve: { 9 | root: path.resolve('../../../build/packages'), 10 | alias: { 11 | 'react': 'react/dist/react', 12 | 'react-dom': 'react-dom/dist/react-dom', 13 | }, 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /packages/inferno-compat/lib/ViewportMetrics.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ViewportMetrics = { 4 | 5 | currentScrollLeft: 0, 6 | 7 | currentScrollTop: 0, 8 | 9 | refreshScrollValues: function(scrollPosition) { 10 | ViewportMetrics.currentScrollLeft = scrollPosition.x; 11 | ViewportMetrics.currentScrollTop = scrollPosition.y; 12 | }, 13 | 14 | }; 15 | 16 | module.exports = ViewportMetrics; 17 | -------------------------------------------------------------------------------- /fixtures/packaging/globals.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 12 | 13 | -------------------------------------------------------------------------------- /packages/inferno-compat/lib/shallowCompare.js: -------------------------------------------------------------------------------- 1 | function shallowDiffers(a, b) { 2 | for (var i in a) if (!(i in b)) return true 3 | for (var i in b) if (a[ i ] !== b[ i ]) return true 4 | return false 5 | } 6 | 7 | module.exports = function(instance, nextProps, nextState) { 8 | return ( 9 | shallowDiffers(instance.props, nextProps) || 10 | shallowDiffers(instance.state, nextState) 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /examples/playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Inferno example 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/data/separate-render/parentfirstseparate.tsx: -------------------------------------------------------------------------------- 1 | import Component from 'inferno-component'; 2 | import { ChildSeparate } from './childseparate'; 3 | 4 | export class ParentFirstSeparate extends Component { 5 | foo: string; 6 | constructor(props) { 7 | super(props); 8 | 9 | this.foo = 'First'; 10 | } 11 | 12 | render() { 13 | return ( 14 |
15 | 16 |
17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/data/separate-render/parentsecondseparate.tsx: -------------------------------------------------------------------------------- 1 | import Component from 'inferno-component'; 2 | import { ChildSeparate } from './childseparate'; 3 | 4 | export class ParentSecondSeparate extends Component { 5 | foo: string; 6 | constructor(props) { 7 | super(props); 8 | 9 | this.foo = 'Second'; 10 | } 11 | 12 | render() { 13 | return ( 14 |
15 | 16 |
17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /browser/jsfiddle-integration-babel.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var tag = document.querySelector( 3 | 'script[type="application/javascript;version=1.7"]' 4 | ); 5 | if (!tag || tag.textContent.indexOf('window.onload=function(){') !== -1) { 6 | alert('Bad JSFiddle configuration, please fork the original Inferno JSFiddle'); 7 | } 8 | tag.setAttribute('type', 'text/babel'); 9 | tag.textContent = tag.textContent.replace(/^\/\/ Inferno package with helpers to create Inferno elements 3 | 4 | ## Install 5 | 6 | ``` 7 | npm install inferno-create-element 8 | ``` 9 | 10 | ## Usage 11 | 12 | ```js 13 | import createElement from 'inferno-create-element'; 14 | import Inferno from 'inferno'; 15 | 16 | Inferno.render( 17 | createElement('div', { className: 'test' }, "I'm a child!"), 18 | document.getElementById("app") 19 | ); 20 | ``` 21 | -------------------------------------------------------------------------------- /packages/inferno-component/README.md: -------------------------------------------------------------------------------- 1 | # inferno-component 2 | > Inferno package for working with ES2015 stateful components 3 | 4 | ## Install 5 | 6 | ``` 7 | npm install inferno-component 8 | ``` 9 | 10 | ## Contents 11 | 12 | * Component 13 | 14 | ## Usage 15 | 16 | ```js 17 | import Inferno from 'inferno'; 18 | import Component from 'inferno-component'; 19 | 20 | class MyComponent extends Component { 21 | render() { 22 | return
; 23 | } 24 | } 25 | ``` 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /packages/inferno-compat/src/isValidElement.ts: -------------------------------------------------------------------------------- 1 | import { VNode } from 'inferno'; 2 | import { isNull, isObject } from 'inferno-shared'; 3 | import VNodeFlags from 'inferno-vnode-flags'; 4 | 5 | export default function isValidElement(obj: VNode): boolean { 6 | const isNotANullObject = isObject(obj) && isNull(obj) === false; 7 | if (isNotANullObject === false) { 8 | return false; 9 | } 10 | const flags = obj.flags; 11 | 12 | return (flags & (VNodeFlags.Component | VNodeFlags.Element)) > 0; 13 | } 14 | -------------------------------------------------------------------------------- /benchmarks/dbmonster/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Inferno example 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.2.1", 3 | "compilerOptions": { 4 | "target": "es6", 5 | "module": "es6", 6 | "allowJs": false, 7 | "moduleResolution": "node", 8 | "preserveConstEnums": true, 9 | "declaration": true, 10 | "outDir": "dist-es", 11 | "sourceMap": false, 12 | "lib": [ 13 | "es6", 14 | "es7", 15 | "dom" 16 | ], 17 | "jsx": "preserve", 18 | "noUnusedLocals": true, 19 | "strictNullChecks": true, 20 | "removeComments": false 21 | }, 22 | "include": [ 23 | "src/**/*" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /benchmarks/uibench/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | UI Benchmark: Inferno 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /benchmarks/uibench-nonkeyed/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | UI Benchmark: Inferno 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .history 3 | .git 4 | build 5 | .build 6 | .git 7 | .history 8 | .idea 9 | .jshintrc 10 | .nyc_output 11 | .sass-cache 12 | .vscode 13 | build 14 | coverage 15 | jsconfig.json 16 | Gemfile.lock 17 | node_modules 18 | .DS_Store 19 | *.map 20 | *.log 21 | *.log* 22 | *.swp 23 | *~ 24 | test/data/result.json 25 | 26 | packages/*/tsconfig.* 27 | packages/*/.npmignore 28 | packages/*/dist-es 29 | packages/*/dist 30 | test/data/baseline.json 31 | test/data/result.json 32 | 33 | .fliphub 34 | .fusebox 35 | .changelog 36 | .npm-debug.log* 37 | -------------------------------------------------------------------------------- /packages/inferno/src/DOM/events/linkEvent.ts: -------------------------------------------------------------------------------- 1 | import {isFunction} from 'inferno-shared'; 2 | /** 3 | * Links given data to event as first parameter 4 | * @param {*} data data to be linked, it will be available in function as first parameter 5 | * @param {Function} event Function to be called when event occurs 6 | * @returns {{data: *, event: Function}} 7 | */ 8 | export function linkEvent(data, event) { 9 | if (isFunction(event)) { 10 | return { data, event }; 11 | } 12 | return null; // Return null when event is invalid, to avoid creating unnecessary event handlers 13 | } 14 | -------------------------------------------------------------------------------- /packages/inferno-mobx/__tests__/tracking.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { trackComponents } from '../dist-es/makeReactive'; 3 | 4 | describe('MobX trackComponents()', () => { 5 | const _WeakMap = WeakMap; 6 | 7 | it('should throw if WeakMap is undefined', () => { 8 | // eslint-disable-next-line 9 | WeakMap = undefined; 10 | expect(trackComponents).to.throw(Error); 11 | }); 12 | 13 | it('should run', () => { 14 | // eslint-disable-next-line 15 | WeakMap = _WeakMap; 16 | trackComponents(); 17 | expect(trackComponents).to.not.throw(Error); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/inferno-compat/lib/EventPluginUtils.js: -------------------------------------------------------------------------------- 1 | function isEndish(topLevelType) { 2 | return topLevelType === 'topMouseUp' || 3 | topLevelType === 'topTouchEnd' || 4 | topLevelType === 'topTouchCancel'; 5 | } 6 | 7 | function isMoveish(topLevelType) { 8 | return topLevelType === 'topMouseMove' || 9 | topLevelType === 'topTouchMove'; 10 | } 11 | function isStartish(topLevelType) { 12 | return topLevelType === 'topMouseDown' || 13 | topLevelType === 'topTouchStart'; 14 | } 15 | 16 | module.exports = { 17 | isEndish: isEndish, 18 | isMoveish: isMoveish, 19 | isStartish: isStartish 20 | }; 21 | -------------------------------------------------------------------------------- /test/data/separate-render/childseparate.tsx: -------------------------------------------------------------------------------- 1 | import Component from 'inferno-component'; 2 | 3 | export class ChildSeparate extends Component { 4 | constructor(props) { 5 | super(props); 6 | 7 | this._update = this._update.bind(this); 8 | } 9 | 10 | _update() { 11 | this.setState({ 12 | data: 'bar' 13 | }); 14 | } 15 | 16 | componentWillMount() { 17 | this.setState({ 18 | data: 'foo' 19 | }); 20 | } 21 | 22 | render() { 23 | return ( 24 |
25 | {this.props.name} 26 | {this.state.data} 27 |
28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/lint/letter-only-props.js: -------------------------------------------------------------------------------- 1 | // for making sure obj properties are letters only 2 | module.exports = { 3 | parser: 'typescript-eslint-parser', 4 | parserOptions: { 5 | sourceType: 'module' 6 | }, 7 | ext: [ '.ts', '.js', '.jsx', '.tsx' ], 8 | ecmaVersion: 2017, 9 | globals: { 10 | document: true, 11 | window: true, 12 | require: true 13 | }, 14 | ecmaFeatures: { 15 | jsx: true, 16 | modules: true, 17 | es6: true 18 | }, 19 | env: { 20 | browser: true, 21 | es6: true, 22 | node: true 23 | }, 24 | rules: { 25 | 'id-match': [ 'error', '^([A-Za-z]+)*$' ] 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /packages/inferno-create-element/__tests__/data/common-render/child.jsx: -------------------------------------------------------------------------------- 1 | import Component from 'inferno-component'; 2 | 3 | export class ChildCommon extends Component { 4 | constructor(props) { 5 | super(props); 6 | 7 | this._update = this._update.bind(this); 8 | } 9 | 10 | _update() { 11 | this.setState({ 12 | data: 'bar' 13 | }); 14 | } 15 | 16 | componentWillMount() { 17 | this.setState({ 18 | data: 'foo' 19 | }); 20 | } 21 | 22 | render() { 23 | return ( 24 |
25 | {this.props.name} 26 | {this.state.data} 27 |
28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/inferno-server/README.md: -------------------------------------------------------------------------------- 1 | # inferno-server 2 | > Inferno package for working with the server 3 | 4 | This package serves as the entry point of the Server-related rendering paths. It is intended to be paired with the isomorphic Inferno, which will be shipped as inferno to npm. 5 | 6 | ## Install 7 | 8 | ``` 9 | npm install inferno inferno-server 10 | ``` 11 | 12 | ## Contents 13 | 14 | * renderToString 15 | 16 | ## Usage 17 | 18 | ```js 19 | import Inferno from 'inferno'; 20 | import InfernoServer from 'inferno-server'; 21 | 22 | InfernoServer.renderToString(
Hello world
, container); 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Before* submitting a PR please: 2 | - Include tests for the functionality you are adding! See CONTRIBUTING.md for details how to run tests. 3 | - Make sure you have based your branch off the [dev branch](https://github.com/infernojs/inferno/tree/dev). 4 | - Submit your PR against the [dev branch](https://github.com/infernojs/inferno/tree/dev). 5 | - Run `npm run build` and check that the build succeeds. 6 | - Ensure that the PR hasn't been submitted before. 7 | 8 | --- 9 | 10 | ## PR Template 11 | 12 | **Objective** 13 | 14 | This PR... 15 | 16 | **Closes Issue** 17 | 18 | It closes Issue #... 19 | -------------------------------------------------------------------------------- /fixtures/packaging/requirejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 20 | 21 | -------------------------------------------------------------------------------- /packages/inferno-mobx/src/index.ts: -------------------------------------------------------------------------------- 1 | import connect from './connect'; 2 | import EventEmitter from './EventEmitter'; 3 | import inject from './inject'; 4 | import { componentByNodeRegistery, renderReporter, trackComponents } from './makeReactive'; 5 | import Provider from './Provider'; 6 | 7 | export default { 8 | EventEmitter, 9 | Provider, 10 | componentByNodeRegistery, 11 | connect, 12 | inject, 13 | observer: connect, 14 | renderReporter, 15 | trackComponents 16 | }; 17 | 18 | export { 19 | EventEmitter, 20 | Provider, 21 | componentByNodeRegistery, 22 | connect as observer, 23 | connect, 24 | inject, 25 | renderReporter, 26 | trackComponents 27 | }; 28 | -------------------------------------------------------------------------------- /examples/playground/playground.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | let t = Inferno.createElement; 3 | 4 | Inferno.render(t('div', null, 5 | t('span', { className: 'foo' }, 'This is a span!') 6 | ), document.getElementById('app')); 7 | 8 | Inferno.render(t('header', null, 9 | t('h1', { className: 'foo' }, 'This is a h1!') 10 | ), document.getElementById('app')); 11 | 12 | Inferno.render(t('header', null, 13 | t('span', { className: 'foo' }, 'This is a span again!') 14 | ), document.getElementById('app')); 15 | 16 | Inferno.render(t('header', null, 17 | 1, t('span', { className: 'foo' }, 'This is a span again!'), 2, 3 18 | ), document.getElementById('app')); 19 | 20 | })(); 21 | -------------------------------------------------------------------------------- /fixtures/packaging/browserify/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-test", 3 | "private": true, 4 | "dependencies": { 5 | "browserify": "^13.3.0", 6 | "cross-env": "^3.2.4" 7 | }, 8 | "scripts": { 9 | "dev": "rm -f dev.js && cross-env NODE_ENV=production && cross-env NODE_PATH=../../../packages/ browserify ./input.js -t [ envify --NODE_ENV development ] > dev.js", 10 | "prod": "rm -f prod.js && cross-env NODE_ENV=production && cross-env NODE_PATH=../../../packages/ browserify ./input.js -t [ envify --NODE_ENV production ] > prod.js", 11 | "build": "npm run dev && npm run prod" 12 | }, 13 | "devDependencies": { 14 | "envify": "^4.0.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/inferno-mobx/src/EventEmitter.ts: -------------------------------------------------------------------------------- 1 | export default class EventEmitter { 2 | 3 | private listeners: Function[] = []; 4 | 5 | public on(cb: Function) { 6 | this.listeners.push(cb); 7 | return () => { 8 | const index = this.listeners.indexOf(cb); 9 | if (index !== -1) { 10 | this.listeners.splice(index, 1); 11 | } 12 | }; 13 | } 14 | 15 | public emit(data: any) { 16 | const listeners = this.listeners; 17 | for (let i = 0, len = listeners.length; i < len; i++) { 18 | listeners[ i ](data); 19 | } 20 | } 21 | 22 | public getTotalListeners(): number { 23 | return this.listeners.length; 24 | } 25 | 26 | public clearListeners(): void { 27 | this.listeners = []; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /benchmarks/async-render/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Async Render: Inferno 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/inferno-shared/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inferno-shared", 3 | "version": "3.1.1", 4 | "license": "MIT", 5 | "description": "Helpers functions for Inferno", 6 | "author": { 7 | "name": "Dominic Gannaway", 8 | "email": "dg@domgan.com" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/infernojs/inferno/issues" 12 | }, 13 | "homepage": "https://github.com/infernojs/inferno#readme", 14 | "main": "index.js", 15 | "inferno:main": "dist-es/index.js", 16 | "typings": "dist/index.d.ts", 17 | "keywords": [ 18 | "inferno", 19 | "helpers" 20 | ], 21 | "repository": "https://github.com/infernojs/inferno", 22 | "rollup": { 23 | "moduleName": "Inferno.Shared" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/inferno-compat/src/PropTypes.ts: -------------------------------------------------------------------------------- 1 | 2 | // Inlined PropTypes, there is propType checking ATM. 3 | // tslint:disable-next-line:no-empty 4 | function proptype() {} 5 | (proptype as any).isRequired = proptype; 6 | 7 | const getProptype = () => proptype; 8 | 9 | const PropTypes = { 10 | any: getProptype, 11 | array: proptype, 12 | arrayOf: getProptype, 13 | bool: proptype, 14 | element: getProptype, 15 | func: proptype, 16 | instanceOf: getProptype, 17 | node: getProptype, 18 | number: proptype, 19 | object: proptype, 20 | objectOf: getProptype, 21 | oneOf: getProptype, 22 | oneOfType: getProptype, 23 | shape: getProptype, 24 | string: proptype, 25 | symbol: proptype 26 | }; 27 | 28 | export default PropTypes; 29 | -------------------------------------------------------------------------------- /packages/inferno-compat/lib/ReactFragment.js: -------------------------------------------------------------------------------- 1 | var inferno = require('inferno-compat'); 2 | 3 | exports.create = function(obj) { 4 | var children = []; 5 | for (var key in obj) { 6 | if (obj.hasOwnProperty(key)) { 7 | var child = [].concat(obj[ key ]); 8 | for (var i = 0; i < child.length; i++) { 9 | var c = child[ i ]; 10 | // if unkeyed, clone attrs and inject key 11 | if (inferno.isValidElement(c) && !(c.props && c.props.key)) { 12 | var a = {}; 13 | if (c.props) for (var j in c.props) a[ j ] = c.props[ j ]; 14 | a.key = key + '.' + i; 15 | c = inferno.createElement(c.type, a, c.children); 16 | } 17 | if (c != null) children.push(c); 18 | } 19 | } 20 | } 21 | return children; 22 | }; 23 | -------------------------------------------------------------------------------- /packages/inferno-vnode-flags/src/index.ts: -------------------------------------------------------------------------------- 1 | const enum VNodeFlags { 2 | Text = 1, 3 | HtmlElement = 1 << 1, 4 | 5 | ComponentClass = 1 << 2, 6 | ComponentFunction = 1 << 3, 7 | ComponentUnknown = 1 << 4, 8 | 9 | HasKeyedChildren = 1 << 5, 10 | HasNonKeyedChildren = 1 << 6, 11 | 12 | SvgElement = 1 << 7, 13 | MediaElement = 1 << 8, 14 | InputElement = 1 << 9, 15 | TextareaElement = 1 << 10, 16 | SelectElement = 1 << 11, 17 | Void = 1 << 12, 18 | 19 | FormElement = InputElement | TextareaElement | SelectElement, 20 | Element = HtmlElement | SvgElement | MediaElement | InputElement | TextareaElement | SelectElement, 21 | Component = ComponentFunction | ComponentClass | ComponentUnknown 22 | } 23 | 24 | export default VNodeFlags; 25 | -------------------------------------------------------------------------------- /packages/inferno-create-element/__tests__/clonenode.spec.jsx: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { render } from 'inferno'; 3 | 4 | describe('CloneVNode use cases', () => { 5 | let container; 6 | 7 | beforeEach(function () { 8 | container = document.createElement('div'); 9 | document.body.appendChild(container); 10 | }); 11 | 12 | afterEach(function () { 13 | render(null, container); 14 | container.innerHTML = ''; 15 | document.body.removeChild(container); 16 | }); 17 | 18 | it('Should be able to render hoisted node', () => { 19 | const a = [ 'foo', 'bar' ]; 20 | 21 | render(
{[ a, a, a, a ]}
, container); 22 | 23 | expect(container.innerHTML).to.eql('
foobarfoobarfoobarfoobar
'); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /packages/inferno-create-class/README.md: -------------------------------------------------------------------------------- 1 | # inferno-create-class 2 | > Provides a helper to create Inferno Components without needing ES2015 3 | 4 | Note: this is similar to `React.createClass` in that methods are out-bound. However, this module does not support mixins. 5 | 6 | ## Install 7 | 8 | ``` 9 | npm install inferno-create-class 10 | ``` 11 | 12 | ## Usage 13 | 14 | ```js 15 | var createClass = require('inferno-create-class'); 16 | var Inferno = require('inferno'); 17 | var InfernoDOM = require('inferno-dom'); 18 | 19 | var MyComponent = createClass({ 20 | displayName: 'MyComponent', 21 | render: function() { 22 | return
Hell world!
; 23 | } 24 | }); 25 | 26 | InfernoDOM.render(, container); 27 | ``` 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/routing/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Inferno example 6 | 7 | 8 |
9 | 10 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/inferno-server/src/index.ts: -------------------------------------------------------------------------------- 1 | import renderToString, { renderToStaticMarkup } from './renderToString'; 2 | import streamQueueAsString, { RenderQueueStream, streamQueueAsStaticMarkup } from './renderToString.queuestream'; 3 | import streamAsString, { RenderStream, streamAsStaticMarkup } from './renderToString.stream'; 4 | 5 | export default { 6 | RenderQueueStream, 7 | RenderStream, 8 | renderToStaticMarkup, 9 | renderToString, 10 | streamAsStaticMarkup, 11 | streamAsString, 12 | streamQueueAsStaticMarkup, 13 | streamQueueAsString 14 | }; 15 | 16 | export { 17 | RenderQueueStream, 18 | RenderStream, 19 | renderToStaticMarkup, 20 | renderToString, 21 | streamAsStaticMarkup, 22 | streamAsString, 23 | streamQueueAsStaticMarkup, 24 | streamQueueAsString 25 | }; 26 | -------------------------------------------------------------------------------- /fixtures/packaging/systemjs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 26 | 27 | -------------------------------------------------------------------------------- /packages/inferno-router/src/RouterContext.ts: -------------------------------------------------------------------------------- 1 | import Component from 'inferno-component'; 2 | import { IRouterProps } from './Router'; 3 | 4 | export default class RouterContext extends Component { 5 | constructor(props?: any, context?: any) { 6 | super(props, context); 7 | if (process.env.NODE_ENV !== 'production') { 8 | if (!props.location || !props.matched) { 9 | throw new TypeError('"inferno-router" requires a "location" and "matched" props passed'); 10 | } 11 | } 12 | } 13 | 14 | public getChildContext() { 15 | return { 16 | router: this.props.router || { 17 | location: { 18 | baseUrl: this.props.baseUrl, 19 | pathname: this.props.location 20 | } 21 | } 22 | }; 23 | } 24 | 25 | public render(props) { 26 | return props.matched; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/inferno/src/core/options.ts: -------------------------------------------------------------------------------- 1 | import { LifecycleClass } from 'inferno-shared'; 2 | import { InfernoInput } from './VNodes'; 3 | 4 | export interface Root { 5 | dom: Element | SVGAElement; 6 | input: InfernoInput; 7 | lifecycle: LifecycleClass; 8 | } 9 | 10 | export const options: { 11 | afterMount: null|Function, 12 | afterRender: null|Function, 13 | afterUpdate: null|Function, 14 | beforeRender: null|Function, 15 | beforeUnmount: null|Function 16 | createVNode: null|Function, 17 | findDOMNodeEnabled: boolean, 18 | recyclingEnabled: boolean, 19 | roots: Root[] 20 | } = { 21 | afterMount: null, 22 | afterRender: null, 23 | afterUpdate: null, 24 | beforeRender: null, 25 | beforeUnmount: null, 26 | createVNode: null, 27 | findDOMNodeEnabled: false, 28 | recyclingEnabled: false, 29 | roots: [] 30 | }; 31 | -------------------------------------------------------------------------------- /packages/inferno-router/__tests__/utils.spec.jsx: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { isEmpty, mapSearchParams } from '../dist-es/utils'; 3 | 4 | describe('Router #utils', () => { 5 | it('it should map search params to object', () => { 6 | let params; 7 | params = mapSearchParams('hello=world'); 8 | expect(params.hello).to.equal('world'); 9 | 10 | params = mapSearchParams('hello=world&utf8=çava-oui'); 11 | expect(params.utf8).to.equal('çava-oui'); 12 | 13 | params = mapSearchParams('arr[]=one&arr[]=two&arr[]=çava-oui'); 14 | expect(params.arr[ 2 ]).to.equal('çava-oui'); 15 | }); 16 | 17 | it('it should return true for an empty object or array', () => { 18 | expect(isEmpty([])).to.equal(true); 19 | expect(isEmpty({})).to.equal(true); 20 | expect(isEmpty(Object.create(null))).to.equal(true); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /benchmarks/vdom-bench/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Virtual DOM Benchmark: InfernoJS 7 | 8 | 9 |

Virtual DOM Benchmark: InfernoJS

10 |

Source code

11 |
12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /packages/inferno-router/src/index.ts: -------------------------------------------------------------------------------- 1 | import { VNode } from 'inferno'; 2 | import createRoutes, { IPlainRouteConfig } from './createRoutes'; 3 | import IndexLink from './IndexLink'; 4 | import IndexRoute from './IndexRoute'; 5 | import Link from './Link'; 6 | import match from './match'; 7 | import Redirect from './Redirect'; 8 | import Route from './Route'; 9 | import Router from './Router'; 10 | import RouterContext from './RouterContext'; 11 | 12 | export { 13 | IndexLink, 14 | Redirect as IndexRedirect, 15 | IndexRoute, 16 | IPlainRouteConfig, 17 | Link, 18 | Redirect, 19 | Route, 20 | Router, 21 | RouterContext, 22 | VNode, 23 | createRoutes, 24 | match 25 | }; 26 | 27 | export default { 28 | IndexLink, 29 | IndexRedirect: Redirect, 30 | IndexRoute, 31 | Link, 32 | Redirect, 33 | Route, 34 | Router, 35 | RouterContext, 36 | createRoutes, 37 | match 38 | }; 39 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - '6' 5 | 6 | notifications: 7 | email: 8 | on_success: change 9 | on_failure: always 10 | 11 | # Use container-based Travis infrastructure. 12 | sudo: required 13 | dist: trusty 14 | 15 | env: 16 | - CXX=g++-4.8 17 | addons: 18 | firefox: "latest" 19 | apt: 20 | sources: 21 | - ubuntu-toolchain-r-test 22 | packages: 23 | - g++-4.8 24 | 25 | addons: 26 | at: 27 | sources: 28 | - google-chrome 29 | packages: 30 | - google-chrome-stable 31 | - google-chrome-beta 32 | 33 | before_install: 34 | - "nvm use $TRAVIS_NODE_VERSION" 35 | # Repo for newer Node.js versions 36 | - curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash - 37 | 38 | before_script: 39 | - "export DISPLAY=:99.0" 40 | - "sh -e /etc/init.d/xvfb start" 41 | 42 | after_script: 43 | - npm run test:publish 44 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "forin": false, 5 | "indent": [ true, "tabs" ], 6 | "interface-name": false, 7 | "ban-types": true, 8 | "max-classes-per-file": true, 9 | "max-line-length": false, 10 | "member-access": true, 11 | "member-ordering": false, 12 | "no-bitwise": false, 13 | "no-conditional-assignment": false, 14 | "no-debugger": false, 15 | "no-empty": true, 16 | "no-namespace": false, 17 | "no-unused-expression": true, 18 | "object-literal-sort-keys": true, 19 | "one-variable-per-declaration": [true, "ignore-for-loop"], 20 | "only-arrow-functions": [false], 21 | "ordered-imports": true, 22 | "prefer-const": true, 23 | "prefer-for-of": false, 24 | "quotemark": [ true, "single", "jsx-double" ], 25 | "trailing-comma": [true, {"multiline": "never", "singleline": "never"}], 26 | "variable-name": false 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/inferno-compat/lib/ReactTransitionEvents.js: -------------------------------------------------------------------------------- 1 | var supported, prefix; 2 | 3 | if (typeof document !== 'undefined' && document.createElement) { 4 | var d = document.createElement('div'); 5 | for (var i in d.style) { 6 | var m = i.match(/^(moz|webkit|ms|)(transition|animation)$/i); 7 | if (m) supported = true; 8 | if (m && m[ 1 ]) prefix = m[ 1 ]; 9 | } 10 | } 11 | 12 | function each(node, fn, listener, prefix) { 13 | node[ fn ]((prefix || '') + 'TransitionEnd', listener); 14 | node[ fn ]((prefix || '') + 'AnimationEnd', listener); 15 | if (prefix) each(node, fn, listener); 16 | } 17 | 18 | module.exports = { 19 | addEndEventListener(el, listener) { 20 | if (supported) each(el, 'addEventListener', listener, prefix); 21 | else setTimeout(listener, 0); 22 | }, 23 | removeEndEventListener(el, listener) { 24 | if (supported) each(el, 'removeEventListener', listener, prefix); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /bin/link: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const { ensureSymlinkSync } = require('fs-extra'); 4 | const { resolve, join } = require('path'); 5 | const rimraf = require('rimraf'); 6 | 7 | const PROJECT_FOLDER = resolve(__dirname, '..'); 8 | // TODO: Remove this once new inferno-vnode-flags is published 9 | rimraf.sync(join(PROJECT_FOLDER, 'node_modules', 'inferno')); 10 | rimraf.sync(join(PROJECT_FOLDER, 'node_modules', 'inferno-vnode-flags')); 11 | 12 | const cwd = process.cwd(); 13 | 14 | ensureSymlinkSync(join(PROJECT_FOLDER, 'tsconfig.json'), join(cwd, 'tsconfig.json')); 15 | ensureSymlinkSync(join(PROJECT_FOLDER, '.npmignore'), join(cwd, '.npmignore')); 16 | 17 | ensureSymlinkSync(join(PROJECT_FOLDER, 'packages', 'inferno-vnode-flags'), join(PROJECT_FOLDER, 'node_modules', 'inferno-vnode-flags')); 18 | // ensureSymlinkSync(join(PROJECT_FOLDER, 'packages', 'inferno'), join(PROJECT_FOLDER, 'node_modules', 'inferno')); 19 | -------------------------------------------------------------------------------- /packages/inferno-create-element/__tests__/normalizeProps.spec.jsx: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { internal_normalize as normalize } from 'inferno'; 3 | 4 | describe('normalizeProps', () => { 5 | it('should delete ref from props', () => { 6 | const vNode = { 7 | children: null, 8 | dom: null, 9 | events: null, 10 | flags: 0, 11 | key: null, 12 | props: { 13 | ref: () => { 14 | } 15 | }, 16 | ref: null, 17 | type: null 18 | }; 19 | 20 | normalize(vNode); 21 | 22 | expect(vNode.props).to.not.have.property('ref'); 23 | }); 24 | 25 | it('should delete key from props', () => { 26 | const vNode = { 27 | children: null, 28 | dom: null, 29 | events: null, 30 | flags: 0, 31 | key: null, 32 | props: { key: 'key' }, 33 | ref: null, 34 | type: null 35 | }; 36 | 37 | normalize(vNode); 38 | 39 | expect(vNode.props).to.not.have.property('key'); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /packages/inferno-server/src/utils.ts: -------------------------------------------------------------------------------- 1 | const ecapeCharacters = { 2 | '"': '"', 3 | '&': '&', 4 | '\'': ''', 5 | '<': '<', 6 | '>': '>' 7 | }; 8 | const escapeChar = (char) => ecapeCharacters[ char ] || char; 9 | 10 | export function escapeText(text) { 11 | return String(text).replace(/[<>"'&]/g, escapeChar); 12 | } 13 | 14 | const uppercasePattern = /[A-Z]/g; 15 | const msPattern = /^ms-/; 16 | 17 | export function toHyphenCase(str) { 18 | return str.replace(uppercasePattern, '-$&').toLowerCase().replace(msPattern, '-ms-'); 19 | } 20 | 21 | const voidElements = { 22 | area: true, 23 | base: true, 24 | br: true, 25 | col: true, 26 | command: true, 27 | embed: true, 28 | hr: true, 29 | img: true, 30 | input: true, 31 | keygen: true, 32 | link: true, 33 | meta: true, 34 | param: true, 35 | source: true, 36 | track: true, 37 | wbr: true 38 | }; 39 | 40 | export function isVoidElement(str) { 41 | return !!voidElements[ str ]; 42 | } 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Before* submitting an issue please: 2 | - Check that you are using the latest version of Inferno. Either using our [CDN @ Master](http://cdn.infernojs.org/latest/inferno.js) or by checking the tags on [NPM](http://www.npmjs.com/package/inferno). 3 | - Check that the bug has not been fixed in the latest development version. Use our [CDN @ Edge](http://cdn.infernojs.org/edge/inferno.js). 4 | - Check that the issue has not been brought up before on [Github issues](http://www.github.com/infernojs/inferno/issues). 5 | 6 | **If you can, please distill your problem down and include a JSFiddle example for illustration. Also when requesting bug fix please include at least one test to avoid regression.** 7 | --- 8 | 9 | ## Issue Template 10 | 11 | **Observed Behaviour** 12 | 13 | Inferno is... 14 | 15 | **Expected Current Behaviour** 16 | 17 | Inferno should... 18 | 19 | **Inferno Metadata** 20 | 21 | macOS / Windowx / Linux 22 | Safari / Chrome / Firefox / ... 23 | -------------------------------------------------------------------------------- /packages/inferno-hyperscript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inferno-hyperscript", 3 | "version": "3.3.1", 4 | "license": "MIT", 5 | "description": "Bridging hyperscript to InfernoJS", 6 | "author": "Terin Stock (https://terinstock.com/)", 7 | "bugs": { 8 | "url": "https://github.com/infernojs/inferno/issues" 9 | }, 10 | "homepage": "https://github.com/infernojs/inferno#readme", 11 | "main": "index.js", 12 | "inferno:main": "dist-es/index.js", 13 | "typings": "dist/index.d.ts", 14 | "keywords": [ 15 | "inferno", 16 | "hyperscript" 17 | ], 18 | "repository": "https://github.com/infernojs/inferno", 19 | "dependencies": { 20 | "inferno": "3.3.1", 21 | "inferno-shared": "3.1.1", 22 | "inferno-vnode-flags": "^3.0.0" 23 | }, 24 | "rollup": { 25 | "bundledDependencies": [ 26 | "inferno-shared", 27 | "inferno-vnode-flags" 28 | ], 29 | "moduleName": "Inferno.h", 30 | "moduleGlobals": { 31 | "inferno": "Inferno" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/inferno-vnode-flags/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inferno-vnode-flags", 3 | "version": "3.0.0", 4 | "license": "MIT", 5 | "description": "Provides an enum of all possible VNode Flags used when calling Inferno.createVNode", 6 | "author": { 7 | "name": "Dominic Gannaway", 8 | "email": "dg@domgan.com" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/infernojs/inferno/issues" 12 | }, 13 | "homepage": "https://github.com/infernojs/inferno#readme", 14 | "keywords": [ 15 | "babel", 16 | "react", 17 | "inferno", 18 | "framework", 19 | "interfaces", 20 | "user interfaces", 21 | "html", 22 | "renderToString", 23 | "server", 24 | "dom", 25 | "browser", 26 | "rollup", 27 | "vnode", 28 | "createVNode", 29 | "flags" 30 | ], 31 | "rollup": { 32 | "moduleName": "Inferno.VNodeFlags" 33 | }, 34 | "main": "index.js", 35 | "inferno:main": "dist-es/index.js", 36 | "typings": "dist/index.d.ts", 37 | "repository": "https://github.com/infernojs/inferno" 38 | } 39 | -------------------------------------------------------------------------------- /packages/inferno/__benchmarks__/rendering.js: -------------------------------------------------------------------------------- 1 | import { createTextVNode, createVNode } from '../dist-es/core/VNodes'; 2 | import { render } from '../dist-es/index'; 3 | 4 | suite('Rendering', () => { 5 | let container = document.createElement('div'); 6 | let emptyFn = function () {}; 7 | 8 | benchmark('Render Single div', () => { 9 | render(createVNode(2, 'div', null, null), container); 10 | }); 11 | 12 | benchmark('Render and unmount single node with attributes and events', () => { 13 | render(createVNode(2, 'div', 'foobar', null, { id: 'test', 'data-attribute': 'foo', second: 'bar', onClick: emptyFn }, null), container); 14 | render(null, container); 15 | }); 16 | 17 | benchmark('Render patch and unmount single node with attributes and events', () => { 18 | render(createVNode(2, 'div', 'foobar', null, { id: 'test', 'data-attribute': 'foo', second: 'bar', onClick: emptyFn }, null), container); 19 | render(createVNode(2, 'div', 'foobar2', null, { id: 'test', XXX: '2', second: 'test', onClick: null }, null), container); 20 | render(null, container); 21 | }); 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /fixtures/packaging/build-all.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | var childProcess = require('child_process'); 5 | 6 | var fixtureDirs = fs.readdirSync(__dirname).filter((file) => { 7 | return fs.statSync(path.join(__dirname, file)).isDirectory(); 8 | }); 9 | 10 | console.log(fixtureDirs); 11 | 12 | var cmdArgs = [ 13 | {cmd: 'npm', args: ['install']}, 14 | {cmd: 'npm', args: ['run', 'build']}, 15 | ]; 16 | 17 | for (const dir of fixtureDirs) { 18 | for (const cmdArg of cmdArgs) { 19 | const opts = { 20 | cwd: path.join(__dirname, dir), 21 | stdio: 'inherit', 22 | }; 23 | 24 | console.log(opts); 25 | 26 | let result = childProcess.spawnSync(cmdArg.cmd, cmdArg.args, opts); 27 | if (result.status !== 0) { 28 | throw new Error('Failed to build fixtures.'); 29 | } 30 | } 31 | } 32 | 33 | console.log('-------------------------'); 34 | console.log('All fixtures were built!'); 35 | console.log('Now make sure to open each HTML file in this directory and each index.html in subdirectories.'); 36 | console.log('-------------------------'); 37 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | Copyright (c) 2013-2016 Dominic Gannaway 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /packages/inferno-redux/src/helpers.ts: -------------------------------------------------------------------------------- 1 | // From https://github.com/lodash/lodash/blob/es 2 | function overArg(func, transform) { 3 | return function(arg) { 4 | return func(transform(arg)); 5 | }; 6 | } 7 | 8 | const getPrototype = overArg(Object.getPrototypeOf, Object); 9 | 10 | function isObjectLike(value) { 11 | return value != null && typeof value === 'object'; 12 | } 13 | 14 | const objectTag = '[object Object]'; 15 | const funcProto = Function.prototype; 16 | const objectProto = Object.prototype; 17 | 18 | const funcToString = funcProto.toString; 19 | const hasOwnProperty = objectProto.hasOwnProperty; 20 | const objectCtorString = funcToString.call(Object); 21 | const objectToString = objectProto.toString; 22 | 23 | export function isPlainObject(value) { 24 | if (!isObjectLike(value) || objectToString.call(value) !== objectTag) { 25 | return false; 26 | } 27 | const proto = getPrototype(value); 28 | if (proto === null) { 29 | return true; 30 | } 31 | const Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; 32 | return (typeof Ctor === 'function' && 33 | Ctor instanceof Ctor && funcToString.call(Ctor) === objectCtorString); 34 | } 35 | -------------------------------------------------------------------------------- /packages/inferno/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inferno", 3 | "version": "3.3.1", 4 | "license": "MIT", 5 | "description": "An extremely fast, React-like JavaScript library for building modern user interfaces", 6 | "author": { 7 | "name": "Dominic Gannaway", 8 | "email": "dg@domgan.com" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/infernojs/inferno/issues" 12 | }, 13 | "homepage": "https://github.com/infernojs/inferno#readme", 14 | "keywords": [ 15 | "babel", 16 | "react", 17 | "inferno", 18 | "framework", 19 | "interfaces", 20 | "user interfaces", 21 | "html", 22 | "renderToString", 23 | "server", 24 | "dom", 25 | "browser", 26 | "rollup" 27 | ], 28 | "rollup": { 29 | "bundledDependencies": [ 30 | "inferno-shared", 31 | "inferno-vnode-flags" 32 | ], 33 | "moduleName": "Inferno" 34 | }, 35 | "main": "index.js", 36 | "inferno:main": "dist-es/index.js", 37 | "typings": "dist/index.d.ts", 38 | "repository": "https://github.com/infernojs/inferno", 39 | "dependencies": { 40 | "inferno-shared": "3.1.1", 41 | "inferno-vnode-flags": "^3.0.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/inferno-create-class/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inferno-create-class", 3 | "version": "3.3.1", 4 | "license": "MIT", 5 | "description": "Provides a helper to create Inferno Components without needing ES2015", 6 | "author": { 7 | "name": "Dominic Gannaway", 8 | "email": "dg@domgan.com" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/infernojs/inferno/issues" 12 | }, 13 | "homepage": "https://github.com/infernojs/inferno#readme", 14 | "main": "index.js", 15 | "inferno:main": "dist-es/index.js", 16 | "typings": "dist/index.d.ts", 17 | "keywords": [ 18 | "babel", 19 | "react", 20 | "inferno", 21 | "framework", 22 | "interfaces", 23 | "user interfaces" 24 | ], 25 | "dependencies": { 26 | "inferno-component": "3.3.1", 27 | "inferno-shared": "3.1.1" 28 | }, 29 | "devDependencies": { 30 | "inferno": "3.3.1", 31 | "inferno-create-element": "3.3.1" 32 | }, 33 | "rollup": { 34 | "bundledDependencies": [ 35 | "inferno-shared" 36 | ], 37 | "moduleName": "Inferno.createClass", 38 | "moduleGlobals": { 39 | "inferno-component": "Inferno.Component" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "compact": false, 3 | "presets": [ 4 | ["es2015", {"loose": true}], 5 | "stage-2" 6 | ], 7 | "plugins": [ 8 | "transform-class-properties", 9 | "transform-object-rest-spread", 10 | "babel-plugin-syntax-jsx", 11 | ["babel-plugin-inferno", {"imports": true}], 12 | ["module-resolver", { 13 | "extensions": [".js", ".jsx"], 14 | "alias": { 15 | "inferno-compat": "./packages/inferno-compat/dist-es", 16 | "inferno-component": "./packages/inferno-component/dist-es", 17 | "inferno-create-class": "./packages/inferno-create-class/dist-es", 18 | "inferno-create-element": "./packages/inferno-create-element/dist-es", 19 | "inferno-shared": "./packages/inferno-shared/dist-es", 20 | "inferno-hyperscript": "./packages/inferno-hyperscript/dist-es", 21 | "inferno-mobx": "./packages/inferno-mobx/dist-es", 22 | "inferno-redux": "./packages/inferno-redux/dist-es", 23 | "inferno-router": "./packages/inferno-router/dist-es", 24 | "inferno-server": "./packages/inferno-server/dist-es", 25 | "inferno": "./packages/inferno/dist-es" 26 | } 27 | }] 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /packages/inferno-create-element/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inferno-create-element", 3 | "version": "3.3.1", 4 | "license": "MIT", 5 | "description": "Provides methods to create Inferno VNodes", 6 | "author": { 7 | "name": "Dominic Gannaway", 8 | "email": "dg@domgan.com" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/infernojs/inferno/issues" 12 | }, 13 | "homepage": "https://github.com/infernojs/inferno#readme", 14 | "main": "index.js", 15 | "inferno:main": "dist-es/index.js", 16 | "typings": "dist/index.d.ts", 17 | "keywords": [ 18 | "babel", 19 | "react", 20 | "inferno", 21 | "framework", 22 | "interfaces", 23 | "user interfaces" 24 | ], 25 | "dependencies": { 26 | "inferno": "3.3.1", 27 | "inferno-component": "3.3.1", 28 | "inferno-shared": "3.1.1", 29 | "inferno-vnode-flags": "^3.0.0" 30 | }, 31 | "rollup": { 32 | "bundledDependencies": [ 33 | "inferno-shared", 34 | "inferno-vnode-flags" 35 | ], 36 | "moduleName": "Inferno.createElement", 37 | "moduleGlobals": { 38 | "inferno": "Inferno", 39 | "inferno-component": "Inferno.Component" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/karma/karma.bench.conf.js: -------------------------------------------------------------------------------- 1 | /* global module */ 2 | /* tslint:disable */ 3 | 4 | const webpack = require('webpack'); 5 | const path = require('path'); 6 | const base = require('./karma.base.conf'); 7 | 8 | module.exports = function (config) { 9 | base(config); 10 | config.set({ 11 | files: [ 12 | `packages/${process.env.BENCH_FILTER || '*'}/__benchmarks__/**/*.js*` 13 | ], 14 | frameworks: [ 15 | 'benchmark' 16 | ], 17 | reporters: [ 18 | 'benchmark', 19 | 'json-result' 20 | ], 21 | jsonResultReporter: { 22 | outputFile: path.join(__dirname, '..', 'data', 'result.json') 23 | }, 24 | webpack: { 25 | plugins: [ 26 | new webpack.DefinePlugin({ 27 | 'process.env': { 28 | NODE_ENV: '"production"' 29 | } 30 | }), 31 | new webpack.optimize.UglifyJsPlugin({ 32 | warnings: false, 33 | compress: { 34 | screw_ie8: true, 35 | dead_code: true, 36 | unused: true, 37 | drop_debugger: true, // 38 | booleans: true // various optimizations for boolean context, for example !!a ? b : c → a ? b : c 39 | }, 40 | mangle: { 41 | screw_ie8: true 42 | } 43 | }) 44 | ] 45 | } 46 | }); 47 | }; 48 | -------------------------------------------------------------------------------- /packages/inferno-devtools/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inferno-devtools", 3 | "version": "3.3.1", 4 | "license": "MIT", 5 | "description": "Provides support for React's Dev Tools for Inferno", 6 | "author": { 7 | "name": "Dominic Gannaway", 8 | "email": "dg@domgan.com" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/infernojs/inferno/issues" 12 | }, 13 | "homepage": "https://github.com/infernojs/inferno#readme", 14 | "main": "index.js", 15 | "inferno:main": "dist-es/index.js", 16 | "typings": "dist/index.d.ts", 17 | "keywords": [ 18 | "babel", 19 | "react", 20 | "inferno", 21 | "framework", 22 | "interfaces", 23 | "user interfaces" 24 | ], 25 | "repository": "https://github.com/infernojs/inferno", 26 | "dependencies": { 27 | "inferno": "3.3.1", 28 | "inferno-component": "3.3.1", 29 | "inferno-shared": "3.1.1", 30 | "inferno-vnode-flags": "^3.0.0" 31 | }, 32 | "rollup": { 33 | "bundledDependencies": [ 34 | "inferno-shared" 35 | ], 36 | "moduleName": "Inferno.DevTools", 37 | "moduleGlobals": { 38 | "inferno": "Inferno", 39 | "inferno-component": "Inferno.Component" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/inferno-component/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inferno-component", 3 | "version": "3.3.1", 4 | "license": "MIT", 5 | "description": "Provides ES2015 stateful components (with lifecycle events) to Inferno", 6 | "author": { 7 | "name": "Dominic Gannaway", 8 | "email": "dg@domgan.com" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/infernojs/inferno/issues" 12 | }, 13 | "homepage": "https://github.com/infernojs/inferno#readme", 14 | "main": "index.js", 15 | "inferno:main": "dist-es/index.js", 16 | "typings": "dist/index.d.ts", 17 | "keywords": [ 18 | "babel", 19 | "react", 20 | "inferno", 21 | "framework", 22 | "interfaces", 23 | "user interfaces", 24 | "html", 25 | "renderToString", 26 | "server", 27 | "dom", 28 | "browser", 29 | "rollup" 30 | ], 31 | "dependencies": { 32 | "inferno": "3.3.1", 33 | "inferno-shared": "3.1.1", 34 | "inferno-vnode-flags": "^3.0.0" 35 | }, 36 | "rollup": { 37 | "bundledDependencies": [ 38 | "inferno-shared", 39 | "inferno-vnode-flags" 40 | ], 41 | "moduleName": "Inferno.Component", 42 | "moduleGlobals": { 43 | "inferno": "Inferno" 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/attributes/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Inferno CSS transform example 5 | 6 | 7 |
8 | 15 | 16 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /packages/inferno-mobx/__tests__/eventemitter.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { EventEmitter } from '../dist-es'; 3 | 4 | const testdata = { 5 | testKey: 'testdata' 6 | }; 7 | const testListener = function (data) { 8 | expect(data).to.equal(testdata); 9 | }; 10 | 11 | describe('mobx - EventEmitter', () => { 12 | it('should have an empty listner array on construction', () => { 13 | const unit = new EventEmitter(); 14 | expect(unit.getTotalListeners()).to.equal(0); 15 | }); 16 | 17 | it('should add a listener and allow to remove it', () => { 18 | const unit = new EventEmitter(); 19 | const removeListener = unit.on(testListener); 20 | 21 | expect(unit.getTotalListeners()).to.equal(1); 22 | 23 | removeListener(); 24 | 25 | expect(unit.getTotalListeners()).to.equal(0); 26 | }); 27 | 28 | it('should all data to be emmitted by the listners', () => { 29 | const unit = new EventEmitter(); 30 | const removeListener = unit.on(testListener); 31 | 32 | unit.emit(testdata); 33 | 34 | removeListener(); 35 | }); 36 | 37 | it('should allow to remove all listeners', () => { 38 | const unit = new EventEmitter(); 39 | unit.on(testListener); 40 | unit.clearListeners(); 41 | 42 | expect(unit.getTotalListeners()).to.equal(0); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /packages/inferno-redux/src/Provider.ts: -------------------------------------------------------------------------------- 1 | import Component from 'inferno-component'; 2 | import { isNullOrUndef, toArray } from 'inferno-shared'; 3 | import { warning } from './utils'; 4 | 5 | let didWarnAboutReceivingStore = false; 6 | function warnAboutReceivingStore() { 7 | if (didWarnAboutReceivingStore) { 8 | return; 9 | } 10 | didWarnAboutReceivingStore = true; 11 | 12 | warning( 13 | ' does not support changing `store` on the fly.' 14 | ); 15 | } 16 | 17 | export default class Provider extends Component { 18 | public store: any; 19 | 20 | constructor(props, context?: any) { 21 | super(props, context); 22 | this.store = props.store; 23 | } 24 | 25 | public getChildContext() { 26 | return { store: this.store }; 27 | } 28 | 29 | public render(props) { 30 | if (isNullOrUndef(this.props.children) || toArray(this.props.children).length !== 1) { 31 | throw Error('Inferno Error: Only one child is allowed within the `Provider` component'); 32 | } 33 | 34 | return props.children; 35 | } 36 | } 37 | 38 | if (process.env.NODE_ENV !== 'production') { 39 | Provider.prototype.componentWillReceiveProps = function(nextProps) { 40 | const { store } = this; 41 | const { store: nextStore } = nextProps; 42 | 43 | if (store !== nextStore) { 44 | warnAboutReceivingStore(); 45 | } 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /packages/inferno-router/__tests__/createRoutes.spec.jsx: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import Route from '../dist-es/Route'; 3 | import IndexRoute from '../dist-es/IndexRoute'; 4 | import createRoutes from '../dist-es/createRoutes'; 5 | 6 | const App = () => (
); 7 | const Home = () => (
); 8 | const Films = () => (
); 9 | const FilmDetail = () => (
); 10 | const NoMatch = () => (
); 11 | 12 | const routeConfig = [ 13 | { 14 | path: '/', 15 | component: App, 16 | indexRoute: { 17 | component: Home 18 | }, 19 | childRoutes: [ 20 | { 21 | path: 'films/', 22 | component: Films, 23 | childRoutes: { 24 | path: 'detail/:id', 25 | component: FilmDetail 26 | } 27 | }, 28 | { 29 | path: '/*', 30 | component: NoMatch 31 | } 32 | ] 33 | } 34 | ]; 35 | 36 | const expectedResult = ( 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ); 45 | 46 | describe('Router #createRoutes', () => { 47 | it('it should parse route configuration', () => { 48 | expect(JSON.stringify(createRoutes(routeConfig)[ 0 ])).to.equal(JSON.stringify(expectedResult)); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /packages/inferno-vnode-flags/README.md: -------------------------------------------------------------------------------- 1 | # inferno-vnode-flags 2 | 3 | Inferno VNode Flags is a small utility library for [Inferno](https://github.com/infernojs/inferno). 4 | 5 | Usage of `inferno-vnode-flags` should be limited to assigning `VNodeFlags` when using `Inferno.createVNode`. 6 | 7 | ## Install 8 | 9 | ``` 10 | npm install --save inferno-vnode-flags 11 | ``` 12 | 13 | ## Contents 14 | 15 | - `VNodeFlags.Text` 16 | - `VNodeFlags.HtmlElement` 17 | - `VNodeFlags.ComponentClass` 18 | - `VNodeFlags.ComponentFunction` 19 | - `VNodeFlags.ComponentUnknown` 20 | - `VNodeFlags.HasKeyedChildren` 21 | - `VNodeFlags.HasNonKeyedChildren` 22 | - `VNodeFlags.SvgElement` 23 | - `VNodeFlags.MediaElement` 24 | - `VNodeFlags.InputElement` 25 | - `VNodeFlags.TextareaElement` 26 | - `VNodeFlags.SelectElement` 27 | - `VNodeFlags.Void` 28 | 29 | You can easily combine multiple flags, by using bitwise operators. A common use case is an element that has keyed children: 30 | 31 | ```js 32 | const flag = VNodeFlags.HtmlElement | VNodeFlags.HasKeyedChildren; 33 | ``` 34 | 35 | ## Example Usage 36 | 37 | ```js 38 | import Inferno from 'inferno'; 39 | import VNodeFlags from 'inferno-vnode-flags'; 40 | 41 | const vNode = Inferno.createVNode(VNodeFlags.Element, 'div', { className: 'example' }, 'Hello world!'); 42 | 43 | Inferno.render(vNode, container); 44 | ``` 45 | -------------------------------------------------------------------------------- /test/karma/sauce.js: -------------------------------------------------------------------------------- 1 | const { 2 | TRAVIS_BRANCH 3 | } = process.env; 4 | 5 | const platformNames = { 6 | 'Windows 7': 'Win7', 7 | 'Windows 10': 'Win10', 8 | 'OS X 10.11': 'Mac', 9 | Linux: 'Linux' 10 | }; 11 | 12 | const browsers = { 13 | 'Windows 7': { 14 | 'Internet Explorer': ['11'], 15 | chrome: [ '55', '56', '57', '58' ], 16 | firefox: [ '50', '51', '52', '53' ] 17 | }, 18 | 'Windows 10': { 19 | chrome: [ '55', '56', '57', '58' ], 20 | firefox: [ '50', '51', '52', '53' ], 21 | MicrosoftEdge: [ '13', '14' ] 22 | }, 23 | 'OS X 10.11': { 24 | safari: [ '9', '10' ] 25 | } 26 | }; 27 | 28 | class Config { 29 | constructor() { 30 | this.launchers = {}; 31 | this.browsers = []; 32 | } 33 | add(platform, browser, version) { 34 | const sauceName = `Sauce_${platformNames[platform]}_${browser}@${version}`; 35 | const config = { 36 | base: 'SauceLabs', 37 | browserName: browser, 38 | platform, 39 | version 40 | }; 41 | this.launchers[sauceName] = config; 42 | this.browsers.push(sauceName); 43 | } 44 | } 45 | 46 | const config = new Config(); 47 | for (let platform in browsers) { 48 | for (let browser in browsers[platform]) { 49 | const versions = browsers[platform][browser]; 50 | for (let i = 0; i < versions.length; i += 1) { 51 | config.add(platform, browser, versions[i]); 52 | } 53 | } 54 | } 55 | 56 | module.exports = config; 57 | -------------------------------------------------------------------------------- /packages/inferno-redux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inferno-redux", 3 | "version": "3.3.1", 4 | "license": "MIT", 5 | "description": "Official Inferno bindings for Redux", 6 | "author": { 7 | "name": "Dominic Gannaway", 8 | "email": "dg@domgan.com" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/infernojs/inferno/issues" 12 | }, 13 | "homepage": "https://github.com/infernojs/inferno#readme", 14 | "main": "index.js", 15 | "inferno:main": "dist-es/index.js", 16 | "typings": "dist/index.d.ts", 17 | "keywords": [ 18 | "babel", 19 | "react", 20 | "inferno", 21 | "framework", 22 | "interfaces", 23 | "user interfaces", 24 | "redux" 25 | ], 26 | "repository": "https://github.com/infernojs/inferno", 27 | "dependencies": { 28 | "hoist-non-inferno-statics": "^1.1.3", 29 | "inferno": "3.3.1", 30 | "inferno-component": "3.3.1", 31 | "inferno-create-element": "3.3.1", 32 | "inferno-shared": "3.1.1", 33 | "redux": "*" 34 | }, 35 | "devDependencies": { 36 | "inferno-router": "3.3.1" 37 | }, 38 | "rollup": { 39 | "bundledDependencies": [ 40 | "inferno-shared" 41 | ], 42 | "moduleName": "Inferno.Redux", 43 | "moduleGlobals": { 44 | "inferno": "Inferno", 45 | "inferno-component": "Inferno.Component", 46 | "inferno-create-element": "Inferno.createElement" 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/inferno-test-utils/__tests__/testutilevents.spec.jsx: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import Component from 'inferno-component'; 3 | import { spy } from 'sinon'; 4 | import { findRenderedVNodeWithType, renderIntoDocument } from '../dist-es'; 5 | 6 | describe('TestUtils events', () => { 7 | it('Should work with Synthetic events', () => { 8 | const testObj = { 9 | clicker: () => { 10 | } 11 | }; 12 | 13 | const sinonSpy = spy(testObj, 'clicker'); 14 | 15 | class FooBar extends Component { 16 | render() { 17 | return ( 18 |
19 | Test 20 |
21 | ); 22 | } 23 | } 24 | const tree = renderIntoDocument(); 25 | 26 | const vnode = findRenderedVNodeWithType(tree, 'div'); 27 | vnode.dom.click(); 28 | 29 | expect(sinonSpy.callCount).to.eql(1); 30 | }); 31 | 32 | it('Should work with native events', () => { 33 | const testObj = { 34 | clicker: () => { 35 | } 36 | }; 37 | 38 | const sinonSpy = spy(testObj, 'clicker'); 39 | 40 | class FooBar extends Component { 41 | render() { 42 | return ( 43 |
44 | Test 45 |
46 | ); 47 | } 48 | } 49 | const tree = renderIntoDocument(); 50 | 51 | const vnode = findRenderedVNodeWithType(tree, 'div'); 52 | vnode.dom.click(); 53 | 54 | expect(sinonSpy.callCount).to.eql(1); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /packages/inferno-devtools/README.md: -------------------------------------------------------------------------------- 1 | # Dev Tools 2 | 3 | Inferno has a development tools module which allows you to inspect the component hierarchies via the React Chrome Developer Tools plugin. You will get a new tab called React in your Chrome DevTools. This shows you the root Inferno components that were rendered on the page, as well as the subcomponents that they ended up rendering. 4 | 5 | By selecting one of the components in the tree, you can inspect and edit its current props and state in the panel on the right. In the breadcrumbs you can inspect the selected component, the component that created it, the component that created that one, and so on. If you inspect an Inferno element on the page using the regular Elements tab, then switch over to the React tab, that element will be automatically selected in the React tree. 6 | 7 | You can install the React Chrome Dev Tools plugin [here](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en). 8 | 9 | ## Enabling Inferno Dev Tools 10 | 11 | To enable the Inferno development tool you must install the `inferno-devtools` module and then `require('inferno-devtools')` before the initial `Inferno.render(..)`. 12 | 13 | The module translates Inferno components so that they are visible via the React Chrome plugin. Be sure to de-activate the tools in production as the development tool module adds extra overhead that will impact your application. -------------------------------------------------------------------------------- /packages/inferno-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inferno-server", 3 | "version": "3.3.1", 4 | "license": "MIT", 5 | "description": "Provides methods to render HTML strings from Inferno elements", 6 | "author": { 7 | "name": "Dominic Gannaway", 8 | "email": "dg@domgan.com" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/infernojs/inferno/issues" 12 | }, 13 | "homepage": "https://github.com/infernojs/inferno#readme", 14 | "keywords": [ 15 | "babel", 16 | "react", 17 | "inferno", 18 | "framework", 19 | "interfaces", 20 | "user interfaces", 21 | "html", 22 | "renderToString", 23 | "server", 24 | "dom", 25 | "browser", 26 | "rollup" 27 | ], 28 | "main": "index.js", 29 | "inferno:main": "dist-es/index.js", 30 | "typings": "dist/index.d.ts", 31 | "repository": "https://github.com/infernojs/inferno", 32 | "dependencies": { 33 | "inferno": "3.3.1", 34 | "inferno-shared": "3.1.1", 35 | "inferno-vnode-flags": "^3.0.0" 36 | }, 37 | "devDependencies": { 38 | "inferno-component": "3.3.1", 39 | "inferno-create-class": "3.3.1", 40 | "inferno-create-element": "3.3.1" 41 | }, 42 | "rollup": { 43 | "bundledDependencies": [ 44 | "inferno-shared", 45 | "inferno-vnode-flags" 46 | ], 47 | "moduleName": "Inferno.Server", 48 | "moduleGlobals": { 49 | "inferno": "Inferno" 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/inferno-test-utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inferno-test-utils", 3 | "version": "3.3.1", 4 | "license": "MIT", 5 | "description": "Suite of utilities for testing Inferno applications", 6 | "author": { 7 | "name": "Matthew Wagerfield", 8 | "email": "matthew@wagerfield.com" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/infernojs/inferno/issues" 12 | }, 13 | "homepage": "https://github.com/infernojs/inferno#readme", 14 | "keywords": [ 15 | "babel", 16 | "react", 17 | "inferno", 18 | "framework", 19 | "interfaces", 20 | "tests", 21 | "utils" 22 | ], 23 | "dependencies": { 24 | "inferno": "3.3.1", 25 | "inferno-create-class": "3.3.1", 26 | "inferno-create-element": "3.3.1", 27 | "inferno-shared": "3.1.1", 28 | "inferno-vnode-flags": "^3.0.0" 29 | }, 30 | "devDependencies": { 31 | "inferno-component": "3.3.1", 32 | "inferno-server": "3.3.1" 33 | }, 34 | "rollup": { 35 | "bundledDependencies": [ 36 | "inferno-shared", 37 | "inferno-vnode-flags" 38 | ], 39 | "moduleName": "Inferno.TestUtils", 40 | "moduleGlobals": { 41 | "inferno": "Inferno", 42 | "inferno-create-element": "Inferno.createElement" 43 | } 44 | }, 45 | "main": "index.js", 46 | "inferno:main": "dist-es/index.js", 47 | "typings": "dist/index.d.ts", 48 | "repository": "https://github.com/infernojs/inferno" 49 | } 50 | -------------------------------------------------------------------------------- /packages/inferno/src/DOM/wrappers/processElement.ts: -------------------------------------------------------------------------------- 1 | import { isNullOrUndef } from 'inferno-shared'; 2 | import VNodeFlags from 'inferno-vnode-flags'; 3 | import { VNode } from '../../core/VNodes'; 4 | import { isCheckedType, processInput } from './InputWrapper'; 5 | import { processSelect } from './SelectWrapper'; 6 | import { processTextarea } from './TextareaWrapper'; 7 | 8 | /** 9 | * There is currently no support for switching same input between controlled and nonControlled 10 | * If that ever becomes a real issue, then re design controlled elements 11 | * Currently user must choose either controlled or non-controlled and stick with that 12 | */ 13 | 14 | export function processElement(flags: number, vNode: VNode, dom: Element, nextPropsOrEmpty, mounting: boolean, isControlled: boolean): void { 15 | if (flags & VNodeFlags.InputElement) { 16 | processInput(vNode, dom, nextPropsOrEmpty, mounting, isControlled); 17 | } 18 | if (flags & VNodeFlags.SelectElement) { 19 | processSelect(vNode, dom, nextPropsOrEmpty, mounting, isControlled); 20 | } 21 | if (flags & VNodeFlags.TextareaElement) { 22 | processTextarea(vNode, dom, nextPropsOrEmpty, mounting, isControlled); 23 | } 24 | } 25 | 26 | export function isControlledFormElement(nextPropsOrEmpty): boolean { 27 | return (nextPropsOrEmpty.type && isCheckedType(nextPropsOrEmpty.type)) ? !isNullOrUndef(nextPropsOrEmpty.checked) : !isNullOrUndef(nextPropsOrEmpty.value); 28 | } 29 | -------------------------------------------------------------------------------- /packages/inferno-server/src/prop-renderers.ts: -------------------------------------------------------------------------------- 1 | import { internal_isUnitlessNumber } from 'inferno'; 2 | import { isNullOrUndef, isNumber, isStringOrNumber, isTrue } from 'inferno-shared'; 3 | import { escapeText, toHyphenCase } from './utils'; 4 | 5 | export function renderStyleToString(style): string|number { 6 | if (isStringOrNumber(style)) { 7 | return style; 8 | } else { 9 | const styles: string[] = []; 10 | 11 | for (const styleName in style) { 12 | const value = style[ styleName ]; 13 | const px = isNumber(value) && !internal_isUnitlessNumber.has(styleName) ? 'px' : ''; 14 | 15 | if (!isNullOrUndef(value)) { 16 | styles.push(`${ toHyphenCase(styleName) }:${ escapeText(value) }${ px };`); 17 | } 18 | } 19 | return styles.join(); 20 | } 21 | } 22 | 23 | export function renderAttributes(props): string[] { 24 | const outputAttrs: string[] = []; 25 | const propsKeys = (props && Object.keys(props)) || []; 26 | 27 | propsKeys.forEach((propKey, i) => { 28 | const value = props[ propKey ]; 29 | switch ( propKey ) { 30 | case 'children': 31 | case 'dangerouslySetInnerHTML': 32 | case 'style': 33 | return; 34 | default: 35 | if (isStringOrNumber(value)) { 36 | outputAttrs.push(escapeText(propKey) + '="' + escapeText(value) + '"'); 37 | } else if (isTrue(value)) { 38 | outputAttrs.push(escapeText(propKey)); 39 | } 40 | } 41 | }); 42 | 43 | return outputAttrs; 44 | } 45 | -------------------------------------------------------------------------------- /packages/inferno-mobx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inferno-mobx", 3 | "version": "3.3.1", 4 | "license": "MIT", 5 | "description": "Official Inferno bindings for Mobx", 6 | "author": { 7 | "name": "Ryan Megidov", 8 | "email": "ryan@megidov.com" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/infernojs/inferno/issues" 12 | }, 13 | "homepage": "https://github.com/infernojs/inferno#readme", 14 | "main": "index.js", 15 | "inferno:main": "dist-es/index.js", 16 | "typings": "dist/index.d.ts", 17 | "keywords": [ 18 | "babel", 19 | "react", 20 | "inferno", 21 | "framework", 22 | "interfaces", 23 | "reactive", 24 | "mobservable", 25 | "mobx" 26 | ], 27 | "repository": "https://github.com/infernojs/inferno", 28 | "dependencies": { 29 | "hoist-non-inferno-statics": "^1.1.3", 30 | "inferno": "3.3.1", 31 | "inferno-component": "3.3.1", 32 | "inferno-create-class": "3.3.1", 33 | "inferno-create-element": "3.3.1", 34 | "inferno-shared": "3.1.1", 35 | "mobx": "*" 36 | }, 37 | "rollup": { 38 | "bundledDependencies": [ 39 | "inferno-shared" 40 | ], 41 | "moduleName": "Inferno.Mobx", 42 | "moduleGlobals": { 43 | "inferno": "Inferno", 44 | "inferno-component": "Inferno.Component", 45 | "inferno-create-element": "Inferno.createElement", 46 | "inferno-create-class": "Inferno.createClass" 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/mocha/DOM.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | var jsdom = require("jsdom"); 3 | var JSDOM = jsdom.JSDOM; 4 | 5 | // We want to execute all scripts, because this is for test environment only 6 | var dom = new JSDOM("", { runScripts: "dangerously" }); 7 | global.window = dom.window; 8 | global.document = dom.window.document; 9 | global.navigator = dom.window.navigator; 10 | global.usingJSDOM = true; 11 | global.HTMLElement = dom.window.HTMLElement; 12 | 13 | global.chai = require("chai"); 14 | global.expect = global.chai.expect; 15 | global.sinon = require("sinon"); 16 | 17 | //JSDOM doesn't support localStorage by default, so lets just fake it.. 18 | if (!global.window.localStorage) { 19 | global.window.localStorage = { 20 | getItem() { return "{}"; }, 21 | setItem() {}, 22 | }; 23 | } 24 | 25 | // take all properties of the window object and also attach it to the 26 | // mocha global object 27 | propagateToGlobal(global.window); 28 | 29 | // from mocha-jsdom https://github.com/rstacruz/mocha-jsdom/blob/master/index.js#L80 30 | function propagateToGlobal (window) { 31 | for (var key in window) { 32 | if (!window.hasOwnProperty(key)) { 33 | continue; 34 | } 35 | if (key in global) { 36 | continue; 37 | } 38 | 39 | global[key] = window[key]; 40 | } 41 | } 42 | if (!global.requestAnimationFrame) { 43 | global.requestAnimationFrame = function (func) { 44 | setTimeout(func, 1000 / 60); 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /examples/routing/routing.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | const t = Inferno.createElement; 3 | const { Router } = Inferno.Router; 4 | const Route = Inferno.Router.Route; 5 | const Link = Inferno.Router.Link; 6 | const browserHistory = window.History.createBrowserHistory(); 7 | 8 | function App({ children }) { 9 | return t('div', null, 10 | t('h1', null, 'Routing Example'), 11 | t('p', null, `Below are the sub-children components for this "app", 12 | they will show and hide depending on the route. This example uses hashbangs only.`), 13 | t('p', null, 'Some links:'), 14 | t('ul', null, 15 | t('li', null, t(Link, { to: '/foo' }, 'Route to foo')), 16 | t('li', null, t(Link, { to: '/bar' }, 'Route to bar')) 17 | ), 18 | t('div', null, children ? children : null) 19 | ); 20 | } 21 | 22 | function Foo() { 23 | return ( 24 | t('div', null, 25 | t('h2', null, 'I am Foo'), 26 | t('p', null, 'I should only appear when you visit #!/foo') 27 | ) 28 | ); 29 | } 30 | 31 | function Bar() { 32 | return ( 33 | t('div', null, 34 | t('h2', null, 'I am Bar'), 35 | t('p', null, 'I should only appear when you visit #!/bar') 36 | ) 37 | ); 38 | } 39 | 40 | Inferno.render(( 41 | t(Router, { history: browserHistory }, 42 | t(Route, { component: App }, 43 | t(Route, { path: '/foo', component: Foo }), 44 | t(Route, { path: '/bar', component: Bar }) 45 | ) 46 | ) 47 | ), document.getElementById('app')); 48 | })(); 49 | -------------------------------------------------------------------------------- /packages/inferno-compat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inferno-compat", 3 | "version": "3.3.1", 4 | "license": "MIT", 5 | "description": "Provides a compatibility with React codebases", 6 | "author": { 7 | "name": "Dominic Gannaway", 8 | "email": "dg@domgan.com" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/infernojs/inferno/issues" 12 | }, 13 | "homepage": "https://github.com/infernojs/inferno#readme", 14 | "main": "index.js", 15 | "inferno:main": "dist-es/index.js", 16 | "typings": "dist/index.d.ts", 17 | "keywords": [ 18 | "babel", 19 | "react", 20 | "inferno", 21 | "framework", 22 | "interfaces", 23 | "user interfaces" 24 | ], 25 | "dependencies": { 26 | "inferno": "3.3.1", 27 | "inferno-component": "3.3.1", 28 | "inferno-create-class": "3.3.1", 29 | "inferno-create-element": "3.3.1", 30 | "inferno-shared": "3.1.1", 31 | "inferno-transition-group": "2.1.0", 32 | "inferno-vnode-flags": "^3.0.0", 33 | "rc-css-transition-group-modern": "^2.1.6" 34 | }, 35 | "devDependencies": { 36 | "inferno-hyperscript": "3.3.1" 37 | }, 38 | "rollup": { 39 | "bundledDependencies": [ 40 | "inferno-shared", 41 | "inferno-vnode-flags", 42 | "inferno-create-class", 43 | "inferno-create-element" 44 | ], 45 | "moduleName": "Inferno", 46 | "moduleGlobals": { 47 | "inferno": "Inferno", 48 | "inferno-component": "Inferno.Component" 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/inferno-redux/src/utils.ts: -------------------------------------------------------------------------------- 1 | import { bindActionCreators } from 'redux'; 2 | 3 | /** 4 | * Prints a warning in the console if it exists. 5 | * 6 | * @param {String} message The warning message. 7 | * @returns {void} 8 | */ 9 | export function warning(message) { 10 | /* eslint-disable no-console */ 11 | if (typeof console !== 'undefined' && typeof console.error === 'function') { 12 | // tslint:disable-next-line:no-console 13 | console.error(message); 14 | } 15 | 16 | try { 17 | // This error was thrown as a convenience so that if you enable 18 | // "break on all exceptions" in your console, 19 | // it would pause the execution at this line. 20 | throw new Error(message); 21 | 22 | // tslint:disable-next-line:no-empty 23 | } catch ( e ) {} 24 | } 25 | 26 | export function shallowEqual(objA, objB) { 27 | if (objA === objB) { 28 | return true; 29 | } 30 | const keysA = Object.keys(objA); 31 | const keysB = Object.keys(objB); 32 | 33 | if (keysA.length !== keysB.length) { 34 | return false; 35 | } 36 | // Test for A's keys different from B. 37 | const hasOwn = Object.prototype.hasOwnProperty; 38 | for (let i = 0, len = keysA.length; i < len; i++) { 39 | const key = keysA[ i ]; 40 | 41 | if (!hasOwn.call(objB, key) || 42 | objA[ key ] !== objB[ key ]) { 43 | return false; 44 | } 45 | } 46 | return true; 47 | } 48 | 49 | export function wrapActionCreators(actionCreators) { 50 | return (dispatch) => bindActionCreators(actionCreators, dispatch); 51 | } 52 | -------------------------------------------------------------------------------- /packages/inferno-router/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inferno-router", 3 | "version": "3.3.1", 4 | "license": "MIT", 5 | "description": "Provides routing functionality for Inferno", 6 | "author": { 7 | "name": "Dominic Gannaway", 8 | "email": "dg@domgan.com" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/infernojs/inferno/issues" 12 | }, 13 | "homepage": "https://github.com/infernojs/inferno#readme", 14 | "main": "index.js", 15 | "inferno:main": "dist-es/index.js", 16 | "typings": "dist/index.d.ts", 17 | "keywords": [ 18 | "babel", 19 | "react", 20 | "inferno", 21 | "framework", 22 | "interfaces", 23 | "user interfaces", 24 | "router" 25 | ], 26 | "repository": "https://github.com/infernojs/inferno", 27 | "dependencies": { 28 | "history": "^4.6.1", 29 | "inferno": "3.3.1", 30 | "inferno-component": "3.3.1", 31 | "inferno-create-element": "3.3.1", 32 | "inferno-shared": "3.1.1", 33 | "inferno-vnode-flags": "^3.0.0", 34 | "path-to-regexp": "^1.7.0", 35 | "path-to-regexp-es6": "0.0.2" 36 | }, 37 | "rollup": { 38 | "bundledDependencies": [ 39 | "inferno-shared", 40 | "path-to-regexp-es6" 41 | ], 42 | "moduleName": "Inferno.Router", 43 | "moduleGlobals": { 44 | "inferno": "Inferno", 45 | "inferno-component": "Inferno.Component", 46 | "inferno-create-element": "Inferno.createElement", 47 | "path-to-regexp-es6": "Inferno.pathToRegexp" 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/devtools/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Inferno CSS transform example 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /packages/inferno/__tests__/rendering.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { createVNode, render } from '../dist-es'; 3 | import { NO_OP } from 'inferno-shared'; 4 | import VNodeFlags from 'inferno-vnode-flags'; 5 | 6 | describe('rendering routine', () => { 7 | let container; 8 | 9 | beforeEach(function () { 10 | container = document.createElement('div'); 11 | document.body.appendChild(container); 12 | }); 13 | 14 | afterEach(function () { 15 | render(null, container); 16 | container.innerHTML = ''; 17 | document.body.removeChild(container); 18 | }); 19 | 20 | it('Should throw error when trying to render to document.body', () => { 21 | const div = createVNode(VNodeFlags.Element, 'div', null, '1', null, null, null, true); 22 | try { 23 | render(div, document.body); 24 | } catch (e) { 25 | expect(e.message).to.eql('Inferno Error: you cannot render() to the "document.body". Use an empty element as a container instead.'); 26 | } 27 | }); 28 | 29 | it('Should do nothing if input is NO-OP', () => { 30 | render(NO_OP, container); 31 | expect(container.innerHTML).to.eql(''); 32 | }); 33 | 34 | it('Should create new object when dom exists', () => { 35 | const bar = createVNode(2, 'div', null, '123', null, null, null, true); 36 | const foo = createVNode(2, 'div', null, bar, null, null, null, true); 37 | 38 | render(foo, container); 39 | expect(container.innerHTML).to.eql('
123
'); 40 | 41 | render(null, container); 42 | 43 | render(foo, container); 44 | expect(container.innerHTML).to.eql('
123
'); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /benchmarks/async-render/app.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | "use strict"; 3 | /* (flags, type, props, children, key, ref, noNormalise) */ 4 | Inferno.options.recyclingEnabled = true; // Advanced optimisation 5 | var createVNode = Inferno.createVNode; 6 | var Component = Inferno.Component; 7 | var createElement = Inferno.createElement; 8 | 9 | class List extends Component { 10 | constructor() { 11 | super(); 12 | // set initial time: 13 | this.state = { 14 | items: [] 15 | }; 16 | this.items = []; 17 | } 18 | 19 | componentDidMount() { 20 | while (this.items.length < 20000) { 21 | this.items[this.items.length] = createElement('li', null, `${this.items.length}bar`); 22 | this.setState({ items: this.items }); 23 | } 24 | } 25 | 26 | render() { 27 | return createElement('ul', null, this.state.items); 28 | } 29 | } 30 | 31 | document.addEventListener('DOMContentLoaded', function () { 32 | var container = document.querySelector('#App'); 33 | 34 | const times = []; 35 | const count = 100; 36 | let totalTime = 0; 37 | 38 | for (var i = 0; i < count; i++) { 39 | var start = window.performance.now(); 40 | 41 | Inferno.render(createElement(List), container); 42 | 43 | var end = window.performance.now(); 44 | 45 | Inferno.render(null, container); 46 | 47 | var roundTime = end - start; 48 | totalTime += roundTime; 49 | 50 | times.push(roundTime); 51 | } 52 | 53 | Inferno.render(createElement('div', null, ` 54 | Rounds: ${count}, 55 | Average: ${totalTime / count}, 56 | Total: ${totalTime} 57 | `), container); 58 | }); 59 | })(); 60 | -------------------------------------------------------------------------------- /packages/inferno-redux/__tests__/utils.spec.jsx: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { stub } from 'sinon'; 3 | import { shallowEqual, warning } from '../dist-es/utils'; 4 | 5 | describe('Redux Utils', () => { 6 | describe('warning', () => { 7 | it('should log message using console.error', () => { 8 | // stub instead of spy to prevent console.error actually firing during test 9 | const stubFn = stub(console, 'error').callsFake((message) => null); 10 | warning('warning!'); 11 | expect(stubFn.calledOnce).to.equal(true); 12 | expect(stubFn.calledWith('warning!')).to.equal(true); 13 | stubFn.restore(); 14 | }); 15 | }); 16 | describe('shallowEqual', () => { 17 | it('should return true if two objects are strictly equal', () => { 18 | const obj = { key: 'value' }; 19 | expect(shallowEqual(obj, obj)).to.equal(true); 20 | }); 21 | it('should return false if two objects have different key lengths', () => { 22 | const obj1 = { key: 'value' }; 23 | const obj2 = { key: 'value', key2: 'value2' }; 24 | expect(shallowEqual(obj1, obj2)).to.equal(false); 25 | }); 26 | it('should return false if two objects have different key values', () => { 27 | const obj1 = { key: 'value', key2: 'value3' }; 28 | const obj2 = { key: 'value', key2: 'value2' }; 29 | expect(shallowEqual(obj1, obj2)).to.equal(false); 30 | }); 31 | it('should return true if two objects have same keys and values', () => { 32 | const obj1 = { key: 'value', key2: 'value2' }; 33 | const obj2 = { key: 'value', key2: 'value2' }; 34 | expect(shallowEqual(obj1, obj2)).to.equal(true); 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /packages/inferno-compat/__tests__/svg.spec.jsx: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { render } from 'inferno'; 3 | import { createElement } from '../dist-es'; 4 | 5 | describe('svg', () => { 6 | let container; 7 | 8 | beforeEach(function () { 9 | container = document.createElement('div'); 10 | }); 11 | 12 | afterEach(function () { 13 | render(null, container); 14 | }); 15 | 16 | it('Should work with normal svg attributes', () => { 17 | render(createElement('svg', { 18 | height: '16', 19 | width: '16', 20 | viewBox: '0 0 1024 1024' 21 | }, [ 22 | createElement('stop', { 23 | offset: 0, 24 | stopColor: 'white', 25 | stopOpacity: 0.5 26 | }) 27 | ]), container); 28 | 29 | expect(container.firstChild.getAttribute('viewBox')).to.equal('0 0 1024 1024'); 30 | expect(container.firstChild.getAttribute('height')).to.equal('16'); 31 | expect(container.firstChild.getAttribute('width')).to.equal('16'); 32 | expect(container.firstChild.firstChild.tagName).to.equal('stop'); 33 | expect(container.firstChild.firstChild.getAttribute('stop-color')).to.equal('white'); 34 | expect(container.firstChild.firstChild.getAttribute('stop-opacity')).to.equal('0.5'); 35 | }); 36 | 37 | it('Should work with namespace svg attributes', () => { 38 | render(createElement('svg', null, [ 39 | createElement('image', { 40 | xlinkHref: 'http://i.imgur.com/w7GCRPb.png' 41 | }) 42 | ]), container); 43 | 44 | expect(container.firstChild.firstChild.tagName).to.equal('image'); 45 | expect(container.firstChild.firstChild.getAttribute('xlink:href')).to.equal('http://i.imgur.com/w7GCRPb.png'); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /packages/inferno-router/src/Link.ts: -------------------------------------------------------------------------------- 1 | import { createVNode, VNode } from 'inferno'; 2 | import { combineFrom, isBrowser, warning } from 'inferno-shared'; 3 | import VNodeFlags from 'inferno-vnode-flags'; 4 | 5 | function renderLink(classNm, children, otherProps) { 6 | return createVNode(VNodeFlags.HtmlElement, 'a', classNm, children, otherProps); 7 | } 8 | 9 | export default function Link(props, { router }): VNode { 10 | const { activeClassName, activeStyle, className, onClick, children, to, ...otherProps } = props; 11 | 12 | let classNm; 13 | if (className) { 14 | classNm = className as string; 15 | } 16 | 17 | if (!router) { 18 | if (process.env.NODE_ENV !== 'production') { 19 | warning(' component used outside of . Fallback to tag.'); 20 | } 21 | 22 | otherProps.href = to; 23 | otherProps.onClick = onClick; 24 | 25 | return renderLink(classNm, children, otherProps); 26 | } 27 | 28 | otherProps.href = isBrowser ? router.createHref({ pathname: to }) : router.location.baseUrl ? router.location.baseUrl + to : to; 29 | 30 | if (router.location.pathname === to) { 31 | if (activeClassName) { 32 | classNm = (className ? className + ' ' : '') + activeClassName; 33 | } 34 | if (activeStyle) { 35 | otherProps.style = combineFrom(props.style, activeStyle); 36 | } 37 | } 38 | 39 | otherProps.onclick = function navigate(e) { 40 | if (e.button !== 0 || e.ctrlKey || e.altKey || e.metaKey || e.shiftKey) { 41 | return; 42 | } 43 | e.preventDefault(); 44 | if (typeof onClick === 'function') { 45 | onClick(e); 46 | } 47 | router.push(to, e.target.textContent); 48 | }; 49 | 50 | return renderLink(classNm, children, otherProps); 51 | } 52 | -------------------------------------------------------------------------------- /test/karma/karma.unit.conf.js: -------------------------------------------------------------------------------- 1 | /* global module */ 2 | /* tslint:disable */ 3 | 4 | const path = require('path'); 5 | const base = require('./karma.base.conf'); 6 | 7 | module.exports = function (config) { 8 | base(config); 9 | 10 | config.set({ 11 | frameworks: [ 12 | 'mocha', 13 | 'chai' 14 | ], 15 | files: [ 16 | require.resolve('es5-shim'), 17 | require.resolve('es6-shim'), 18 | require.resolve('babel-polyfill/dist/polyfill'), 19 | require.resolve('sinon/pkg/sinon'), 20 | `packages/${process.env.PKG_FILTER || '*'}/__tests__/**/*.js*` 21 | ], 22 | reporters: [ 23 | 'failed' 24 | ] 25 | }); 26 | 27 | 28 | const { 29 | CI, 30 | TRAVIS_BRANCH, 31 | TRAVIS_BUILD_NUMBER, 32 | TRAVIS_JOB_NUMBER, 33 | TRAVIS_PULL_REQUEST 34 | } = process.env; 35 | 36 | const ci = String(CI).match(/^(1|true)$/gi); 37 | const pullRequest = !String(TRAVIS_PULL_REQUEST).match(/^(0|false|undefined)$/gi); 38 | const masterBranch = String(TRAVIS_BRANCH).match(/^master$/gi); 39 | const sauce = ci && !pullRequest && masterBranch; 40 | 41 | if (ci) { 42 | config.set({ 43 | reporters: [ 44 | 'failed' 45 | ] 46 | }); 47 | } 48 | 49 | const varToBool = (sVar) => !!String(sVar).match('true'); 50 | if (sauce) { 51 | const sauceLaunchers = require('./sauce'); 52 | config.set({ 53 | sauceLabs: { 54 | testName: 'Inferno Browser Karma Tests: ' + TRAVIS_JOB_NUMBER, 55 | build: (TRAVIS_JOB_NUMBER || 'Local'), 56 | tags: [(TRAVIS_BRANCH || 'master')] 57 | }, 58 | customLaunchers: sauceLaunchers.launchers, 59 | browsers: sauceLaunchers.browsers, 60 | reporters: [ 61 | 'failed', 62 | 'saucelabs' 63 | ] 64 | }); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /fixtures/packaging/README.md: -------------------------------------------------------------------------------- 1 | # Manual Testing Fixtures 2 | 3 | Thanks for React! 4 | Idea for this test has been adopted from React/fixtures/packaging 5 | 6 | This folder exists for **Inferno contributors** only. 7 | 8 | These fixtures verify that the built Inferno distributions are usable in different environments. 9 | TODO: 10 | fix brunch, rjs, system-builder, webpack, webpack-alias 11 | add rollup 12 | all of them should test minified / dev versions 13 | 14 | **They are not running automatically.** (At least not yet, feel free to contribute to automate them.) 15 | 16 | Run them when you make changes to how we package React and ReactDOM. 17 | 18 | ## How to Run 19 | 20 | First, build React and the fixtures: 21 | 22 | ``` 23 | cd inferno 24 | npm run build 25 | 26 | cd fixtures/packaging 27 | node build-all.js 28 | ``` 29 | 30 | Then run a local server at the root of the repo, e.g. 31 | 32 | ``` 33 | npm i -g pushstate-server 34 | cd ../.. 35 | pushstate-server . 36 | ``` 37 | 38 | (Too complicated? Send a PR to simplify this :-). 39 | 40 | Then open the corresponding URLs, for example: 41 | 42 | ``` 43 | open http://localhost:9000/fixtures/globals.html 44 | open http://localhost:9000/fixtures/requirejs.html 45 | open http://localhost:9000/fixtures/systemjs.html 46 | open http://localhost:9000/fixtures/browserify/index.html 47 | open http://localhost:9000/fixtures/brunch/index.html 48 | open http://localhost:9000/fixtures/rjs/index.html 49 | open http://localhost:9000/fixtures/systemjs-builder/index.html 50 | open http://localhost:9000/fixtures/webpack/index.html 51 | open http://localhost:9000/fixtures/webpack-alias/index.html 52 | ``` 53 | 54 | You should see two things: 55 | 56 | * "Develpment and Production" is rendered. 57 | * There is single error in console one for stating production build is used and one with normal error message 58 | -------------------------------------------------------------------------------- /packages/inferno-hyperscript/README.md: -------------------------------------------------------------------------------- 1 | # inferno-hyperscript [![Travis-CI Status](https://img.shields.io/travis/terinjokes/inferno-hyperscript/master.svg?label=Travis%20CI&style=flat-square)](https://travis-ci.org/terinjokes/inferno-hyperscript) [![](https://img.shields.io/npm/dm/inferno-hyperscript.svg?style=flat-square)](https://www.npmjs.org/package/inferno-hyperscript) [![](https://img.shields.io/npm/v/inferno-hyperscript.svg?style=flat-square)](https://www.npmjs.org/package/inferno-hyperscript) [![](https://img.shields.io/coveralls/terinjokes/inferno-hyperscript/master.svg?style=flat-square)](https://coveralls.io/github/terinjokes/inferno-hyperscript) 2 | > [Hyperscript][hyperscript] syntax for [Inferno][inferno] termplates. 3 | 4 | ## Usage 5 | 6 | ```javascript 7 | var h = require('inferno-hyperscript'); 8 | 9 | module.exports = function ExampleComponent(props) { 10 | return h('.example', [ 11 | h('a.example-link', { 12 | href: '#' 13 | }, [ 14 | 'Hello', 15 | props.whom, 16 | '!' 17 | ]) 18 | ]); 19 | }; 20 | ``` 21 | 22 | ## Documentation 23 | 24 | ### `h(componentOrTag, properties, children)` 25 | 26 | Returns an Inferno VNode from a Hyperscript representation. 27 | 28 | * **componentOrTag** `(Object|String)` can be an Inferno component **OR** tag string with optional css class names and ids in the format `h1#some-id.foo.bar`. 29 | If a tag string, the tag name is parsed out, and the `id` and `className` propertires of the properties argument will be modified. 30 | * **properties** `(Object)` *(optional)* An object containing the properties you'd like to set on the element. 31 | * **children** `(Array|String)` *(optional)* An array of `h()` children or strings, This will create childen or text nodes respectively. 32 | 33 | [hyperscript]: https://github.com/dominictarr/hyperscript 34 | [inferno]: https://github.com/infernojs/inferno 35 | -------------------------------------------------------------------------------- /packages/inferno-mobx/src/connect.ts: -------------------------------------------------------------------------------- 1 | import Component from 'inferno-component'; 2 | import createClass from 'inferno-create-class'; 3 | import { throwError } from 'inferno-shared'; 4 | import inject from './inject'; 5 | import makeReactive from './makeReactive'; 6 | 7 | /** 8 | * Wraps a component and provides stores as props 9 | */ 10 | function connect(arg1: any, arg2?: any): any { 11 | if (typeof arg1 === 'string') { 12 | throwError('Store names should be provided as array'); 13 | } 14 | 15 | if (Array.isArray(arg1)) { 16 | // component needs stores 17 | if (!arg2) { 18 | // invoked as decorator 19 | return (componentClass) => connect(arg1, componentClass); 20 | } else { 21 | // TODO: deprecate this invocation style 22 | return inject.apply(null, arg1)(connect(arg2)); 23 | } 24 | } 25 | const componentClass = arg1; 26 | 27 | // Stateless function component: 28 | // If it is function but doesn't seem to be a Inferno class constructor, 29 | // wrap it to a Inferno class automatically 30 | if (typeof componentClass === 'function' 31 | && (!componentClass.prototype || !componentClass.prototype.render) 32 | && !componentClass.isReactClass 33 | && !Component.isPrototypeOf(componentClass) 34 | ) { 35 | const newClass = createClass({ 36 | contextTypes: componentClass.contextTypes, 37 | displayName: componentClass.displayName || componentClass.name, 38 | getDefaultProps: () => componentClass.defaultProps, 39 | propTypes: componentClass.propTypes, 40 | render() { 41 | return componentClass.call(this, this.props, this.context); 42 | } 43 | }); 44 | 45 | return connect(newClass); 46 | } 47 | 48 | if (!componentClass) { 49 | throwError('Please pass a valid component to "connect"'); 50 | } 51 | 52 | componentClass.isMobXReactObserver = true; 53 | return makeReactive(componentClass); 54 | } 55 | 56 | export default connect; 57 | -------------------------------------------------------------------------------- /fixtures/packaging/browserify/input.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | var Inferno = require('inferno'); 3 | var Component = require('inferno-component'); 4 | 5 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6 | 7 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 8 | 9 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 10 | 11 | var isProduction = process.env.NODE_ENV === 'production'; 12 | 13 | var Example = function (_Component) { 14 | _inherits(Example, _Component); 15 | 16 | function Example() { 17 | _classCallCheck(this, Example); 18 | 19 | return _possibleConstructorReturn(this, _Component.apply(this, arguments)); 20 | } 21 | 22 | Example.prototype.render = function render() { 23 | return Inferno.createVNode(2, 'h1', null, isProduction ? 'PRODUCTION' : 'DEVELOPMENT'); 24 | }; 25 | 26 | return Example; 27 | }(Component); 28 | 29 | Inferno.render( 30 | Inferno.createVNode(16, Example), 31 | document.getElementById(isProduction ? 'prod' : 'dev') 32 | ); 33 | 34 | // Wait 1 second to verify error message 35 | setTimeout(function () { 36 | Inferno.render( 37 | {error: 'test'}, 38 | document.getElementById(isProduction ? 'prod' : 'dev') 39 | ) 40 | }, 1000); 41 | -------------------------------------------------------------------------------- /benchmarks/dbmonster/style.css: -------------------------------------------------------------------------------- 1 | body {color:#333;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;margin:0;} 2 | label {display:inline-block;font-weight:700;margin-bottom:5px;} 3 | input[type=range] {display:block;width:100%;} 4 | table {border-collapse:collapse;border-spacing:0;} 5 | :before,:after {box-sizing: border-box;} 6 | 7 | .table > thead > tr > th,.table > tbody > tr > th,.table > tfoot > tr > th,.table > thead > tr > td,.table > tbody > tr > td,.table > tfoot > tr > td {border-top:1px solid #ddd;line-height:1.42857143;padding:8px;vertical-align:top;} 8 | .table {width:100%;} 9 | .table-striped > tbody > tr:nth-child(odd) > td,.table-striped > tbody > tr:nth-child(odd) > th {background:#f9f9f9;} 10 | 11 | .label {border-radius:.25em;color:#fff;display:inline;font-size:75%;font-weight:700;line-height:1;padding:.2em .6em .3em;text-align:center;vertical-align:baseline;white-space:nowrap;} 12 | .label-success {background-color:#5cb85c;} 13 | .label-warning {background-color:#f0ad4e;} 14 | 15 | .popover {background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,.2);display:none;left:0;max-width:276px;padding:1px;position:absolute;text-align:left;top:0;white-space:normal;z-index:1010;} 16 | .popover>.arrow:after {border-width:10px;content:"";} 17 | .popover.left {margin-left:-10px;} 18 | .popover.left > .arrow {border-right-width:0;border-left-color:rgba(0,0,0,.25);margin-top:-11px;right:-11px;top:50%;} 19 | .popover.left > .arrow:after {border-left-color:#fff;border-right-width:0;bottom:-10px;content:" ";right:1px;} 20 | .popover > .arrow {border-width:11px;} 21 | .popover > .arrow,.popover>.arrow:after {border-color:transparent;border-style:solid;display:block;height:0;position:absolute;width:0;} 22 | 23 | .popover-content {padding:9px 14px;} 24 | 25 | .Query {position:relative;} 26 | .Query:hover .popover {display:block;left:-100%;width:100%;} -------------------------------------------------------------------------------- /packages/inferno-compat/lib/EventConstants.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Types of raw signals from the browser caught at the top level. 5 | */ 6 | var topLevelTypes = { 7 | topAbort: null, 8 | topAnimationEnd: null, 9 | topAnimationIteration: null, 10 | topAnimationStart: null, 11 | topBlur: null, 12 | topCanPlay: null, 13 | topCanPlayThrough: null, 14 | topChange: null, 15 | topClick: null, 16 | topCompositionEnd: null, 17 | topCompositionStart: null, 18 | topCompositionUpdate: null, 19 | topContextMenu: null, 20 | topCopy: null, 21 | topCut: null, 22 | topDoubleClick: null, 23 | topDrag: null, 24 | topDragEnd: null, 25 | topDragEnter: null, 26 | topDragExit: null, 27 | topDragLeave: null, 28 | topDragOver: null, 29 | topDragStart: null, 30 | topDrop: null, 31 | topDurationChange: null, 32 | topEmptied: null, 33 | topEncrypted: null, 34 | topEnded: null, 35 | topError: null, 36 | topFocus: null, 37 | topInput: null, 38 | topInvalid: null, 39 | topKeyDown: null, 40 | topKeyPress: null, 41 | topKeyUp: null, 42 | topLoad: null, 43 | topLoadedData: null, 44 | topLoadedMetadata: null, 45 | topLoadStart: null, 46 | topMouseDown: null, 47 | topMouseMove: null, 48 | topMouseOut: null, 49 | topMouseOver: null, 50 | topMouseUp: null, 51 | topPaste: null, 52 | topPause: null, 53 | topPlay: null, 54 | topPlaying: null, 55 | topProgress: null, 56 | topRateChange: null, 57 | topReset: null, 58 | topScroll: null, 59 | topSeeked: null, 60 | topSeeking: null, 61 | topSelectionChange: null, 62 | topStalled: null, 63 | topSubmit: null, 64 | topSuspend: null, 65 | topTextInput: null, 66 | topTimeUpdate: null, 67 | topTouchCancel: null, 68 | topTouchEnd: null, 69 | topTouchMove: null, 70 | topTouchStart: null, 71 | topTransitionEnd: null, 72 | topVolumeChange: null, 73 | topWaiting: null, 74 | topWheel: null, 75 | }; 76 | 77 | var EventConstants = { 78 | topLevelTypes: topLevelTypes 79 | }; 80 | 81 | module.exports = EventConstants; 82 | -------------------------------------------------------------------------------- /packages/inferno-mobx/src/Provider.ts: -------------------------------------------------------------------------------- 1 | import Component from 'inferno-component'; 2 | import { warning } from 'inferno-shared'; 3 | 4 | const specialKeys = { 5 | children: true, 6 | key: true, 7 | ref: true 8 | }; 9 | 10 | export default class Provider extends Component { 11 | public contextTypes: any = { 12 | // tslint:disable-next-line:no-empty 13 | mobxStores() {} 14 | }; 15 | public childContextTypes: any = { 16 | // tslint:disable-next-line:no-empty 17 | mobxStores() {} 18 | }; 19 | private store: any; 20 | 21 | constructor(props?: any, context?: any) { 22 | super(props, context); 23 | this.store = props.store; 24 | } 25 | 26 | public render() { 27 | return this.props.children; 28 | } 29 | 30 | public getChildContext() { 31 | const stores = {}; 32 | // inherit stores 33 | const baseStores = this.context.mobxStores; 34 | 35 | if (baseStores) { 36 | for (const key in baseStores) { 37 | stores[ key ] = baseStores[ key ]; 38 | } 39 | } 40 | // add own stores 41 | for (const key in this.props) { 42 | if (!specialKeys[ key ]) { 43 | stores[ key ] = this.props[ key ]; 44 | } 45 | } 46 | return { 47 | mobxStores: stores 48 | }; 49 | } 50 | } 51 | 52 | if (process.env.NODE_ENV !== 'production') { 53 | Provider.prototype.componentWillReceiveProps = function(nextProps) { 54 | 55 | // Maybe this warning is to aggressive? 56 | if (Object.keys(nextProps).length !== Object.keys(this.props).length) { 57 | warning( 58 | 'MobX Provider: The set of provided stores has changed. ' + 59 | 'Please avoid changing stores as the change might not propagate to all children' 60 | ); 61 | } 62 | for (const key in nextProps) { 63 | if (!specialKeys[ key ] && this.props[ key ] !== nextProps[ key ]) { 64 | warning( 65 | `MobX Provider: Provided store '${key}' has changed. ` + 66 | `Please avoid replacing stores as the change might not propagate to all children` 67 | ); 68 | } 69 | } 70 | }; 71 | } 72 | -------------------------------------------------------------------------------- /benchmarks/dbmonster/app.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | "use strict"; 3 | var elem = document.getElementById('app'); 4 | Inferno.options.recyclingEnabled = true; // Advanced optimisation 5 | 6 | perfMonitor.startFPSMonitor(); 7 | perfMonitor.startMemMonitor(); 8 | perfMonitor.initProfiler('view update'); 9 | 10 | var createVNode = Inferno.createVNode; 11 | 12 | function renderBenchmark(dbs) { 13 | var length = dbs.length; 14 | var databases = new Array(length); 15 | 16 | for (var i = 0; i < length; i++) { 17 | var db = dbs[i]; 18 | var lastSample = db.lastSample; 19 | var children = new Array(7); 20 | 21 | children[0] = createVNode(2, 'td', 'dbname', db.dbname, null, null, null, true); 22 | children[1] = createVNode(2, 'td', 'query-count', 23 | createVNode(2, 'span', lastSample.countClassName, lastSample.nbQueries, null, null, null, true), 24 | null, null, null, true); 25 | 26 | for (var i2 = 0; i2 < 5; i2++) { 27 | var query = lastSample.topFiveQueries[i2]; 28 | 29 | children[i2 + 2] = createVNode(66, 'td', query.elapsedClassName, [ 30 | createVNode(2, 'div', 'foo', query.formatElapsed, null, null, null, true), 31 | createVNode(66, 'div', 'popover left', [ 32 | createVNode(2, 'div', 'popover-content', query.query, null, null, null, true), 33 | createVNode(2, 'div', 'arrow', null, null, null, null, true) 34 | ], null, null, null, true) 35 | ], null, null, null, true); 36 | } 37 | databases[i] = createVNode(66, 'tr', null, children, null, null, null, true); 38 | } 39 | 40 | Inferno.render( 41 | createVNode(2, 'table', 'table table-striped latest-data', createVNode(66, 'tbody', null, databases, null, null, null, true), null, null, null, true), 42 | elem); 43 | } 44 | 45 | function render() { 46 | var dbs = ENV.generateData(false).toArray(); 47 | perfMonitor.startProfile('view update'); 48 | renderBenchmark(dbs); 49 | perfMonitor.endProfile('view update'); 50 | setTimeout(render, ENV.timeout); 51 | } 52 | render(); 53 | })(); 54 | -------------------------------------------------------------------------------- /config/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tests 5 | 6 | 7 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | 25 | 52 | 53 | 54 |
55 | 56 | 57 | -------------------------------------------------------------------------------- /test/karma/bench.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | Object.entries = Object.entries || require('object.entries'); 4 | 5 | function loadResult(file, cb) { 6 | try { 7 | const result = require(path.join(__dirname, '..', 'data', file)); 8 | cb(null, result); 9 | } catch (e) { 10 | cb(e, null); 11 | } 12 | } 13 | 14 | function calculateResult(testName, resultOps, baselineOps) { 15 | const testFailure = (baselineOps <= resultOps) || Math.abs(baselineOps - resultOps) <= baselineOps * 0.1; 16 | const testResultMessage = testFailure ? 'PASSED' : 'FAILED'; 17 | 18 | const message = testResultMessage + ': ' + testName + ' performs ' + resultOps + ' ops/s. Baseline: ' + baselineOps + ' ops/s'; 19 | console.log(message); 20 | 21 | return testFailure; 22 | } 23 | 24 | loadResult('baseline.json', (baselineErr, baseline) => { 25 | loadResult('result.json', (testResultErr, testResult) => { 26 | if (baseline === null && testResult !== null) { 27 | const newbaselines = JSON.stringify(testResult); 28 | return fs.writeFile(path.join(__dirname, '..', 'data', 'baseline.json'), newbaselines, (err) => { 29 | if (err) { 30 | throw new Error(err); 31 | } 32 | console.log('New baseline written'); 33 | process.exit(0); 34 | }); 35 | } else if (baseline === null) { 36 | throw new Error(baselineErr); 37 | } else if (testResult === null) { 38 | throw new Error(testResultErr); 39 | } 40 | 41 | const baselineTests = Object.entries(baseline); 42 | if (baselineTests.length !== Object.keys(testResult).length) { 43 | throw new Error('Inconsistent number of tests, please regenerate baseline'); 44 | } 45 | 46 | let failed = false; 47 | if (failed) { 48 | return process.exit(1); 49 | } 50 | 51 | for (let i = 0; i < baselineTests.length; i += 1) { 52 | const [ testName, baselineResult ] = baselineTests[i]; 53 | const resultMeta = testResult[ testName ]; 54 | const result = calculateResult(testName, parseInt(1 / resultMeta.time , 10), parseInt(1 / baselineResult.time , 10)); 55 | 56 | if (!result) { 57 | failed = true; 58 | } 59 | } 60 | 61 | if (failed) { 62 | return process.exit(1); 63 | } 64 | 65 | return process.exit(0); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /packages/inferno-create-element/__benchmarks__/functionalcomponents.js: -------------------------------------------------------------------------------- 1 | import { createVNode, render } from 'inferno'; 2 | import InfernoVNodeFlags from 'inferno-vnode-flags'; 3 | 4 | suite('functional components', function() { 5 | benchmark('single functional component (with div) + render', function() { 6 | function Com() { 7 | return createVNode(InfernoVNodeFlags.Element, 'div', null, '1'); 8 | } 9 | 10 | render(createVNode(InfernoVNodeFlags.ComponentFunction, Com), this.testDiv); 11 | }, { 12 | setup: function() { 13 | this.testDiv = document.createElement('div'); 14 | }, 15 | 16 | teardown: function() { 17 | this.testDiv.innerHTML = ''; 18 | } 19 | }); 20 | 21 | benchmark('20 children functional components', function() { 22 | 23 | function Com() { 24 | return createVNode(InfernoVNodeFlags.Element, 'div', null, '1'); 25 | } 26 | 27 | render(createVNode(InfernoVNodeFlags.ComponentFunction, Com, null, [ 28 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 29 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 30 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 31 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 32 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 33 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 34 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 35 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 36 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 37 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 38 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 39 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 40 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 41 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 42 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 43 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 44 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 45 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 46 | createVNode(InfernoVNodeFlags.ComponentFunction, Com), 47 | createVNode(InfernoVNodeFlags.ComponentFunction, Com) 48 | ]), this.testDiv); 49 | }, { 50 | setup: function() { 51 | this.testDiv = document.createElement('div'); 52 | }, 53 | 54 | teardown: function() { 55 | this.testDiv.innerHTML = ''; 56 | } 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /packages/inferno-test-utils/src/jest.ts: -------------------------------------------------------------------------------- 1 | import { VNode } from 'inferno'; 2 | import { isArray, isNull, isObject, isString } from 'inferno-shared'; 3 | import { getTagNameOfVNode, isDOMVNode, renderIntoDocument } from './index'; 4 | 5 | // Jest Snapshot Utilities 6 | // Jest formats it's snapshots prettily because it knows how to play with the React test renderer. 7 | // Symbols and algorithm have been reversed from the following file: 8 | // https://github.com/facebook/react/blob/v15.4.2/src/renderers/testing/ReactTestRenderer.js#L98 9 | 10 | function createSnapshotObject(object: object) { 11 | Object.defineProperty(object, '$$typeof', { 12 | value: Symbol.for('react.test.json') 13 | }); 14 | 15 | return object; 16 | } 17 | 18 | export function vNodeToSnapshot(node: VNode) { 19 | let object; 20 | const children: any[] = []; 21 | 22 | if (isDOMVNode(node)) { 23 | const props = { ...node.props }; 24 | 25 | // Remove undefined props 26 | Object.keys(props).forEach((propKey) => { 27 | if (props[propKey] === undefined) { 28 | delete props[propKey]; 29 | } 30 | }); 31 | 32 | // Create the actual object that Jest will interpret as the snapshot for this VNode 33 | object = createSnapshotObject({ 34 | props, 35 | type: getTagNameOfVNode(node) 36 | }); 37 | } 38 | 39 | if (isArray(node.children)) { 40 | node.children.forEach((child) => { 41 | const asJSON = vNodeToSnapshot(child as VNode); 42 | if (asJSON) { 43 | children.push(asJSON); 44 | } 45 | }); 46 | } else if (isString(node.children)) { 47 | children.push(node.children); 48 | } else if (isObject(node.children) && !isNull(node.children)) { 49 | const asJSON = vNodeToSnapshot(node.children); 50 | if (asJSON) { 51 | children.push(asJSON); 52 | } 53 | } 54 | 55 | if (object) { 56 | object.children = children.length ? children : null; 57 | return object; 58 | } 59 | 60 | if (children.length > 1) { 61 | return children; 62 | } else if (children.length === 1) { 63 | return children[0]; 64 | } 65 | 66 | return object; 67 | } 68 | 69 | export function renderToSnapshot(input: VNode) { 70 | const vnode = renderIntoDocument(input) as VNode; 71 | 72 | if (!isNull(vnode.props)) { 73 | const snapshot = vNodeToSnapshot(vnode.props.children as VNode); 74 | delete snapshot.props.children; 75 | return snapshot; 76 | } 77 | 78 | return undefined; 79 | } 80 | 81 | export default { 82 | createSnapshotObject, 83 | renderToSnapshot, 84 | vNodeToSnapshot 85 | }; 86 | -------------------------------------------------------------------------------- /packages/inferno/src/DOM/wrappers/TextareaWrapper.ts: -------------------------------------------------------------------------------- 1 | import { isNullOrUndef } from 'inferno-shared'; 2 | import { EMPTY_OBJ } from '../utils'; 3 | 4 | function wrappedOnChange(e) { 5 | const props = this.vNode.props || EMPTY_OBJ; 6 | const event = props.onChange; 7 | 8 | if (event.event) { 9 | event.event(event.data, e); 10 | } else { 11 | event(e); 12 | } 13 | } 14 | 15 | function onTextareaInputChange(e) { 16 | const vNode = this.vNode; 17 | const props = vNode.props || EMPTY_OBJ; 18 | const previousValue = props.value; 19 | 20 | if (props.onInput) { 21 | const event = props.onInput; 22 | 23 | if (event.event) { 24 | event.event(event.data, e); 25 | } else { 26 | event(e); 27 | } 28 | } else if (props.oninput) { 29 | props.oninput(e); 30 | } 31 | 32 | // the user may have updated the vNode from the above onInput events syncronously 33 | // so we need to get it from the context of `this` again 34 | const newVNode = this.vNode; 35 | const newProps = newVNode.props || EMPTY_OBJ; 36 | 37 | // If render is going async there is no value change yet, it will come back to process input soon 38 | if (previousValue !== newProps.value) { 39 | // When this happens we need to store current cursor position and restore it, to avoid jumping 40 | 41 | applyValue(newVNode, vNode.dom, false); 42 | } 43 | } 44 | 45 | export function processTextarea(vNode, dom, nextPropsOrEmpty, mounting: boolean, isControlled: boolean) { 46 | applyValue(nextPropsOrEmpty, dom, mounting); 47 | 48 | if (isControlled) { 49 | dom.vNode = vNode; // TODO: Remove this when implementing Fiber's 50 | 51 | if (mounting) { 52 | dom.oninput = onTextareaInputChange; 53 | dom.oninput.wrapped = true; 54 | if (nextPropsOrEmpty.onChange) { 55 | dom.onchange = wrappedOnChange; 56 | dom.onchange.wrapped = true; 57 | } 58 | } 59 | } 60 | } 61 | 62 | export function applyValue(nextPropsOrEmpty, dom, mounting: boolean) { 63 | const value = nextPropsOrEmpty.value; 64 | const domValue = dom.value; 65 | 66 | if (isNullOrUndef(value)) { 67 | if (mounting) { 68 | const defaultValue = nextPropsOrEmpty.defaultValue; 69 | 70 | if (!isNullOrUndef(defaultValue)) { 71 | if (defaultValue !== domValue) { 72 | dom.value = defaultValue; 73 | } 74 | } else if (domValue !== '') { 75 | dom.value = ''; 76 | } 77 | } 78 | } else { 79 | /* There is value so keep it controlled */ 80 | if (domValue !== value) { 81 | dom.value = value; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /packages/inferno-create-element/__tests__/findDOMNodes.spec.jsx: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { findDOMNode, options, render } from 'inferno'; 3 | import Component from 'inferno-component'; 4 | 5 | describe('findDOMNodes (JSX)', () => { 6 | let container; 7 | 8 | beforeEach(function () { 9 | container = document.createElement('div'); 10 | document.body.appendChild(container); 11 | }); 12 | 13 | afterEach(function () { 14 | render(null, container); 15 | container.innerHTML = ''; 16 | document.body.removeChild(container); 17 | }); 18 | 19 | describe('various tests to see if the DOM node is right for the component', () => { 20 | options.findDOMNodeEnabled = true; 21 | let instance1; 22 | let instance2; 23 | let instance3; 24 | let ref; 25 | const refFunc = (dom) => { 26 | if (dom) { 27 | ref = dom; 28 | } 29 | }; 30 | 31 | class Example1 extends Component { 32 | render() { 33 | instance1 = this; 34 | return
; 35 | } 36 | } 37 | 38 | class Example2 extends Component { 39 | render() { 40 | instance2 = this; 41 | return
; 42 | } 43 | } 44 | 45 | class Example3 extends Component { 46 | render() { 47 | instance3 = this; 48 | return
; 49 | } 50 | } 51 | 52 | it('simple findDOMNodes', () => { 53 | render(, container); 54 | expect(findDOMNode(instance1) === document.getElementById('example1')).to.equal(true); 55 | render(null, container); 56 | expect(findDOMNode(instance1) === null).to.equal(true); 57 | render(, container); 58 | expect(findDOMNode(instance2) === document.getElementById('example2')).to.equal(true); 59 | render(, container); 60 | expect(findDOMNode(instance1) === document.getElementById('example1')).to.equal(true); 61 | render(, container); 62 | expect(findDOMNode(instance3) === document.getElementById('example3')).to.equal(true); 63 | expect(findDOMNode(instance2) === document.getElementById('example2')).to.equal(true); 64 | expect(findDOMNode(instance1) === document.getElementById('example1')).to.equal(true); 65 | render(null, container); 66 | expect(findDOMNode(instance1) === null).to.equal(true); 67 | expect(findDOMNode(instance2) === null).to.equal(true); 68 | expect(findDOMNode(instance3) === null).to.equal(true); 69 | expect(findDOMNode(ref) === ref).to.equal(true); 70 | }); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /packages/inferno-router/src/Router.ts: -------------------------------------------------------------------------------- 1 | import { createVNode, VNode } from 'inferno'; 2 | import Component from 'inferno-component'; 3 | import VNodeFlags from 'inferno-vnode-flags'; 4 | import match, { matchPath } from './match'; 5 | import RouterContext from './RouterContext'; 6 | 7 | export interface IRouterProps { 8 | history?: any; 9 | children?: any; 10 | router: any; 11 | location: any; 12 | baseUrl?: any; 13 | component?: Component; 14 | onUpdate?: any; 15 | } 16 | 17 | function createrRouter(history) { 18 | if (!history) { 19 | throw new TypeError('Inferno: Error "inferno-router" requires a history prop passed'); 20 | } 21 | return { 22 | createHref: history.createHref, 23 | listen: history.listen, 24 | push: history.push, 25 | replace: history.replace, 26 | isActive(url) { 27 | return matchPath(true, url, this.url); 28 | }, 29 | get location() { 30 | return history.location.pathname !== 'blank' ? history.location : { 31 | pathname: '/', 32 | search: '' 33 | }; 34 | }, 35 | get url() { 36 | return this.location.pathname + this.location.search; 37 | } 38 | }; 39 | } 40 | 41 | export default class Router extends Component { 42 | public router: any; 43 | public unlisten: any; 44 | 45 | constructor(props?: any, context?: any) { 46 | super(props, context); 47 | this.router = createrRouter(props.history); 48 | this.state = { 49 | url: props.url || this.router.url 50 | }; 51 | } 52 | 53 | public componentWillMount() { 54 | if (this.router) { 55 | this.unlisten = this.router.listen(() => { 56 | this.routeTo(this.router.url); 57 | }); 58 | } 59 | } 60 | 61 | public componentWillReceiveProps(nextProps) { 62 | this.setState( 63 | { url: nextProps.url }, 64 | this.props.onUpdate ? () => this.props.onUpdate() : void 0 65 | ); 66 | } 67 | 68 | public componentWillUnmount() { 69 | if (this.unlisten) { 70 | this.unlisten(); 71 | } 72 | } 73 | 74 | public routeTo(url) { 75 | this.setState( 76 | { url }, 77 | this.props.onUpdate ? () => this.props.onUpdate() : void 0 78 | ); 79 | } 80 | 81 | public render(props): VNode|null { 82 | const hit = match(props.children, this.state.url); 83 | 84 | if (hit.redirect) { 85 | setTimeout(() => { 86 | this.router.replace(hit.redirect); 87 | }, 0); 88 | return null; 89 | } 90 | 91 | return createVNode(VNodeFlags.ComponentClass, RouterContext, null, null, { 92 | location: this.state.url, 93 | matched: hit.matched, 94 | router: this.router 95 | }); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /packages/inferno-redux/README.md: -------------------------------------------------------------------------------- 1 | # inferno-redux 2 | 3 | Inferno Redux is a [redux](https://github.com/reactjs/redux) library for [Inferno](https://github.com/infernojs/inferno). 4 | 5 | Inferno Redux passes `context.store` value to each component. 6 | 7 | ## Install 8 | 9 | ``` 10 | npm install inferno-redux 11 | ``` 12 | 13 | ## Contents 14 | 15 | * Provider 16 | * connect 17 | 18 | ## Usage 19 | 20 | Usage of `inferno-redux` is similar to that of [react-redux](https://github.com/reactjs/react-redux). 21 | Inspiration was taken from `react-redux` to provide Inferno with a similar API. 22 | 23 | ```js 24 | import Inferno from 'inferno'; 25 | import { Router, Route, browserHistory } from 'inferno-router'; 26 | import { Provider } from 'inferno-redux'; 27 | import { createStore } from 'redux'; 28 | 29 | const store = createStore(function(state, action) { 30 | switch (action.type) { 31 | case 'CHANGE_NAME': 32 | return { 33 | name: action.name 34 | } 35 | default: 36 | return { 37 | name: 'TOM' 38 | }; 39 | } 40 | }) 41 | 42 | class App extends Component { 43 | render() { 44 | return
45 | { this.props.children } 46 |
; 47 | } 48 | } 49 | 50 | class BasicComponent1 extends Component { 51 | render() { 52 | const store = this.context.store; 53 | const state = store.getState(); 54 | 55 | const onClick = e => { 56 | e.preventDefault(); 57 | store.dispatch({ 58 | type: 'CHANGE_NAME', 59 | name: 'Jerry' 60 | }); 61 | }; 62 | 63 | return ( 64 |
69 | ); 70 | } 71 | } 72 | 73 | class BasicComponent2 extends Component { 74 | render() { 75 | const store = this.context.store; 76 | const state = store.getState(); 77 | 78 | return ( 79 |
80 | { state.name === 'Jerry' ? 'You\'re a mouse!' : 'You\'re a cat!' } 81 |
82 | ); 83 | } 84 | } 85 | 86 | InfernoDOM.render(( 87 | 88 | 89 | 90 | 91 | 92 | 93 | ), container); 94 | ``` 95 | -------------------------------------------------------------------------------- /packages/inferno-mobx/README.md: -------------------------------------------------------------------------------- 1 | # inferno-mobx 2 | 3 | [![MIT](https://img.shields.io/npm/l/inferno.svg?style=flat-square)](https://github.com/infernojs/inferno/blob/master/LICENSE.md) 4 | [![NPM Version](https://img.shields.io/npm/v/inferno-mobx.svg?style=flat-square)](https://www.npmjs.com/package/inferno-mobx) 5 | [![#mobservable channel on reactiflux discord](https://img.shields.io/badge/discord-%23mobx%20%40reactiflux-blue.svg)](https://discord.gg/0ZcbPKXt5bYAa2J1) 6 | [![#inferno channel on slack](https://img.shields.io/badge/slack-%23inferno%20%40infernojs-red.svg)](https://infernojs.slack.com) 7 | 8 | This is a fork of [mobx-react](https://github.com/mobxjs/mobx-react) for [Inferno](https://github.com/infernojs/inferno) 9 | 10 | *The module is compatible with Inferno v1, for older versions use [mobx-inferno](https://www.npmjs.com/package/mobx-inferno)* 11 | 12 |

 

13 |

14 |

 

15 | 16 | This package provides the bindings for MobX and Inferno. 17 | Exports `observer` and `connect` decorators, a `Provider` and some development utilities. 18 | 19 | ## Install 20 | 21 | ``` 22 | npm install --save inferno-mobx 23 | ``` 24 | 25 | Also install [mobx](https://github.com/mobxjs/mobx) dependency _(required)_ if you don't already have it 26 | 27 | ``` 28 | npm install --save mobx 29 | ``` 30 | 31 | ## Example 32 | 33 | You can inject props using the following syntax 34 | 35 | ```javascript 36 | // MyComponent.js 37 | import Inferno from 'inferno' 38 | import Component from 'inferno-component' 39 | import { connect } from 'inferno-mobx' 40 | 41 | @connect(['englishStore', 'frenchStore']) 42 | class MyComponent extends Component { 43 | render({ englishStore, frenchStore }) { 44 | return
45 |

{ englishStore.title }

46 |

{ frenchStore.title }

47 |
48 | } 49 | } 50 | 51 | export default MyComponent 52 | ``` 53 | 54 | Just make sure that you provided your stores using the `Provider`. Ex: 55 | 56 | ```javascript 57 | // index.js 58 | import Inferno from 'inferno' 59 | import { Provider } from 'inferno-mobx' 60 | import { observable } from 'mobx' 61 | import MyComponent from './MyComponent' 62 | 63 | const englishStore = observable({ 64 | title: 'Hello World' 65 | }) 66 | 67 | const frenchStore = observable({ 68 | title: 'Bonjour tout le monde' 69 | }) 70 | 71 | Inferno.render( 72 | 73 | , document.getElementById('root')) 74 | ``` 75 | -------------------------------------------------------------------------------- /config/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | 3 | const webpack = require('webpack'); 4 | const glob = require('glob'); 5 | const path = require('path'); 6 | 7 | const DEVSERVER_FILTER = process.env.DEVSERVER_FILTER || '*'; 8 | const testFiles = glob.sync(`./packages/${DEVSERVER_FILTER}/*__tests__*/**/*.js*`); 9 | console.log({ DEVSERVER_FILTER }); 10 | 11 | module.exports = { 12 | watch: true, 13 | entry: testFiles, 14 | // devtool: 'source-map', 15 | output: { 16 | filename: '__spec-build.js' 17 | }, 18 | performance: { 19 | hints: false 20 | }, 21 | module: { 22 | loaders: [ 23 | { 24 | test: /\.jsx?$/, 25 | loader: 'babel-loader', 26 | query: { 27 | // TODO: This is workaround because some of devDeps are shipping ES6... 28 | babelrc: false, 29 | presets: [ 30 | [ 'es2015', { loose: true, modules: false }], 31 | 'stage-2' 32 | ], 33 | plugins: [ 34 | 'transform-class-properties', 35 | 'transform-object-rest-spread', 36 | 'babel-plugin-syntax-jsx', 37 | [ 'babel-plugin-inferno', { imports: true }], 38 | [ 'module-resolver', { 39 | extensions: [ '.js', '.jsx' ], 40 | alias: { 41 | 'inferno-compat': './packages/inferno-compat/dist-es', 42 | 'inferno-component': './packages/inferno-component/dist-es', 43 | 'inferno-create-class': './packages/inferno-create-class/dist-es', 44 | 'inferno-create-element': './packages/inferno-create-element/dist-es', 45 | 'inferno-shared': './packages/inferno-shared/dist-es', 46 | 'inferno-hyperscript': './packages/inferno-hyperscript/dist-es', 47 | 'inferno-mobx': './packages/inferno-mobx/dist-es', 48 | 'inferno-redux': './packages/inferno-redux/dist-es', 49 | 'inferno-router': './packages/inferno-router/dist-es', 50 | 'inferno-server': './packages/inferno-server/dist-es', 51 | inferno: './packages/inferno/dist-es' 52 | } 53 | }] 54 | ] 55 | } 56 | } 57 | ] 58 | }, 59 | devServer: { 60 | contentBase: './', 61 | port: 8080, 62 | noInfo: false, 63 | hot: true, 64 | inline: true, 65 | historyApiFallback: { 66 | index: '/config/index.html' 67 | } 68 | }, 69 | resolve: { 70 | extensions: [ '.js', '.jsx' ], 71 | mainFields: [ 'browser', 'inferno:main', 'module', 'main' ] 72 | }, 73 | plugins: [ 74 | // By default, webpack does `n=>n` compilation with entry files. This concatenates 75 | // them into a single chunk. 76 | new webpack.optimize.LimitChunkCountPlugin({ 77 | maxChunks: 1 78 | }), 79 | new webpack.HotModuleReplacementPlugin() 80 | ] 81 | }; 82 | -------------------------------------------------------------------------------- /packages/inferno/src/index.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:object-literal-sort-keys */ 2 | import { NO_OP, warning } from 'inferno-shared'; 3 | import { LifecycleClass as _LifecycleClass} from 'inferno-shared'; 4 | import _VNodeFlags from 'inferno-vnode-flags'; 5 | import { getFlagsForElementVnode, normalize as internal_normalize } from './core/normalization'; 6 | import { options, Root as _Root } from './core/options'; 7 | import { cloneVNode, createVNode, InfernoChildren, InfernoInput, Props, VNode } from './core/VNodes'; 8 | import { isUnitlessNumber as internal_isUnitlessNumber } from './DOM/constants'; 9 | import { linkEvent } from './DOM/events/linkEvent'; 10 | import { patch as internal_patch } from './DOM/patching'; 11 | import { componentToDOMNodeMap as internal_DOMNodeMap, createRenderer, findDOMNode, render } from './DOM/rendering'; 12 | import { EMPTY_OBJ } from './DOM/utils'; 13 | 14 | if (process.env.NODE_ENV !== 'production') { 15 | /* tslint:disable-next-line:no-empty */ 16 | const testFunc = function testFn() {}; 17 | if (((testFunc as Function).name || testFunc.toString()).indexOf('testFn') === -1) { 18 | warning(('It looks like you\'re using a minified copy of the development build ' + 19 | 'of Inferno. When deploying Inferno apps to production, make sure to use ' + 20 | 'the production build which skips development warnings and is faster. ' + 21 | 'See http://infernojs.org for more details.' 22 | )); 23 | } 24 | } 25 | 26 | // To please the TS God 27 | // https://github.com/Microsoft/TypeScript/issues/6307 28 | export declare const VNodeFlags: _VNodeFlags; 29 | export declare const Root: _Root; 30 | export declare const LifecycleClass: _LifecycleClass; 31 | 32 | const version = '3.3.1'; 33 | 34 | // we duplicate it so it plays nicely with different module loading systems 35 | export default { 36 | EMPTY_OBJ, // used to shared common items between Inferno libs 37 | NO_OP, // used to shared common items between Inferno libs 38 | cloneVNode, // cloning 39 | createRenderer, // DOM 40 | createVNode, // core shapes 41 | findDOMNode, // DOM 42 | getFlagsForElementVnode, 43 | internal_DOMNodeMap, 44 | internal_isUnitlessNumber, 45 | internal_normalize, 46 | internal_patch, 47 | linkEvent, 48 | options, // DOM 49 | render, // DOM 50 | version // DOM 51 | }; 52 | 53 | export { 54 | EMPTY_OBJ, 55 | InfernoChildren, 56 | InfernoInput, 57 | NO_OP, 58 | Props, 59 | VNode, 60 | cloneVNode, 61 | createRenderer, 62 | createVNode, 63 | findDOMNode, 64 | getFlagsForElementVnode, 65 | internal_DOMNodeMap, 66 | internal_isUnitlessNumber, 67 | internal_normalize, 68 | internal_patch, 69 | linkEvent, 70 | options, 71 | render, 72 | version 73 | }; 74 | -------------------------------------------------------------------------------- /packages/inferno-mobx/src/inject.ts: -------------------------------------------------------------------------------- 1 | import hoistStatics from 'hoist-non-inferno-statics'; 2 | import { createVNode } from 'inferno'; 3 | import createClass from 'inferno-create-class'; 4 | import VNodeFlags from 'inferno-vnode-flags'; 5 | 6 | interface IStoreProps { 7 | ref: any; 8 | } 9 | 10 | /** 11 | * Store Injection 12 | */ 13 | function createStoreInjector(grabStoresFn: Function, component) { 14 | const Injector: any = createClass({ 15 | displayName: component.name, 16 | render() { 17 | const newProps = {} as IStoreProps; 18 | for (const key in this.props) { 19 | if (this.props.hasOwnProperty(key)) { 20 | newProps[ key ] = this.props[ key ]; 21 | } 22 | } 23 | const additionalProps = grabStoresFn(this.context.mobxStores || {}, newProps, this.context) || {}; 24 | for (const key in additionalProps) { 25 | newProps[ key ] = additionalProps[ key ]; 26 | } 27 | newProps.ref = (instance) => { 28 | this.wrappedInstance = instance; 29 | }; 30 | 31 | return createVNode(VNodeFlags.ComponentUnknown, component, null, null, newProps); 32 | } 33 | }); 34 | 35 | Injector.contextTypes = { 36 | // tslint:disable-next-line:no-empty 37 | mobxStores() {} 38 | }; 39 | hoistStatics(Injector, component); 40 | 41 | return Injector; 42 | } 43 | 44 | const grabStoresByName = function(storeNames: string[]): Function { 45 | return function(baseStores: Object, nextProps: Object): Object { 46 | storeNames.forEach(function(storeName) { 47 | 48 | // Prefer props over stores 49 | if (storeName in nextProps) { 50 | return; 51 | } 52 | 53 | if (!(storeName in baseStores)) { 54 | throw new Error( 55 | `MobX observer: Store "${storeName}" is not available! ` + 56 | `Make sure it is provided by some Provider` 57 | ); 58 | } 59 | 60 | nextProps[ storeName ] = baseStores[ storeName ]; 61 | }); 62 | return nextProps; 63 | }; 64 | }; 65 | 66 | /** 67 | * Higher order component that injects stores to a child. 68 | * takes either a varargs list of strings, which are stores read from the context, 69 | * or a function that manually maps the available stores from the context to props: 70 | * storesToProps(mobxStores, props, context) => newProps 71 | */ 72 | export default function inject(grabStoresFn?: Function | string): any { 73 | 74 | if (typeof grabStoresFn !== 'function') { 75 | 76 | const storesNames: any = []; 77 | for (let i = 0, len = arguments.length; i < len; i++) { 78 | storesNames[ i ] = arguments[ i ]; 79 | } 80 | 81 | grabStoresFn = grabStoresByName(storesNames); 82 | } 83 | 84 | return (componentClass) => createStoreInjector(grabStoresFn as Function, componentClass); 85 | } 86 | -------------------------------------------------------------------------------- /packages/inferno/src/test/utils.ts: -------------------------------------------------------------------------------- 1 | import { isArray, isNullOrUndef, isStringOrNumber } from 'inferno-shared'; 2 | import VNodeFlags from 'inferno-vnode-flags'; 3 | 4 | const comparer = document.createElement('div'); 5 | 6 | export function sortAttributes(html: string): string { 7 | return html.replace(/<([a-z0-9-]+)((?:\s[a-z0-9:_.-]+=".*?")+)((?:\s*\/)?>)/gi, (s, pre, attrs, after) => { 8 | const attrName = (attribute: string): string => attribute.split('=')[ 0 ]; 9 | const list: string[] = attrs.match(/\s[a-z0-9:_.-]+=".*?"/gi).sort((a, b) => attrName(a) > attrName(b) ? 1 : -1); 10 | if (~after.indexOf('/')) { 11 | after = '>'; 12 | } 13 | return '<' + pre + list.join('') + after; 14 | }); 15 | } 16 | 17 | export function innerHTML(HTML: string): string { 18 | comparer.innerHTML = HTML; 19 | return sortAttributes(comparer.innerHTML); 20 | } 21 | 22 | export function createStyler(CSS: string): string { 23 | if (typeof CSS === 'undefined' || CSS === null) { 24 | return CSS; 25 | } 26 | comparer.style.cssText = CSS; 27 | return comparer.style.cssText; 28 | } 29 | 30 | export function style(CSS: string[] | string): string[] | string { 31 | if (CSS instanceof Array) { 32 | return CSS.map(createStyler); 33 | } else { 34 | return createStyler(CSS); 35 | } 36 | } 37 | 38 | export function createContainerWithHTML(html: string): HTMLDivElement { 39 | const container = document.createElement('div'); 40 | 41 | container.innerHTML = html; 42 | return container; 43 | } 44 | 45 | export function validateNodeTree(node: any): boolean { 46 | if (!node) { 47 | return true; 48 | } 49 | if (isStringOrNumber(node)) { 50 | return true; 51 | } 52 | if (!node.dom) { 53 | return false; 54 | } 55 | const children = node.children; 56 | const flags = node.flags; 57 | 58 | if (flags & VNodeFlags.Element) { 59 | if (!isNullOrUndef(children)) { 60 | if (isArray(children)) { 61 | for (const child of children) { 62 | const val = validateNodeTree(child); 63 | 64 | if (!val) { 65 | return false; 66 | } 67 | } 68 | } else { 69 | const val = validateNodeTree(children); 70 | 71 | if (!val) { 72 | return false; 73 | } 74 | } 75 | } 76 | } 77 | return true; 78 | } 79 | 80 | export function waits(timer: number, done: () => void) { 81 | setTimeout(done, timer); 82 | } 83 | 84 | export function triggerEvent(name: string, element: any) { 85 | let eventType; 86 | 87 | if (name === 'click' || name === 'dblclick' || name === 'mousedown' || name === 'mouseup') { 88 | eventType = 'MouseEvents'; 89 | } else if (name === 'focus' || name === 'change' || name === 'blur' || name === 'select') { 90 | eventType = 'HTMLEvents'; 91 | } else { 92 | throw new Error('Unsupported `"' + name + '"`event'); 93 | 94 | } 95 | const event = document.createEvent(eventType); 96 | event.initEvent(name, name !== 'change', true); 97 | element.dispatchEvent(event, true); 98 | } 99 | -------------------------------------------------------------------------------- /packages/inferno-devtools/src/init.ts: -------------------------------------------------------------------------------- 1 | import { options } from 'inferno'; 2 | import Component from 'inferno-component'; 3 | import { isStatefulComponent } from 'inferno-shared'; 4 | import VNodeFlags from 'inferno-vnode-flags'; 5 | import { createDevToolsBridge } from './bridge'; 6 | 7 | const functionalComponentWrappers = new Map(); 8 | 9 | function wrapFunctionalComponent(vNode) { 10 | const originalRender = vNode.type; 11 | const name = vNode.type.name || 'Function (anonymous)'; 12 | const wrappers = functionalComponentWrappers; 13 | 14 | if (!wrappers.has(originalRender)) { 15 | const wrapper = class extends Component { 16 | public render(props, state, context) { 17 | return originalRender(props, context); 18 | } 19 | }; 20 | // Expose the original component name. React Dev Tools will use 21 | // this property if it exists or fall back to Function.name 22 | // otherwise. 23 | /* tslint:disable */ 24 | wrapper[ 'displayName' ] = name; 25 | /* tslint:enable */ 26 | wrappers.set(originalRender, wrapper); 27 | } 28 | vNode.type = wrappers.get(originalRender); 29 | vNode.type.defaultProps = originalRender.defaultProps; 30 | vNode.ref = null; 31 | vNode.flags = VNodeFlags.ComponentClass; 32 | } 33 | 34 | // Credit: this based on on the great work done with Preact and its devtools 35 | // https://github.com/developit/preact/blob/master/devtools/devtools.js 36 | 37 | export default function initDevTools() { 38 | /* tslint:disable */ 39 | if (typeof window[ '__REACT_DEVTOOLS_GLOBAL_HOOK__' ] === 'undefined') { 40 | /* tslint:enable */ 41 | // React DevTools are not installed 42 | return; 43 | } 44 | const nextVNode = options.createVNode; 45 | options.createVNode = (vNode) => { 46 | const flags = vNode.flags; 47 | 48 | if ((flags & VNodeFlags.Component) && !isStatefulComponent(vNode.type)) { 49 | wrapFunctionalComponent(vNode); 50 | } 51 | if (nextVNode) { 52 | return nextVNode(vNode); 53 | } 54 | }; 55 | // Notify devtools when preact components are mounted, updated or unmounted 56 | const bridge = createDevToolsBridge(); 57 | const nextAfterMount = options.afterMount; 58 | 59 | options.afterMount = (vNode) => { 60 | bridge.componentAdded(vNode); 61 | if (nextAfterMount) { 62 | nextAfterMount(vNode); 63 | } 64 | }; 65 | const nextAfterUpdate = options.afterUpdate; 66 | 67 | options.afterUpdate = (vNode) => { 68 | bridge.componentUpdated(vNode); 69 | if (nextAfterUpdate) { 70 | nextAfterUpdate(vNode); 71 | } 72 | }; 73 | const nextBeforeUnmount = options.beforeUnmount; 74 | 75 | options.beforeUnmount = (vNode) => { 76 | bridge.componentRemoved(vNode); 77 | if (nextBeforeUnmount) { 78 | nextBeforeUnmount(vNode); 79 | } 80 | }; 81 | // Notify devtools about this instance of "React" 82 | /* tslint:disable */ 83 | window[ '__REACT_DEVTOOLS_GLOBAL_HOOK__' ].inject(bridge); 84 | /* tslint:enable */ 85 | return () => { 86 | options.afterMount = nextAfterMount; 87 | options.afterUpdate = nextAfterUpdate; 88 | options.beforeUnmount = nextBeforeUnmount; 89 | }; 90 | } 91 | -------------------------------------------------------------------------------- /packages/inferno/__tests__/patching.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { createVNode, render } from '../dist-es'; 3 | import VNodeFlags from 'inferno-vnode-flags'; 4 | import { createTextVNode } from '../dist-es/core/VNodes'; 5 | 6 | describe('patching routine', () => { 7 | let container; 8 | 9 | beforeEach(function () { 10 | container = document.createElement('div'); 11 | document.body.appendChild(container); 12 | }); 13 | 14 | afterEach(function () { 15 | render(null, container); 16 | container.innerHTML = ''; 17 | document.body.removeChild(container); 18 | }); 19 | 20 | it('Should do nothing if lastVNode strictly equals nextVnode', () => { 21 | const yar = createVNode(2, 'div', null, '123', null, null, null, true); 22 | const bar = createVNode(2, 'div', null, '123', null, null, null, true); 23 | let foo = createVNode(2, 'div', null, [ bar, yar ], null, null, null, true); 24 | 25 | render(foo, container); 26 | expect(container.innerHTML).to.eql('
123
123
'); 27 | 28 | foo = createVNode(2, 'div', null, [ bar, yar ], null, null, null, true); 29 | 30 | render(foo, container); 31 | expect(container.innerHTML).to.eql('
123
123
'); 32 | }); 33 | 34 | it('Should mount nextNode if lastNode crashed', () => { 35 | const validNode = createVNode( 36 | VNodeFlags.HtmlElement, 37 | 'span', 38 | null, 39 | createTextVNode('a'), 40 | null, 41 | null, 42 | null, 43 | false 44 | ); 45 | const invalidNode = createVNode(0, 'span'); 46 | 47 | render(validNode, container); 48 | try { 49 | render(invalidNode, container); 50 | } catch (e) { 51 | expect(e.message.indexOf('Inferno Error: mount() received an object')).to.not.eql(-1); 52 | } 53 | expect(container.innerHTML).to.eql('a'); 54 | 55 | render(validNode, container); 56 | expect(container.innerHTML).to.eql('a'); 57 | }); 58 | 59 | it('Patch operation when nextChildren is NOT Invalid/Array/StringOrNumber/VNode', () => { 60 | const validNode = createVNode( 61 | VNodeFlags.HtmlElement, 62 | 'span', 63 | null, 64 | createVNode( 65 | VNodeFlags.HtmlElement, 66 | 'span', 67 | null, 68 | createTextVNode('a'), 69 | null, 70 | null, 71 | null, 72 | false 73 | ), 74 | null, 75 | null, 76 | null, 77 | false 78 | ); 79 | 80 | const invalidChildNode = createVNode( 81 | VNodeFlags.HtmlElement, 82 | 'span', 83 | null, 84 | createVNode(0, 'span'), 85 | null, 86 | null, 87 | null, 88 | false 89 | ); 90 | 91 | render(validNode, container); 92 | render(invalidChildNode, container); 93 | }); 94 | 95 | it('Should not access real DOM property when text does not change', () => { 96 | render(createTextVNode('a'), container); 97 | expect(container.innerHTML).to.eql('a'); 98 | render(createTextVNode('a'), container); 99 | expect(container.innerHTML).to.eql('a'); 100 | }); 101 | }); 102 | -------------------------------------------------------------------------------- /packages/inferno-create-element/src/index.ts: -------------------------------------------------------------------------------- 1 | import { createVNode, getFlagsForElementVnode, InfernoChildren, Props, VNode } from 'inferno'; 2 | import Component from 'inferno-component'; 3 | import { isInvalid, isNullOrUndef, isObject, isString, isUndefined } from 'inferno-shared'; 4 | import VNodeFlags from 'inferno-vnode-flags'; 5 | 6 | const componentHooks = new Set(); 7 | componentHooks.add('onComponentWillMount'); 8 | componentHooks.add('onComponentDidMount'); 9 | componentHooks.add('onComponentWillUnmount'); 10 | componentHooks.add('onComponentShouldUpdate'); 11 | componentHooks.add('onComponentWillUpdate'); 12 | componentHooks.add('onComponentDidUpdate'); 13 | 14 | /** 15 | * Creates virtual node 16 | * @param {string|Function|Component} type Type of node 17 | * @param {object=} props Optional props for virtual node 18 | * @param {...{object}=} _children Optional children for virtual node 19 | * @returns {VNode} new virtual ndoe 20 | */ 21 | export default function createElement(type: string | Function | Component, props?: T & Props|null, ..._children: Array): VNode { 22 | if (isInvalid(type) || isObject(type)) { 23 | throw new Error('Inferno Error: createElement() name parameter cannot be undefined, null, false or true, It must be a string, class or function.'); 24 | } 25 | let children: any = _children; 26 | let ref: any = null; 27 | let key = null; 28 | let className = null; 29 | let flags = 0; 30 | let newProps; 31 | 32 | if (_children) { 33 | if (_children.length === 1) { 34 | children = _children[ 0 ]; 35 | } else if (_children.length === 0) { 36 | children = void 0; 37 | } 38 | } 39 | if (isString(type)) { 40 | flags = getFlagsForElementVnode(type as string); 41 | 42 | if (!isNullOrUndef(props)) { 43 | newProps = {} as T & Props; 44 | 45 | for (const prop in props) { 46 | if (prop === 'className' || prop === 'class') { 47 | className = props[ prop ]; 48 | } else if (prop === 'key') { 49 | key = props.key; 50 | } else if (prop === 'children' && isUndefined(children)) { 51 | children = props.children; // always favour children args, default to props 52 | } else if (prop === 'ref') { 53 | ref = props.ref; 54 | } else { 55 | newProps[prop] = props[prop]; 56 | } 57 | } 58 | } 59 | } else { 60 | flags = VNodeFlags.ComponentUnknown; 61 | if (!isUndefined(children)) { 62 | if (!props) { 63 | props = {} as T; 64 | } 65 | props.children = children; 66 | children = null; 67 | } 68 | 69 | if (!isNullOrUndef(props)) { 70 | newProps = {} as T & Props; 71 | 72 | for (const prop in props) { 73 | if (componentHooks.has(prop)) { 74 | if (!ref) { 75 | ref = {}; 76 | } 77 | ref[ prop ] = props[ prop ]; 78 | } else if (prop === 'key') { 79 | key = props.key; 80 | } else { 81 | newProps[prop] = props[prop]; 82 | } 83 | } 84 | } 85 | } 86 | return createVNode( 87 | flags, 88 | type as string|Function, 89 | className, 90 | children, 91 | newProps, 92 | key, 93 | ref 94 | ); 95 | } 96 | -------------------------------------------------------------------------------- /packages/inferno-router/src/Route.ts: -------------------------------------------------------------------------------- 1 | import { VNode } from 'inferno'; 2 | import Component from 'inferno-component'; 3 | import createElement from 'inferno-create-element'; 4 | import { isArray } from 'inferno-shared'; 5 | import { rest } from './utils'; 6 | 7 | const resolvedPromise = Promise.resolve(); 8 | 9 | export type IRouteHook = (props?: any, router?: any) => void; 10 | 11 | export interface IRouteProps { 12 | params?: any; 13 | onEnter?: IRouteHook; 14 | onLeave?: IRouteHook; 15 | path: string; 16 | children: Array>; 17 | component?: Component; 18 | getComponent(nextState: any, callback: (error: any, comp: Component) => void): void; 19 | } 20 | 21 | export default class Route extends Component { 22 | constructor(props?: IRouteProps, context?: any) { 23 | super(props, context); 24 | this.state = { 25 | asyncComponent: null 26 | }; 27 | } 28 | 29 | public componentWillMount() { 30 | const { onEnter } = this.props; 31 | const { router } = this.context; 32 | 33 | if (onEnter) { 34 | resolvedPromise.then(() => { 35 | onEnter({ props: this.props, router }); 36 | }); 37 | } 38 | 39 | const { getComponent } = this.props; 40 | if (getComponent) { 41 | resolvedPromise.then(() => { 42 | getComponent({ props: this.props, router }, this._onComponentResolved); 43 | }); 44 | } 45 | } 46 | 47 | private _onComponentResolved = (error, component) => { 48 | this.setState({ 49 | asyncComponent: component 50 | }); 51 | } 52 | 53 | public onLeave(trigger = false) { 54 | const { onLeave } = this.props; 55 | const { router } = this.context; 56 | 57 | if (onLeave && trigger) { 58 | onLeave({ props: this.props, router }); 59 | } 60 | } 61 | 62 | public onEnter(nextProps) { 63 | const { onEnter } = nextProps; 64 | const { router } = this.context; 65 | 66 | if (this.props.path !== nextProps.path && onEnter) { 67 | onEnter({ props: nextProps, router }); 68 | } 69 | } 70 | 71 | public getComponent(nextProps) { 72 | const { getComponent } = nextProps; 73 | const { router } = this.context; 74 | 75 | if (this.props.path !== nextProps.path && getComponent) { 76 | getComponent({ props: nextProps, router }, this._onComponentResolved); 77 | } 78 | } 79 | 80 | public componentWillUnmount() { 81 | this.onLeave(true); 82 | } 83 | 84 | public componentWillReceiveProps(nextProps: IRouteProps) { 85 | this.getComponent(nextProps); 86 | this.onEnter(nextProps); 87 | this.onLeave(this.props.path !== nextProps.path); 88 | } 89 | 90 | public render(_args: IRouteProps): VNode|null { 91 | const { component, children } = _args; 92 | const props = rest(_args, [ 'component', 'children', 'path', 'getComponent' ]); 93 | 94 | const { asyncComponent } = this.state; 95 | 96 | const resolvedComponent = component || asyncComponent; 97 | if (!resolvedComponent) { 98 | return !isArray(children) ? children : null; 99 | } 100 | 101 | return createElement(resolvedComponent, props, children); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /packages/inferno/src/DOM/wrappers/SelectWrapper.ts: -------------------------------------------------------------------------------- 1 | import { isArray, isInvalid, isNullOrUndef } from 'inferno-shared'; 2 | import { isVNode } from '../../core/VNodes'; 3 | import { EMPTY_OBJ } from '../utils'; 4 | 5 | function updateChildOptionGroup(vNode, value) { 6 | const type = vNode.type; 7 | 8 | if (type === 'optgroup') { 9 | const children = vNode.children; 10 | 11 | if (isArray(children)) { 12 | for (let i = 0, len = children.length; i < len; i++) { 13 | updateChildOption(children[ i ], value); 14 | } 15 | } else if (isVNode(children)) { 16 | updateChildOption(children, value); 17 | } 18 | } else { 19 | updateChildOption(vNode, value); 20 | } 21 | } 22 | 23 | function updateChildOption(vNode, value) { 24 | const props = vNode.props || EMPTY_OBJ; 25 | const dom = vNode.dom; 26 | 27 | // we do this as multiple may have changed 28 | dom.value = props.value; 29 | if ((isArray(value) && value.indexOf(props.value) !== -1) || props.value === value) { 30 | dom.selected = true; 31 | } else if (!isNullOrUndef(value) || !isNullOrUndef(props.selected)) { 32 | dom.selected = props.selected || false; 33 | } 34 | } 35 | 36 | function onSelectChange(e) { 37 | const vNode = this.vNode; 38 | const props = vNode.props || EMPTY_OBJ; 39 | const dom = vNode.dom; 40 | const previousValue = props.value; 41 | 42 | if (props.onChange) { 43 | const event = props.onChange; 44 | 45 | if (event.event) { 46 | event.event(event.data, e); 47 | } else { 48 | event(e); 49 | } 50 | } else if (props.onchange) { 51 | props.onchange(e); 52 | } 53 | // the user may have updated the vNode from the above onInput events syncronously 54 | // so we need to get it from the context of `this` again 55 | const newVNode = this.vNode; 56 | const newProps = newVNode.props || EMPTY_OBJ; 57 | 58 | // If render is going async there is no value change yet, it will come back to process input soon 59 | if (previousValue !== newProps.value) { 60 | // When this happens we need to store current cursor position and restore it, to avoid jumping 61 | 62 | applyValue(newVNode, dom, newProps, false); 63 | } 64 | } 65 | 66 | export function processSelect(vNode, dom, nextPropsOrEmpty, mounting: boolean, isControlled: boolean) { 67 | applyValue(vNode, dom, nextPropsOrEmpty, mounting); 68 | 69 | if (isControlled) { 70 | dom.vNode = vNode; // TODO: Remove this when implementing Fiber's 71 | 72 | if (mounting) { 73 | dom.onchange = onSelectChange; 74 | dom.onchange.wrapped = true; 75 | } 76 | } 77 | } 78 | 79 | export function applyValue(vNode, dom, nextPropsOrEmpty, mounting: boolean) { 80 | if (nextPropsOrEmpty.multiple !== dom.multiple) { 81 | dom.multiple = nextPropsOrEmpty.multiple; 82 | } 83 | const children = vNode.children; 84 | 85 | if (!isInvalid(children)) { 86 | let value = nextPropsOrEmpty.value; 87 | if (mounting && isNullOrUndef(value)) { 88 | value = nextPropsOrEmpty.defaultValue; 89 | } 90 | if (isArray(children)) { 91 | for (let i = 0, len = children.length; i < len; i++) { 92 | updateChildOptionGroup(children[ i ], value); 93 | } 94 | } else if (isVNode(children)) { 95 | updateChildOptionGroup(children, value); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /packages/inferno-server/__tests__/creation-stream.spec.js: -------------------------------------------------------------------------------- 1 | import { streamAsStaticMarkup } from '../dist-es'; 2 | import { render } from 'inferno'; 3 | import createClass from 'inferno-create-class'; 4 | import createElement from 'inferno-create-element'; 5 | import { expect } from 'chai'; 6 | import concatStream from 'concat-stream-es6'; 7 | import Component from 'inferno-component'; 8 | 9 | describe('SSR Root Creation Streams - (non-JSX)', () => { 10 | let container; 11 | 12 | beforeEach(function () { 13 | container = document.createElement('div'); 14 | document.body.appendChild(container); 15 | }); 16 | 17 | afterEach(function () { 18 | render(null, container); 19 | document.body.removeChild(container); 20 | }); 21 | 22 | it('should throw with invalid children', () => { 23 | const test = (value) => createElement('a', null, true); 24 | 25 | return streamPromise(test('foo')).catch((err) => { 26 | expect(err.toString()).to.equal('Error: invalid component'); 27 | }); 28 | }); 29 | 30 | it('should use getChildContext', () => { 31 | const TestComponent = createClass({ 32 | getChildContext() { 33 | return { hello: 'world' }; 34 | }, 35 | render() { 36 | return createElement('a', null, this.context.hello); 37 | } 38 | }); 39 | return streamPromise(createElement(TestComponent, null)).then(function (output) { 40 | expect(output).to.equal('world'); 41 | }); 42 | }); 43 | 44 | describe('Component hook', () => { 45 | it('Should allow changing state in CWM', () => { 46 | class Another extends Component { 47 | constructor(props, context) { 48 | super(props, context); 49 | 50 | this.state = { 51 | foo: 'bar' 52 | }; 53 | } 54 | 55 | componentWillMount() { 56 | this.setState({ 57 | foo: 'bar2' 58 | }); 59 | } 60 | 61 | render() { 62 | return ( 63 | createElement('div', null, this.state.foo) 64 | ); 65 | } 66 | } 67 | 68 | class Tester extends Component { 69 | constructor(props, context) { 70 | super(props, context); 71 | 72 | this.state = { 73 | foo: 'bar' 74 | }; 75 | } 76 | 77 | componentWillMount() { 78 | this.setState({ 79 | foo: 'bar2' 80 | }); 81 | } 82 | 83 | render() { 84 | return ( 85 | createElement('div', null, [ 86 | this.state.foo, 87 | createElement(Another) 88 | ]) 89 | ); 90 | } 91 | } 92 | 93 | const vDom = createElement(Tester); 94 | return streamPromise(vDom).then(function (output) { 95 | const container = document.createElement('div'); 96 | document.body.appendChild(container); 97 | container.innerHTML = output; 98 | expect(output).to.equal('
bar2
bar2
'); 99 | document.body.removeChild(container); 100 | }); 101 | }); 102 | }); 103 | }); 104 | 105 | function streamPromise(dom) { 106 | return new Promise(function (res, rej) { 107 | streamAsStaticMarkup(dom) 108 | .on('error', rej) 109 | .pipe(concatStream(function (buffer) { 110 | res(buffer.toString('utf-8')); 111 | })); 112 | }); 113 | } 114 | -------------------------------------------------------------------------------- /packages/inferno-component/__tests__/state.spec.jsx: -------------------------------------------------------------------------------- 1 | import { createVNode, render } from 'inferno'; 2 | import Component from '../dist-es'; 3 | import VNodeFlags from 'inferno-vnode-flags'; 4 | 5 | class TestCWRP extends Component { 6 | constructor(props) { 7 | super(props); 8 | 9 | this.state = { 10 | a: 0, 11 | b: 0 12 | }; 13 | } 14 | 15 | componentWillReceiveProps() { 16 | this.setStateSync({ a: 1 }); 17 | 18 | if (this.state.a !== 1) { 19 | this.props.done('state is not correct'); 20 | return; 21 | } 22 | 23 | this.props.done(); 24 | } 25 | 26 | render() { 27 | return
{JSON.stringify(this.state)}
; 28 | } 29 | } 30 | 31 | describe('state', () => { 32 | let container; 33 | 34 | beforeEach(function () { 35 | container = document.createElement('div'); 36 | document.body.appendChild(container); 37 | }); 38 | 39 | afterEach(function () { 40 | render(null, container); 41 | container.innerHTML = ''; 42 | document.body.removeChild(container); 43 | }); 44 | 45 | // As per React 46 | it('Should not have state defined in base constructor', () => { 47 | class Foo extends Component { 48 | constructor(p, c) { 49 | super(p, c); 50 | 51 | expect(this.state).to.be.a('null'); 52 | } 53 | } 54 | 55 | const f = new Foo({}, {}); 56 | 57 | expect(f).not.to.be.a('null'); 58 | }); 59 | 60 | describe('setting state', () => { 61 | 62 | it('setStateSync should apply state during componentWillReceiveProps', (done) => { 63 | const node = createVNode(VNodeFlags.ComponentClass, TestCWRP, null, null, { done }, null); 64 | render(node, container); 65 | node.props.foo = 1; 66 | render(node, container); 67 | }); 68 | }); 69 | 70 | 71 | describe('didUpdate and setState', () => { 72 | it('order', (done) => { 73 | class Test extends Component { 74 | 75 | constructor(props, context) { 76 | super(props, context); 77 | 78 | this.state = { 79 | testScrollTop: 0 80 | }; 81 | } 82 | 83 | componentWillReceiveProps(nextProps) { 84 | console.log('CWRP', nextProps.scrollTop); 85 | if (nextProps.scrollTop !== 0){ 86 | this.setState({ testScrollTop: nextProps.scrollTop }); 87 | } 88 | } 89 | 90 | componentDidUpdate(prevProps, prevState) { 91 | expect(prevState.testScrollTop).to.equal(0); 92 | expect(this.state.testScrollTop).to.equal(200); 93 | } 94 | 95 | render(){ 96 | return (
aa
); 97 | } 98 | 99 | } 100 | 101 | class Example extends Component{ 102 | 103 | constructor(props, context) { 104 | super(props, context); 105 | this.state = { 106 | exampleScrollTop: 0 107 | }; 108 | } 109 | 110 | render(){ 111 | return (); 112 | } 113 | 114 | componentDidMount(){ 115 | setTimeout(()=> { 116 | this.setState({ exampleScrollTop: 200 }); 117 | 118 | setTimeout(() => { 119 | done(); 120 | }, 50); 121 | }, 50); 122 | } 123 | } 124 | 125 | render( 126 | , 127 | document.getElementById('container') 128 | ); 129 | }); 130 | }); 131 | }); 132 | -------------------------------------------------------------------------------- /packages/inferno-compat/README.md: -------------------------------------------------------------------------------- 1 | # inferno-compat 2 | 3 | This module is a compatibility layer that makes React-based modules work with Inferno, without any code changes. 4 | 5 | It provides the same exports as `react` and `react-dom`, meaning you can use your build tool of choice to drop it in where React is being depended on. 6 | 7 | Do note however, as with almost all compatability layer libraries, there is an associated cost of extra overhead. As such, you should never expect native Inferno performance when using `inferno-compat`. 8 | 9 | ## How to install? 10 | 11 | ```bash 12 | npm install --save inferno 13 | npm install --save inferno-compat 14 | ``` 15 | 16 | ## What is currently supported? 17 | 18 | ### `react` 19 | 20 | - `React.createClass` 21 | - `React.createElement` 22 | - `React.cloneElement` 23 | - `React.Component` 24 | - `React.PureComponent` 25 | - `React.PropTypes` 26 | - `React.Children` 27 | - `React.isValidElement` 28 | 29 | Note: Inferno will not currently validate `PropTypes` 30 | 31 | ### `react-dom` 32 | 33 | - `ReactDOM.render` 34 | - `ReactDOM.unmountComponentAtNode` 35 | - `ReactDOM.findDOMNode` 36 | - `React.DOM` 37 | - `React.createFactory` 38 | 39 | ## Usage with Webpack 40 | 41 | Using `inferno-compat` with Webpack is easy. 42 | 43 | All you have to do is add an alias for `react` and `react-dom`: 44 | 45 | ```js 46 | { 47 | resolve: { 48 | alias: { 49 | 'react': 'inferno-compat', 50 | 'react-dom': 'inferno-compat' 51 | } 52 | } 53 | } 54 | ``` 55 | 56 | ## Usage with Babel 57 | 58 | Install the Babel plugin for module aliasing: `npm install --save-dev babel-plugin-module-resolver`. 59 | 60 | Babel can now alias `react` and `react-dom` to `inferno` by adding the following to your `.babelrc` file: 61 | 62 | ```js 63 | { 64 | "plugins": [ 65 | ["module-resolver", { 66 | "root": ["."], 67 | "alias": { 68 | "react": "inferno-compat", 69 | "react-dom": "inferno-compat" 70 | } 71 | }] 72 | ] 73 | } 74 | ``` 75 | 76 | ## Usage with Browserify 77 | 78 | Using `inferno-compat` with Browserify is as simple as installing and configuring [aliasify](http://npm.im/aliasify). 79 | 80 | First, install it: `npm install --save-dev aliasify` 81 | 82 | ... then in your `package.json`, configure aliasify to alias `react` and `react-dom`: 83 | 84 | ```js 85 | { 86 | // ... 87 | "aliasify": { 88 | "aliases": { 89 | "react": "inferno-compat", 90 | "react-dom": "inferno-compat" 91 | } 92 | } 93 | // ... 94 | } 95 | ``` 96 | 97 | ## Once Aliased 98 | 99 | With the above Webpack or Browserify aliases in place, existing React modules should work nicely: 100 | 101 | ```js 102 | import React from 'react'; 103 | import ReactDOM from 'react-dom'; 104 | 105 | class Foo extends React.Component { 106 | propTypes = { 107 | a: React.PropTypes.string.isRequired 108 | }; 109 | render() { 110 | let { a, b, children } = this.props; 111 | return
{ children }
; 112 | } 113 | } 114 | 115 | ReactDOM.render(( 116 | test 117 | ), document.getElementById("app")); 118 | ``` 119 | -------------------------------------------------------------------------------- /packages/inferno-router/src/createRoutes.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Helper function for parsing plain route configurations 3 | * based on react-router createRoutes handler. 4 | * 5 | * currently supported keys: 6 | * - path 7 | * - component 8 | * - childRoutes 9 | * - indexRoute 10 | * 11 | * Usage example: 12 | * const routes = createRoutes([ 13 | * { 14 | * path : '/', 15 | * component : App, 16 | * indexRoute : { 17 | * component : Home, 18 | * }, 19 | * childRoutes : [ 20 | * { 21 | * path : 'films/', 22 | * component : Films, 23 | * childRoutes : { 24 | * path : 'detail/:id', 25 | * component : FilmDetail, 26 | * } 27 | * }, 28 | * { 29 | * path : '/*', 30 | * component : NoMatch 31 | * } 32 | * ] 33 | * } 34 | * ]); 35 | * 36 | * Usage on Router JSX 37 | * 38 | */ 39 | 40 | import { VNode } from 'inferno'; 41 | import Component from 'inferno-component'; 42 | import createElement from 'inferno-create-element'; 43 | import { isArray } from 'inferno-shared'; 44 | import Route, { IRouteHook } from './Route'; 45 | 46 | export interface IPlainRouteConfig { 47 | path: string; 48 | component: Component; 49 | indexRoute?: IPlainRouteConfig; 50 | childRoutes?: IPlainRouteConfig | IPlainRouteConfig[]; 51 | children?: VNode | VNode[]; 52 | onEnter?: IRouteHook; 53 | onLeave?: IRouteHook; 54 | } 55 | 56 | const handleIndexRoute = (indexRouteNode: IPlainRouteConfig): VNode => createElement(Route, indexRouteNode); 57 | const handleChildRoute = (childRouteNode: IPlainRouteConfig): VNode => handleRouteNode(childRouteNode); 58 | const handleChildRoutes = (childRouteNodes: IPlainRouteConfig[]): VNode[] => childRouteNodes.map(handleChildRoute); 59 | 60 | function handleRouteNode(routeConfigNode: IPlainRouteConfig): VNode { 61 | 62 | if (routeConfigNode.indexRoute && !routeConfigNode.childRoutes) { 63 | return createElement(Route, routeConfigNode); 64 | } 65 | 66 | // create deep copy of config 67 | const node: IPlainRouteConfig = {} as IPlainRouteConfig; 68 | for (const key in routeConfigNode) { 69 | node[ key ] = routeConfigNode[ key ]; 70 | } 71 | 72 | node.children = []; 73 | 74 | // handle index route config 75 | if (node.indexRoute) { 76 | node.children.push(handleIndexRoute(node.indexRoute)); 77 | delete node.indexRoute; 78 | } 79 | 80 | // handle child routes config 81 | if (node.childRoutes) { 82 | const nodes: IPlainRouteConfig[] = isArray(node.childRoutes) ? node.childRoutes : [ node.childRoutes ]; 83 | node.children.push(...handleChildRoutes(nodes)); 84 | delete node.childRoutes; 85 | } 86 | 87 | // cleanup to match native rendered result 88 | if (node.children.length === 1) { 89 | node.children = node.children[ 0 ]; 90 | } 91 | if ( 92 | (isArray(node.children) && node.children.length === 0) || 93 | (!isArray(node.children) && Object.keys(node.children).length === 0) 94 | ) { 95 | delete node.children; 96 | } 97 | 98 | return createElement(Route, node); 99 | } 100 | 101 | export default (routeConfig: IPlainRouteConfig[]): VNode[] => routeConfig.map(handleRouteNode); 102 | -------------------------------------------------------------------------------- /packages/inferno-create-element/__benchmarks__/classcomponents.js: -------------------------------------------------------------------------------- 1 | import { createVNode, render } from 'inferno'; 2 | import Component from 'inferno-component'; 3 | import InfernoVNodeFlags from 'inferno-vnode-flags'; 4 | 5 | suite('class components', function() { 6 | benchmark('single component', function() { 7 | class Com extends Component { 8 | render() { 9 | return createVNode(InfernoVNodeFlags.Element, 'div', null, '1'); 10 | } 11 | } 12 | 13 | render(createVNode(InfernoVNodeFlags.ComponentClass, Com), this.testDiv); 14 | }, { 15 | setup: function() { 16 | this.testDiv = document.createElement('div'); 17 | }, 18 | 19 | teardown: function() { 20 | this.testDiv.innerHTML = ''; 21 | } 22 | }); 23 | 24 | 25 | benchmark('single component + state change in CWM', function() { 26 | class Com extends Component { 27 | 28 | componentWillMount() { 29 | this.setState({ 30 | foo: 'bar', 31 | daa: 'jaa', 32 | c: 'g' 33 | }); 34 | } 35 | 36 | render() { 37 | return createVNode(InfernoVNodeFlags.Element, 'div', null, '1'); 38 | } 39 | } 40 | 41 | render(createVNode(InfernoVNodeFlags.ComponentClass, Com), this.testDiv); 42 | }, { 43 | setup: function() { 44 | this.testDiv = document.createElement('div'); 45 | }, 46 | 47 | teardown: function() { 48 | this.testDiv.innerHTML = ''; 49 | } 50 | }); 51 | 52 | benchmark('20 children components with state change in CWM', function() { 53 | 54 | class Com extends Component { 55 | 56 | componentWillMount() { 57 | this.setState({ 58 | foo: 'bar', 59 | daa: 'jaa', 60 | c: 'g' 61 | }); 62 | } 63 | 64 | render() { 65 | return createVNode(InfernoVNodeFlags.Element, 'div', this.props.children, '1'); 66 | } 67 | } 68 | 69 | render(createVNode(InfernoVNodeFlags.ComponentClass, Com, null, [ 70 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 71 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 72 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 73 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 74 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 75 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 76 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 77 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 78 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 79 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 80 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 81 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 82 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 83 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 84 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 85 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 86 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 87 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 88 | createVNode(InfernoVNodeFlags.ComponentClass, Com), 89 | createVNode(InfernoVNodeFlags.ComponentClass, Com) 90 | ]), this.testDiv); 91 | }, { 92 | setup: function() { 93 | this.testDiv = document.createElement('div'); 94 | }, 95 | 96 | teardown: function() { 97 | this.testDiv.innerHTML = ''; 98 | } 99 | }); 100 | }); 101 | -------------------------------------------------------------------------------- /packages/inferno-shared/src/index.ts: -------------------------------------------------------------------------------- 1 | export const NO_OP = '$NO_OP'; 2 | export const ERROR_MSG = 'a runtime error occured! Use Inferno in development environment to find the error.'; 3 | 4 | // This should be boolean and not reference to window.document 5 | export const isBrowser = !!(typeof window !== 'undefined' && window.document); 6 | 7 | export function toArray(children): any[] { 8 | return isArray(children) ? children : (children ? [ children ] : children); 9 | } 10 | 11 | // this is MUCH faster than .constructor === Array and instanceof Array 12 | // in Node 7 and the later versions of V8, slower in older versions though 13 | export const isArray = Array.isArray; 14 | 15 | export function isStatefulComponent(o: any): boolean { 16 | return !isUndefined(o.prototype) && !isUndefined(o.prototype.render); 17 | } 18 | 19 | export function isStringOrNumber(o: any): o is string|number { 20 | const type = typeof o; 21 | 22 | return type === 'string' || type === 'number'; 23 | } 24 | 25 | export function isNullOrUndef(o: any): o is undefined|null { 26 | return isUndefined(o) || isNull(o); 27 | } 28 | 29 | export function isInvalid(o: any): o is null|false|true|undefined { 30 | return isNull(o) || o === false || isTrue(o) || isUndefined(o); 31 | } 32 | 33 | export function isFunction(o: any): o is Function { 34 | return typeof o === 'function'; 35 | } 36 | 37 | export function isString(o: any): o is string { 38 | return typeof o === 'string'; 39 | } 40 | 41 | export function isNumber(o: any): o is number { 42 | return typeof o === 'number'; 43 | } 44 | 45 | export function isNull(o: any): o is null { 46 | return o === null; 47 | } 48 | 49 | export function isTrue(o: any): o is true { 50 | return o === true; 51 | } 52 | 53 | export function isUndefined(o: any): o is undefined { 54 | return o === void 0; 55 | } 56 | 57 | export function isObject(o: any): o is object { 58 | return typeof o === 'object'; 59 | } 60 | 61 | export function throwError(message?: string) { 62 | if (!message) { 63 | message = ERROR_MSG; 64 | } 65 | throw new Error(`Inferno Error: ${ message }`); 66 | } 67 | 68 | export function warning(message: string) { 69 | // tslint:disable-next-line:no-console 70 | console.warn(message); 71 | } 72 | 73 | export function combineFrom(first?: {}|null, second?: {}|null): object { 74 | const out = {}; 75 | if (first) { 76 | for (const key in first) { 77 | out[ key ] = first[ key ]; 78 | } 79 | } 80 | if (second) { 81 | for (const key in second) { 82 | out[ key ] = second[ key ]; 83 | } 84 | } 85 | return out; 86 | } 87 | 88 | /* 89 | * This is purely a tiny event-emitter/pubsub 90 | */ 91 | export interface LifecycleClass { 92 | listeners: Array<() => void>; 93 | addListener(callback: Function): void; 94 | trigger(): void; 95 | } 96 | 97 | export function Lifecycle() { 98 | this.listeners = []; 99 | } 100 | 101 | Lifecycle.prototype.addListener = function addListener(callback) { 102 | this.listeners.push(callback); 103 | }; 104 | Lifecycle.prototype.trigger = function trigger() { 105 | const listeners = this.listeners; 106 | 107 | let listener; 108 | // We need to remove current listener from array when calling it, because more listeners might be added 109 | while (listener = listeners.shift()) { 110 | listener(); 111 | } 112 | }; 113 | -------------------------------------------------------------------------------- /packages/inferno-create-element/__tests__/functional.spec.jsx: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { createRenderer } from 'inferno'; 3 | import curry from 'lodash/curry'; 4 | import { map, reduce, scan } from 'most'; 5 | import { hold, sync } from 'most-subject'; 6 | import Type from 'union-type-es'; 7 | 8 | describe('Functional methods (JSX)', () => { 9 | let container; 10 | 11 | beforeEach(function () { 12 | container = document.createElement('div'); 13 | }); 14 | 15 | it('A basic example', (done) => { 16 | // Update 17 | // eslint-disable-next-line new-cap 18 | const Action = Type({ Increment: [], Decrement: [] }); 19 | 20 | const update = (model, action) => Action.case({ 21 | Increment: (_) => model + 1, 22 | Decrement: (_) => model - 1 23 | }, action); 24 | 25 | const actions$ = hold(1, sync()); 26 | 27 | const emitAction = (action) => actions$.next(action); 28 | // eslint-disable-next-line new-cap 29 | const emitDecrement = (_) => emitAction(Action.Decrement()); 30 | // eslint-disable-next-line new-cap 31 | const emitIncrement = (_) => emitAction(Action.Increment()); 32 | 33 | // View 34 | const countStyle = { 35 | fontSize: '48px', 36 | fontFamily: 'monospace', 37 | width: '100%', 38 | textAlign: 'center' 39 | }; 40 | 41 | // noinspection TypeScriptUnresolvedFunction 42 | const view = curry((actions, model) => 43 |
44 | 45 |
{model}
46 | 47 |
); 48 | 49 | // FRP 50 | const model$ = scan(update, 0, actions$); 51 | const vNodes$ = map(view(actions$), model$); 52 | 53 | // Logging 54 | // observe(state => console.log('model', state), model$) // eslint-disable-line fp/no-unused-expression 55 | 56 | const renderer = createRenderer(); 57 | 58 | const runApp = () => reduce(renderer, container, vNodes$); 59 | 60 | runApp(); 61 | setTimeout(() => { 62 | /* 63 |
64 | 65 |
0
66 | 67 |
68 | */ 69 | const outerDiv = container.firstChild; 70 | 71 | expect(outerDiv.style.fontSize).to.eql('48px'); 72 | expect(outerDiv.style.fontFamily).to.eql('monospace'); 73 | expect(outerDiv.style.width).to.eql('100%'); 74 | expect(outerDiv.style.textAlign).to.eql('center'); 75 | expect(outerDiv.tagName).to.eql('DIV'); 76 | 77 | const firstButton = outerDiv.childNodes[ 0 ]; 78 | expect(firstButton.getAttribute('id')).to.eql('decrement'); 79 | expect(firstButton.innerHTML).to.eql('-'); 80 | 81 | const midDiv = outerDiv.childNodes[ 1 ]; 82 | expect(midDiv.style.fontSize).to.eql('48px'); 83 | expect(midDiv.style.fontFamily).to.eql('monospace'); 84 | expect(midDiv.style.width).to.eql('100%'); 85 | expect(midDiv.style.textAlign).to.eql('center'); 86 | expect(midDiv.tagName).to.eql('DIV'); 87 | expect(midDiv.innerHTML).to.eql('0'); 88 | 89 | const secondButton = outerDiv.childNodes[ 2 ]; 90 | expect(secondButton.getAttribute('id')).to.eql('increment'); 91 | expect(secondButton.innerHTML).to.eql('+'); 92 | done(); 93 | }, 10); 94 | }); 95 | }); 96 | -------------------------------------------------------------------------------- /packages/inferno-create-element/__tests__/svgXlink.spec.jsx: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { render } from 'inferno'; 3 | 4 | 5 | describe('createTree - SVG (JSX)', () => { 6 | let container; 7 | 8 | beforeEach(function () { 9 | container = document.createElement('div'); 10 | }); 11 | 12 | afterEach(function () { 13 | render(null, container); 14 | }); 15 | 16 | it('should remove namespaced SVG attributes', () => { 17 | render( 18 | 19 | , container); 20 | 21 | expect(container.firstChild.tagName).to.eql('svg'); 22 | expect(container.firstChild.firstChild.hasAttributeNS( 23 | 'http://www.w3.org/1999/xlink', 24 | 'href' 25 | )).to.equal(true); 26 | 27 | render( 28 | 29 | , container); 30 | 31 | expect(container.firstChild.tagName).to.eql('svg'); 32 | expect(container.firstChild.firstChild.hasAttributeNS( 33 | 'http://www.w3.org/1999/xlink', 34 | 'href' 35 | )).to.equal(false); 36 | }); 37 | 38 | it('should update namespaced SVG attributes', () => { 39 | render( 40 | 41 | , container); 42 | 43 | expect(container.firstChild.tagName).to.eql('svg'); 44 | expect(container.firstChild.firstChild.hasAttributeNS( 45 | 'http://www.w3.org/1999/xlink', 46 | 'href' 47 | )).to.equal(true); 48 | 49 | render( 50 | 51 | , container); 52 | 53 | expect(container.firstChild.tagName).to.eql('svg'); 54 | expect(container.firstChild.firstChild.getAttributeNS( 55 | 'http://www.w3.org/1999/xlink', 56 | 'href' 57 | )).to.equal('http://i.imgur.com/JvqCM2p.png'); 58 | }); 59 | 60 | it('should add / change / remove xlink:href attribute', () => { 61 | render( 62 | 63 | , container); 64 | 65 | expect(container.firstChild.firstChild.getAttributeNS( 66 | 'http://www.w3.org/1999/xlink', 67 | 'href' 68 | )).to.equal('#test'); 69 | 70 | render( 71 | 72 | , container); 73 | 74 | expect(container.firstChild.firstChild.getAttributeNS( 75 | 'http://www.w3.org/1999/xlink', 76 | 'href' 77 | )).to.equal('#changed'); 78 | 79 | render( 80 | 81 | , container); 82 | 83 | expect(container.firstChild.firstChild.hasAttributeNS( 84 | 'http://www.w3.org/1999/xlink', 85 | 'href' 86 | )).to.equal(false); 87 | }); 88 | 89 | it('should add / change / remove xlinkHref attribute (babel plugin should transpile it)', () => { 90 | render( 91 | 92 | , container); 93 | 94 | expect(container.firstChild.firstChild.getAttributeNS( 95 | 'http://www.w3.org/1999/xlink', 96 | 'href' 97 | )).to.equal('#test'); 98 | 99 | render( 100 | 101 | , container); 102 | 103 | expect(container.firstChild.firstChild.getAttributeNS( 104 | 'http://www.w3.org/1999/xlink', 105 | 'href' 106 | )).to.equal('#changed'); 107 | 108 | render( 109 | 110 | , container); 111 | 112 | expect(container.firstChild.firstChild.hasAttributeNS( 113 | 'http://www.w3.org/1999/xlink', 114 | 'href' 115 | )).to.equal(false); 116 | }); 117 | }); 118 | -------------------------------------------------------------------------------- /packages/inferno-router/src/utils.ts: -------------------------------------------------------------------------------- 1 | import { isArray, isString } from 'inferno-shared'; 2 | 3 | export const emptyObject = {}; 4 | 5 | export function decode(val: any): any { 6 | return typeof val !== 'string' ? val : decodeURIComponent(val); 7 | } 8 | 9 | export function isEmpty(children): boolean { 10 | return !children || !(isArray(children) ? children : Object.keys(children)).length; 11 | } 12 | 13 | export function flatten(oldArray) { 14 | const newArray = []; 15 | 16 | flattenArray(oldArray, newArray); 17 | return newArray; 18 | } 19 | 20 | export function getURLString(location): string { 21 | return isString(location) ? location : (location.pathname + location.search); 22 | } 23 | 24 | /** 25 | * Maps a querystring to an object 26 | * Supports arrays and utf-8 characters 27 | * @param search 28 | * @returns {any} 29 | */ 30 | export function mapSearchParams(search): any { 31 | if (search === '') { 32 | return {}; 33 | } 34 | 35 | // Create an object with no prototype 36 | const map = Object.create(null); 37 | const fragments = search.split('&'); 38 | 39 | for (let i = 0, len = fragments.length; i < len; i++) { 40 | const fragment = fragments[ i ]; 41 | const [ k, v ] = fragment.split('=').map(mapFragment).map(decodeURIComponent); 42 | 43 | if (map[ k ]) { 44 | map[ k ] = isArray(map[ k ]) ? map[ k ] : [ map[ k ] ]; 45 | map[ k ].push(v); 46 | } else { 47 | map[ k ] = v; 48 | } 49 | } 50 | return map; 51 | } 52 | 53 | /** 54 | * Gets the relevant part of the URL for matching 55 | * @param fullURL 56 | * @param partURL 57 | * @returns {string} 58 | */ 59 | export function toPartialURL(fullURL: string, partURL: string) { 60 | if (fullURL.indexOf(partURL) === 0) { 61 | return fullURL.substr(partURL.length); 62 | } 63 | return fullURL; 64 | } 65 | 66 | /** 67 | * Simulates ... operator by returning first argument 68 | * with the keys in the second argument excluded 69 | * @param _args 70 | * @param excluded 71 | * @returns {{}} 72 | */ 73 | export function rest(_args, excluded) { 74 | const t = {}; 75 | for (const p in _args) { 76 | if (excluded.indexOf(p) < 0) { 77 | t[ p ] = _args[ p ]; 78 | } 79 | } 80 | return t; 81 | } 82 | 83 | /** 84 | * Sorts an array according to its `path` prop length 85 | * @param a 86 | * @param b 87 | * @returns {number} 88 | */ 89 | export function pathRankSort(a: any, b: any) { 90 | const aAttr = a.props || emptyObject; 91 | const bAttr = b.props || emptyObject; 92 | const diff = rank(bAttr.path) - rank(aAttr.path); 93 | return diff || ((bAttr.path && aAttr.path) ? (bAttr.path.length - aAttr.path.length) : 0); 94 | } 95 | 96 | /** 97 | * Helper function for parsing querystring arrays 98 | */ 99 | function mapFragment(p: string, isVal: number): string { 100 | return decodeURIComponent(isVal | 0 ? p : p.replace('[]', '')); 101 | } 102 | 103 | function strip(url: string): string { 104 | return url.replace(/(^\/+|\/+$)/g, ''); 105 | } 106 | 107 | function rank(url: string = ''): number { 108 | return (strip(url).match(/\/+/g) || '').length; 109 | } 110 | 111 | function flattenArray(oldArray, newArray) { 112 | for (let i = 0, len = oldArray.length; i < len; i++) { 113 | const item = oldArray[ i ]; 114 | 115 | if (isArray(item)) { 116 | flattenArray(item, newArray); 117 | } else { 118 | newArray.push(item); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /packages/inferno/src/DOM/constants.ts: -------------------------------------------------------------------------------- 1 | export const xlinkNS = 'http://www.w3.org/1999/xlink'; 2 | export const xmlNS = 'http://www.w3.org/XML/1998/namespace'; 3 | export const svgNS = 'http://www.w3.org/2000/svg'; 4 | 5 | export const strictProps = new Set(); 6 | strictProps.add('volume'); 7 | strictProps.add('defaultChecked'); 8 | 9 | export const booleanProps = new Set(); 10 | booleanProps.add('muted'); 11 | booleanProps.add('scoped'); 12 | booleanProps.add('loop'); 13 | booleanProps.add('open'); 14 | booleanProps.add('checked'); 15 | booleanProps.add('default'); 16 | booleanProps.add('capture'); 17 | booleanProps.add('disabled'); 18 | booleanProps.add('readOnly'); 19 | booleanProps.add('required'); 20 | booleanProps.add('autoplay'); 21 | booleanProps.add('controls'); 22 | booleanProps.add('seamless'); 23 | booleanProps.add('reversed'); 24 | booleanProps.add('allowfullscreen'); 25 | booleanProps.add('novalidate'); 26 | booleanProps.add('hidden'); 27 | booleanProps.add('autoFocus'); 28 | booleanProps.add('selected'); 29 | 30 | export const namespaces = new Map(); 31 | namespaces.set( 'xlink:href', xlinkNS); 32 | namespaces.set( 'xlink:arcrole', xlinkNS); 33 | namespaces.set( 'xlink:actuate', xlinkNS); 34 | namespaces.set( 'xlink:show', xlinkNS); 35 | namespaces.set( 'xlink:role', xlinkNS); 36 | namespaces.set( 'xlink:title', xlinkNS); 37 | namespaces.set( 'xlink:type', xlinkNS); 38 | namespaces.set( 'xml:base', xmlNS); 39 | namespaces.set( 'xml:lang', xmlNS); 40 | namespaces.set( 'xml:space', xmlNS); 41 | 42 | export const isUnitlessNumber = new Set(); 43 | isUnitlessNumber.add('animationIterationCount'); 44 | isUnitlessNumber.add('borderImageOutset'); 45 | isUnitlessNumber.add('borderImageSlice'); 46 | isUnitlessNumber.add('borderImageWidth'); 47 | isUnitlessNumber.add('boxFlex'); 48 | isUnitlessNumber.add('boxFlexGroup'); 49 | isUnitlessNumber.add('boxOrdinalGroup'); 50 | isUnitlessNumber.add('columnCount'); 51 | isUnitlessNumber.add('flex'); 52 | isUnitlessNumber.add('flexGrow'); 53 | isUnitlessNumber.add('flexPositive'); 54 | isUnitlessNumber.add('flexShrink'); 55 | isUnitlessNumber.add('flexNegative'); 56 | isUnitlessNumber.add('flexOrder'); 57 | isUnitlessNumber.add('gridRow'); 58 | isUnitlessNumber.add('gridColumn'); 59 | isUnitlessNumber.add('fontWeight'); 60 | isUnitlessNumber.add('lineClamp'); 61 | isUnitlessNumber.add('lineHeight'); 62 | isUnitlessNumber.add('opacity'); 63 | isUnitlessNumber.add('order'); 64 | isUnitlessNumber.add('orphans'); 65 | isUnitlessNumber.add('tabSize'); 66 | isUnitlessNumber.add('widows'); 67 | isUnitlessNumber.add('zIndex'); 68 | isUnitlessNumber.add('zoom'); 69 | isUnitlessNumber.add('fillOpacity'); 70 | isUnitlessNumber.add('floodOpacity'); 71 | isUnitlessNumber.add('stopOpacity'); 72 | isUnitlessNumber.add('strokeDasharray'); 73 | isUnitlessNumber.add('strokeDashoffset'); 74 | isUnitlessNumber.add('strokeMiterlimit'); 75 | isUnitlessNumber.add('strokeOpacity'); 76 | isUnitlessNumber.add('strokeWidth'); 77 | 78 | export const skipProps = new Set(); 79 | skipProps.add('children'); 80 | skipProps.add('childrenType'); 81 | skipProps.add('defaultValue'); 82 | skipProps.add('ref'); 83 | skipProps.add('key'); 84 | skipProps.add('checked'); 85 | skipProps.add('multiple'); 86 | 87 | export const delegatedEvents = new Set(); 88 | delegatedEvents.add('onClick'); 89 | delegatedEvents.add('onMouseDown'); 90 | delegatedEvents.add('onMouseUp'); 91 | delegatedEvents.add('onMouseMove'); 92 | delegatedEvents.add('onSubmit'); 93 | delegatedEvents.add('onDblClick'); 94 | delegatedEvents.add('onKeyDown'); 95 | delegatedEvents.add('onKeyUp'); 96 | delegatedEvents.add('onKeyPress'); 97 | -------------------------------------------------------------------------------- /test/karma/karma.base.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const grep = process.env.TEST_GREP_FILTER || false; 4 | const filter = process.env.PKG_FILTER || '*'; 5 | const distes = 'packages/*/dist-es/**/*'; 6 | const dist = 'packages/*/index.js'; 7 | const benchmarks = `packages/${filter}/__benchmarks__/**/*`; 8 | const tests = `packages/${filter}/__tests__/**/*`; 9 | 10 | console.log({ filter, grep }); 11 | 12 | module.exports = function (config) { 13 | if (grep) { 14 | config.set({ 15 | client: { 16 | mocha: { 17 | grep // passed directly to mocha 18 | } 19 | } 20 | }); 21 | } 22 | 23 | config.set({ 24 | basePath: path.resolve(__dirname, '..', '..'), 25 | browsers: [ 26 | 'Chrome' 27 | ], 28 | preprocessors: { 29 | [distes]: ['webpack'], 30 | [dist]: ['webpack'], 31 | [benchmarks]: ['webpack'], 32 | [tests]: ['webpack'] 33 | }, 34 | webpack: { 35 | module: { 36 | loaders: [ 37 | { 38 | test: /\.jsx?$/, 39 | loader: 'babel-loader', 40 | exclude: /node_modules/, 41 | query: { 42 | // TODO: This is workaround because some of devDeps are shipping ES6... 43 | babelrc: false, 44 | presets: [ 45 | [ 'es2015', { loose: true, modules: false }], 46 | 'stage-2' 47 | ], 48 | plugins: [ 49 | 'transform-class-properties', 50 | 'transform-object-rest-spread', 51 | 'babel-plugin-syntax-jsx', 52 | [ 'babel-plugin-inferno', { imports: true }], 53 | [ 'module-resolver', { 54 | extensions: [ '.js', '.jsx' ], 55 | alias: { 56 | 'inferno-compat': './packages/inferno-compat/dist-es', 57 | 'inferno-component': './packages/inferno-component/dist-es', 58 | 'inferno-create-class': './packages/inferno-create-class/dist-es', 59 | 'inferno-create-element': './packages/inferno-create-element/dist-es', 60 | 'inferno-shared': './packages/inferno-shared/dist-es', 61 | 'inferno-hyperscript': './packages/inferno-hyperscript/dist-es', 62 | 'inferno-mobx': './packages/inferno-mobx/dist-es', 63 | 'inferno-redux': './packages/inferno-redux/dist-es', 64 | 'inferno-router': './packages/inferno-router/dist-es', 65 | 'inferno-server': './packages/inferno-server/dist-es', 66 | inferno: './packages/inferno/dist-es' 67 | } 68 | }] 69 | ] 70 | } 71 | } 72 | ] 73 | }, 74 | resolve: { 75 | extensions: [ '.js', '.jsx' ], 76 | mainFields: [ 'inferno:main', 'module', 'main' ] 77 | }, 78 | performance: { 79 | hints: false 80 | } 81 | }, 82 | webpackMiddleware: { 83 | stats: 'errors-only', 84 | noInfo: true 85 | }, 86 | 87 | concurrency: 1, 88 | browserConsoleLogOptions: { 89 | terminal: true 90 | }, 91 | browserDisconnectTimeout: 10000, 92 | browserDisconnectTolerance: 2, 93 | browserLogOptions: { 94 | terminal: true 95 | }, 96 | browserNoActivityTimeout: 2 * 60 * 1000, 97 | captureTimeout: 2 * 60 * 10000, 98 | autoWatch: false, 99 | singleRun: true, 100 | failOnEmptyTestSuite: false 101 | }); 102 | 103 | const ci = String(process.env.CI).match(/^(1|true)$/gi); 104 | if (ci) { 105 | const travisLaunchers = { 106 | Chrome_travis_ci: { 107 | base: 'Chrome', 108 | flags: ['--no-sandbox'] 109 | } 110 | }; 111 | config.set({ 112 | customLaunchers: travisLaunchers, 113 | browsers: [ 114 | 'Firefox', 115 | 'Chrome_travis_ci' 116 | ] 117 | }); 118 | } 119 | }; 120 | -------------------------------------------------------------------------------- /packages/inferno/src/DOM/recycling.ts: -------------------------------------------------------------------------------- 1 | import { isNull, isUndefined, LifecycleClass } from 'inferno-shared'; 2 | import VNodeFlags from 'inferno-vnode-flags'; 3 | import { Refs, VNode } from '../core/VNodes'; 4 | import { patchComponent, patchElement } from './patching'; 5 | 6 | const componentPools = new Map(); 7 | const elementPools = new Map(); 8 | 9 | interface Pools { 10 | nonKeyed: VNode[]; 11 | keyed: Map; 12 | } 13 | 14 | export function recycleElement(vNode: VNode, lifecycle: LifecycleClass, context: Object, isSVG: boolean) { 15 | const tag = vNode.type as string | null; 16 | const pools: Pools|undefined = elementPools.get(tag); 17 | 18 | if (!isUndefined(pools)) { 19 | const key = vNode.key; 20 | const pool = key === null ? pools.nonKeyed : pools.keyed.get(key); 21 | 22 | if (!isUndefined(pool)) { 23 | const recycledVNode = pool.pop(); 24 | 25 | if (!isUndefined(recycledVNode)) { 26 | patchElement(recycledVNode, vNode, null, lifecycle, context, isSVG, true); 27 | return vNode.dom; 28 | } 29 | } 30 | } 31 | return null; 32 | } 33 | 34 | export function poolElement(vNode: VNode) { 35 | const tag = vNode.type as string | null; 36 | const key = vNode.key; 37 | let pools: Pools|undefined = elementPools.get(tag); 38 | 39 | if (isUndefined(pools)) { 40 | pools = { 41 | keyed: new Map(), 42 | nonKeyed: [] 43 | }; 44 | elementPools.set(tag, pools); 45 | } 46 | if (isNull(key)) { 47 | pools.nonKeyed.push(vNode); 48 | } else { 49 | let pool = pools.keyed.get(key); 50 | 51 | if (isUndefined(pool)) { 52 | pool = []; 53 | pools.keyed.set(key, pool); 54 | } 55 | pool.push(vNode); 56 | } 57 | } 58 | 59 | export function recycleComponent(vNode: VNode, lifecycle: LifecycleClass, context: Object, isSVG: boolean) { 60 | const type = vNode.type as Function; 61 | const pools: Pools|undefined = componentPools.get(type); 62 | 63 | if (!isUndefined(pools)) { 64 | const key = vNode.key; 65 | const pool = key === null ? pools.nonKeyed : pools.keyed.get(key); 66 | 67 | if (!isUndefined(pool)) { 68 | const recycledVNode = pool.pop(); 69 | 70 | if (!isUndefined(recycledVNode)) { 71 | const flags = vNode.flags; 72 | const failed = patchComponent( 73 | recycledVNode, 74 | vNode, 75 | null, 76 | lifecycle, 77 | context, 78 | isSVG, 79 | (flags & VNodeFlags.ComponentClass) > 0, 80 | true 81 | ); 82 | 83 | if (!failed) { 84 | return vNode.dom; 85 | } 86 | } 87 | } 88 | } 89 | return null; 90 | } 91 | 92 | export function poolComponent(vNode: VNode) { 93 | const hooks = vNode.ref as Refs; 94 | const nonRecycleHooks = hooks && ( 95 | hooks.onComponentWillMount || 96 | hooks.onComponentWillUnmount || 97 | hooks.onComponentDidMount || 98 | hooks.onComponentWillUpdate || 99 | hooks.onComponentDidUpdate 100 | ); 101 | if (nonRecycleHooks) { 102 | return; 103 | } 104 | const type = vNode.type; 105 | const key = vNode.key; 106 | let pools: Pools|undefined = componentPools.get(type as Function); 107 | 108 | if (isUndefined(pools)) { 109 | pools = { 110 | keyed: new Map(), 111 | nonKeyed: [] 112 | }; 113 | componentPools.set(type as Function, pools); 114 | } 115 | if (isNull(key)) { 116 | pools.nonKeyed.push(vNode); 117 | } else { 118 | let pool = pools.keyed.get(key); 119 | 120 | if (isUndefined(pool)) { 121 | pool = []; 122 | pools.keyed.set(key, pool); 123 | } 124 | pool.push(vNode); 125 | } 126 | } 127 | --------------------------------------------------------------------------------