diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..e7822e5 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,22 @@ +{ + "env": { + "browser": false, + "amd": false, + "es6": true, + "node": true, + "mocha": true + }, + "rules": { + "comma-dangle": 1, + "quotes": [ 1, "single" ], + "no-undef": 1, + "global-strict": 0, + "no-extra-semi": 1, + "no-underscore-dangle": 0, + "no-console": 0, + "no-unused-vars": 1, + "no-trailing-spaces": [1, { "skipBlankLines": true }], + "no-unreachable": 1, + "no-alert": 0 + } +} diff --git a/.gitignore b/.gitignore index 17d340a..7e8a112 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,3 @@ $RECYCLE.BIN/ node_modules/ npm-debug.log .idea/ -/test/temp-test diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index da64b6e..0000000 --- a/.jshintrc +++ /dev/null @@ -1,21 +0,0 @@ -{ - "node": true, - "esnext": true, - "bitwise": true, - "camelcase": true, - "curly": true, - "eqeqeq": true, - "immed": true, - "indent": 2, - "latedef": true, - "newcap": true, - "noarg": true, - "quotmark": "single", - "regexp": true, - "undef": true, - "unused": true, - "strict": true, - "trailing": true, - "smarttabs": true, - "white": true -} diff --git a/README.md b/README.md index 88b7e58..a763b10 100644 --- a/README.md +++ b/README.md @@ -1,359 +1,76 @@ -# generator-react-webpack [![Build Status](https://secure.travis-ci.org/newtriks/generator-react-webpack.png?branch=master)](https://travis-ci.org/newtriks/generator-react-webpack) [![Built with Grunt](https://cdn.gruntjs.com/builtwith.png)](http://gruntjs.com/) +# generator-react-webpack V2.0 [![Build Status](https://secure.travis-ci.org/newtriks/generator-react-webpack.png?branch=master)](https://travis-ci.org/newtriks/generator-react-webpack) [![Built with Grunt](https://cdn.gruntjs.com/builtwith.png)](http://gruntjs.com/) > Yeoman generator for [ReactJS](http://facebook.github.io/react/) - lets you quickly set up a project including karma test runner and [Webpack](http://webpack.github.io/) module system. +# About +Generator-React-Webpack will help you build new Reactprojects using modern technologies. -## Usage +Out of the box it comes with support for: +- Webpack +- ES2015 via Babel-Loader +- Different supported style languages (sass, scss, less, stylus) +- Automatic code linting via esLint +- Ability to unit test components via Karma and Mocha/Chai -Install `generator-react-webpack`: -``` -npm install -g generator-react-webpack -``` +## Subgenerators +This generator is also the base project for some other generators that depend on it: +### Generator-React-Webpack-Alt +(https://github.com/weblogixx/generator-react-webpack-alt) +Generator based on generator-react-webpack, providing support for Alt.js. -Make a new directory, and `cd` into it: -``` -mkdir my-new-project && cd $_ -``` +--- -Run `yo react-webpack`, optionally passing an app name: -``` -yo react-webpack [app-name] -``` - -Run `grunt build` for building and `grunt serve` for preview in the browser at [localhost](http://localhost:8000). - -## Generators - -Available generators: - -* [react-webpack](#app) (aka [react-webpack:app](#app)) -* [react-webpack:component](#component) - -and for **Flux** or **Reflux** : -* [react-webpack:action](#action) -* [react-webpack:store](#store) - - -### App - -Sets up a new ReactJS app, generating all the boilerplate you need to get started. The app generator also facilitates the following: - -1. Configures a Gruntfile to run the app on a local server. -2. Configures Webpack to modularise the app enabling [loading of various file formats](http://webpack.github.io/docs/loader-list.html) e.g. JSON, CSS, PNG, etc. -3. Configures [Karma](http://karma-runner.github.io) to run all tests. -4. Watches for changes and recompiles JS and refreshes the browser. - -Example: +## Installation ```bash +npm install -g yo +npm install generator-react-webpack +``` + +## Setting up projects +```bash +# Create a new directory, and `cd` into it: +mkdir my-new-project && cd my-new-project + +# Run the generator yo react-webpack ``` -### Component +Please make sure to edit your newly generated `package.json` file to set description, author information and the like. -Generates a [JSX](http://facebook.github.io/react/docs/jsx-in-depth.html) component in `src/components`, its corresponding test in `test/spec/components` and its style in `src/styles`. - -Example: +## Generating new components ```bash -yo react-webpack:component foo //or just: yo react-webpack:c foo +# After setup of course :) +# cd my-new-project +yo react-webpack:component my/namespaced/components/name ``` -Produces `src/components/Foo.js` (*javascript - JSX*): -```js -'use strict'; +The above command will create a new component, as well as its stylesheet and a basic testcase. -var React = require('react/addons'); - -require('styles/componentName.css'); //or .sass,.less etc... - -var Foo = React.createClass({ - render: function () { - return ( -
-

Content for Foo

-
- ) - } -}); - -module.exports = Foo; -``` - -And `test/spec/components/Foo.js` (*javascript - jasmine, as seen on http://simonsmith.io/unit-testing-react-components-without-a-dom/*): -```js -'use strict'; - -// Uncomment the following lines to use the react test utilities -// import React from 'react/addons'; -// const TestUtils = React.addons.TestUtils; - -import createComponent from 'helpers/createComponent'; -import Foo from 'components/Foo.js'; - -describe('Foo', () => { - - let FooComponent; - - beforeEach(() => { - FooComponent = createComponent(Foo); - }); - - it('should have its component name as default className', () => { - expect(FooComponent._store.props.className).toBe('Foo'); - }); -}); -``` - -And `src/styles/Foo.css` (or .sass, .less etc...) : -```css -.Foo { - border: 1px dashed #f00; -} -``` - -### rich flag - -For all you lazy programmers out there, we've added another shortcut - `rich` flag: +## Usage +The following commands are available in your project: ```bash -yo react-webpack:c foofoo --rich +# Start for development +npm start # or +npm run serve + +# Start the dev-server with the dist version +npm run serve:dist + +# Just build the dist version and copy static files +npm run dist + +# Run unit tests +npm test + +# Lint all files in src (also automatically done AFTER tests are run) +npm run lint + +# Clean up the dist directory +npm run clean + +# Just copy the static assets +npm run copy ``` -This will give you all of react component's most common stuff : - ```js - var React = require('react/addons'); - - require('styles/Foofoo.sass'); - - var Foofoo = React.createClass({ - mixins: [], - getInitialState: function() { - return {}; - }, - getDefaultProps: function() {}, - componentWillMount: function() {}, - componentDidMount: function() {}, - shouldComponentUpdate: function() {}, - componentDidUpdate: function() {}, - componentWillUnmount: function() {}, - - render: function () { - return ( -
-

Content for Foofoo

-
- ); - } - }); - - module.exports = Foofoo; - ``` - -Just remove those you don't need, then fill and space out the rest. - -### Action - -When using Flux or Reflux architecture, it generates an actionCreator in `src/actions` and it's corresponding test in `src/spec/actions`. - -Example: -```bash -yo react-webpack:action bar //or just: yo react-webpack:a bar -``` -Will create a file - `src/actions/BarActionCreators.js` - -if 'architecture' is **Flux**, it Produces : -```js -'use strict'; - -var BarActionCreators = { - -} - -module.exports = BarActionCreators; -``` -And if it's **Reflux**: -```js -'use strict'; - -var Reflux = require('reflux'); - -var BarActionCreators = Reflux.createActions([ - -]); - - -module.exports = BarActionCreators; -``` - -and same test for both architectures: -```js -'use strict'; - -describe('BarActionCreators', () => { - let action; - - beforeEach(function() { - action = require('actions/BarActionCreators.js'); - }); - - it('should be defined', () => { - expect(action).toBeDefined(); - }); -}); -``` - -### Store - -When using Flux or Reflux architecture, it generates a store in `src/stores` and it's corresponding test in `src/spec/stores`. - -Example: -```bash -yo react-webpack:store baz //or just: yo react-webpack:s baz -``` -Will create a file - `src/stores/BazStore.js` - -if 'architecture' is **Flux**, it Produces : -```js -'use strict'; - -var EventEmitter = require('events').EventEmitter; -var assign = require('object-assign'); -var MainAppDispatcher = require('../dispatcher/MainAppDispatcher'); - -var BazStore = assign({}, EventEmitter.prototype, { - -}); - -BazStore.dispatchToken = MainAppDispatcher.register(function(action) { - - switch(action.type) { - default: - } - -}); - -module.exports = BazStore; -``` -And if it's **Reflux**: -```js -'use strict'; - -var Reflux = require('reflux'); -//var Actions = require('actions/..'); - - -var BazStore = Reflux.createStore({ - listenables: Actions, - - -}); - -module.exports = BazStore; -``` - -and same test for both architectures: -```js -'use strict'; - -describe('BazStore', () => { - let store; - - beforeEach(() => { - store = require('stores/BazStore.js'); - }); - - it('should be defined', () => { - expect(store).toBeDefined(); - }); -}); -``` - - -## Options -Options are available as additional installs to the initial application generation phase. - -### [ReactRouter](https://github.com/rackt/react-router) - -A complete routing library for React. This option only adds the basic hooks to get started with [react router](https://github.com/rackt/react-router). - -### styles language - -css, sass, scss, less or stylus - -Sets the style file's template and extension - -### component suffix - -js or jsx - -Sets the file suffix for generated components. Defaults to "js". Please note that you need to require files *including* the file ending when using jsx as suffix. Example: - -```js -var MyJSComponent = require('./MyJSComponent'); -var MyJSX = require('./MyJSX.jsx'); -``` - -### architecture - -[flux](https://facebook.github.io/flux/) or [reflux](https://github.com/spoike/refluxjs) - -### es6 - -If you are using `es6`, and want to use its export functionality (and not webpack's), just add `--es6` flag when you create a component, action or store. - - -## Testing - -Running `grunt test` will run the unit tests with karma. Tests are written using [Jasmine](http://jasmine.github.io/) by default. - -## Further Information - -### Project Structure - -The react-webpack generator automates the setup of a [ReactJS](http://facebook.github.io/react/) project using the specific structure detailed below: - -``` -project - - src - -components - MainApp.js - Foo.js - AnotherComponent.js - - //for flux/reflux - -actions - BarActionCreators.js - -stores - BazStore.js - //for flux - -dispatcher - FooAppDispatcher - - - styles - main.css - index.html - - test - - spec - - components - MainApp.js - Foo.js - AnotherComponent.js - - //for flux/reflux - -actions - BarActionCreators.js - -stores - BazStore.js - - - helpers - - react - addons.js - phantomjs-shims.js - Gruntfile.js - karma.conf.js - package.json - webpack.config.js - webpack.dist.config.js -``` - -I have tried to keep the project structure as simple as possible and understand it may not suit everyone. ### Naming Components @@ -363,27 +80,6 @@ I have opted to follow [@floydophone](https://twitter.com/floydophone) conventio Each component is a module and can be required using the [Webpack](http://webpack.github.io/) module system. [Webpack](http://webpack.github.io/) uses [Loaders](http://webpack.github.io/docs/loaders.html) which means you can also require CSS and a host of other file types. Read the [Webpack documentation](http://webpack.github.io/docs/home.html) to find out more. -### Grunt - -Out the box the [Gruntfile](http://gruntjs.com/api/grunt.file) is configured with the following: - -1. **webpack**: uses the [grunt-webpack](https://github.com/webpack/grunt-webpack) plugin to load all required modules and output to a single JS file `src/main.js`. This is included in the `src/index.html` file by default and will reload in the browser as and when it is recompiled. -2. **webpack-dev-server**: uses the [webpack-dev-server](https://github.com/webpack/webpack-dev-server) to watch for file changes and also serve the webpack app in development. -3. **connect**: uses the [grunt-connect](https://github.com/gruntjs/grunt-contrib-connect) plugin to start a webserver at [localhost](http://localhost:8000). -4. **karma**: uses the [grunt-karma](https://github.com/karma-runner/grunt-karma) plugin to load the Karma configuration file `karma.conf.js` located in the project root. This will run all tests using [PhantomJS](http://phantomjs.org/) by default but supports many other browsers. Please note that karma-launchers other than PhantomJS must be installed separately and configured in `karma.conf.js`. - -### CSS - -Included in the project is the [normalize.css](http://necolas.github.io/normalize.css/) script. There is also a `src/styles/main.css` script that's required by the core `src/components/App.js` component using Webpack. - -### Linting - -Webpack is automatically configured to run esLint (http://eslint.org) on every file change or build. The configuration can be found in `PROJECTROOT/.eslintrc`. There are plugins for different editors that use this tool directly: -- linter-eslint for Atom -- Sublime-Linter-eslint for Sublime - -You could also use jsxhint, the corresponding rules file is located in `PROJECTROOT/.jshintrc`. However, the support for jsxhint is planned to be dropped in a later release and only available for backwards compatibility. - ## Props Thanks to all who contributed to [generator-angular](https://github.com/yeoman/generator-angular) as the majority of code here has been shamelessy sourced from that repos. @@ -396,7 +92,7 @@ Contributions are welcomed. When submitting a bugfix, write a test that exposes ### Running Tests -`node node_modules/.bin/mocha` +`npm test` or `node node_modules/.bin/mocha` ## License diff --git a/a/index.js b/a/index.js deleted file mode 100644 index eeffccf..0000000 --- a/a/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../action'); diff --git a/action/index.js b/action/index.js deleted file mode 100644 index 228642b..0000000 --- a/action/index.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict'; -var util = require('util'); -var ScriptBase = require('../script-base.js'); - -var ActionGenerator = module.exports = function ActionGenerator(args, options, config) { - if (!args[0]) console.log('\n Please specify a name for this action creator \n'); - else { - args[0] += 'ActionCreators'; - ScriptBase.apply(this, arguments) - } -}; - -util.inherits(ActionGenerator, ScriptBase); - -ActionGenerator.prototype.createActionFile = function createActionFile() { - this.option('es6'); - this.es6 = this.options.es6; - - var actionTemplate; - switch (this.architecture){ - case 'flux': - actionTemplate = 'FluxAction'; - break; - case 'reflux': - actionTemplate = 'RefluxAction'; - break; - case 'alt': - actionTemplate = 'AltAction'; - break; - } - - console.log('Creating ' + this.architecture + ' action'); - - this.generateSourceAndTest( - actionTemplate, - 'spec/Action', - 'actions' - ); -}; diff --git a/app/index.js b/app/index.js deleted file mode 100644 index 2fa6982..0000000 --- a/app/index.js +++ /dev/null @@ -1,164 +0,0 @@ -'use strict'; -var util = require('util'); -var path = require('path'); -var yeoman = require('yeoman-generator'); -var generalUtils = require('../util.js'); - -var ReactWebpackGenerator = module.exports = function ReactWebpackGenerator(args, options, config) { - yeoman.generators.Base.apply(this, arguments); - this.option('es6'); - - this.argument('appname', { type: String, required: false }); - this.appname = this.appname || path.basename(process.cwd()); - this.appname = this._.camelize(this._.slugify(this._.humanize(this.appname))); - this.scriptAppName = this._.capitalize(this.appname) + generalUtils.appName(this); - - this.config.set('app-name', this.appname); - - - if (typeof this.options.appPath === 'undefined') { - this.options.appPath = this.options.appPath || 'src'; - } - - this.appPath = this.options.appPath; - - args = [this.scriptAppName]; - - this.composeWith('react-webpack:common', { - args: args - }); - - this.composeWith('react-webpack:main', { - args: args - }); - - this.on('end', function () { - this.installDependencies({ skipInstall: options['skip-install'], bower: false }); - }); - - - this.pkg = JSON.parse(this.readFileAsString(path.join(__dirname, '../package.json'))); - - this.config.save(); -}; - -util.inherits(ReactWebpackGenerator, yeoman.generators.Base); - -ReactWebpackGenerator.prototype.welcome = function welcome() { - // welcome message - if (!this.options['skip-welcome-message']) { - console.log(this.yeoman); - console.log( - 'Out of the box I include Webpack and some default React components.\n' - ); - } -}; - -ReactWebpackGenerator.prototype.askForReactRouter = function () { - var done = this.async(); - this.prompt({ - type : 'confirm', - name : 'reactRouter', - message : 'Would you like to include react-router?', - default : true - }, function (props) { - this.env.options.reactRouter = props.reactRouter; - done(); - }.bind(this)); -}; - -ReactWebpackGenerator.prototype.askForArchitecture = function() { - var done = this.async(); - this.prompt({ - type : 'list', - name : 'architecture', - message : 'Would you like to use one of these architectures?', - choices: [ - {name:'No need for that, thanks',value:false}, - {name:'Flux',value:'flux'}, - {name:'ReFlux',value:'reflux'}, - {name:'Alt',value:'alt'} - ], - default : false - }, function(props) { - this.env.options.architecture = props.architecture; - this.config.set('architecture', props.architecture); - done(); - }.bind(this)); -}; - -ReactWebpackGenerator.prototype.askForStylesLanguage = function () { - var done = this.async(); - this.prompt({ - type : 'list', - name : 'stylesLanguage', - message : 'Which styles language you want to use?', - choices: [ - {name: 'CSS', value: 'css'}, - {name: 'SASS', value: 'sass'}, - {name: 'SCSS', value: 'scss'}, - {name: 'LESS', value: 'less'}, - {name: 'Stylus', value: 'stylus'} - ], - default : 'css' - }, function (props) { - this.env.options.stylesLanguage = props.stylesLanguage; - this.config.set('styles-language', props.stylesLanguage); - done(); - }.bind(this)); -}; - -// Allow to set the generated files suffix for the project when using components -// @see https://github.com/newtriks/generator-react-webpack/issues/99 -ReactWebpackGenerator.prototype.askForComponentSuffix = function() { - var done = this.async(); - this.prompt({ - type: 'list', - name: 'componentSuffix', - message: 'Which file suffix do you want to use for components?', - choices: [ - { name: '.js (default)', value: 'js' }, - { name: '.jsx (deprecated)', value: 'jsx' } - ], - default: 'js' - }, function (props) { - this.env.options.componentSuffix = props.componentSuffix; - this.config.set('component-suffix', props.componentSuffix); - done(); - }.bind(this)); -}; - -ReactWebpackGenerator.prototype.readIndex = function readIndex() { - this.indexFile = this.engine(this.read('../../templates/common/index.html'), this); -}; - -ReactWebpackGenerator.prototype.createIndexHtml = function createIndexHtml() { - this.indexFile = this.indexFile.replace(/'/g, "'"); - this.write(path.join(this.appPath, 'index.html'), this.indexFile); -}; - -ReactWebpackGenerator.prototype.packageFiles = function () { - this.es6 = this.options.es6; - this.reactRouter = this.env.options.reactRouter; - this.architecture = this.env.options.architecture; - this.stylesLanguage = this.env.options.stylesLanguage; - this.template('../../templates/common/_package.json', 'package.json'); - this.template('../../templates/common/_webpack.config.js', 'webpack.config.js'); - this.template('../../templates/common/_webpack.dist.config.js', 'webpack.dist.config.js'); - this.copy('../../templates/common/Gruntfile.js', 'Gruntfile.js'); - this.copy('../../templates/common/gitignore', '.gitignore'); -}; - -ReactWebpackGenerator.prototype.styleFiles = function styleFiles() { - var mainFile = 'main.css'; - this.copy('styles/' + mainFile, 'src/styles/' + mainFile); -}; - -ReactWebpackGenerator.prototype.imageFiles = function () { - this.sourceRoot(path.join(__dirname, 'templates')); - this.directory('images', 'src/images', true); -}; - -ReactWebpackGenerator.prototype.karmaFiles = function () { - this.copy('../../templates/common/karma.conf.js', 'karma.conf.js'); -}; diff --git a/app/templates/images/yeoman.png b/app/templates/images/yeoman.png deleted file mode 100644 index 92497ad..0000000 Binary files a/app/templates/images/yeoman.png and /dev/null differ diff --git a/app/templates/styles/main.css b/app/templates/styles/main.css deleted file mode 100644 index 321e9a1..0000000 --- a/app/templates/styles/main.css +++ /dev/null @@ -1,41 +0,0 @@ -/* Stiziles */ - -html, body { - background: #222222; -} - -/* main */ - -.main { - width: 100%; - height: 100%; - background: #222222; - color: #fff; -} - -.main img { - width: 103px; - height: 89px; - margin-bottom: 10px; - text-align: center; -} - -/* transitions */ - -.fade-enter { - opacity: 0.01; - transition: opacity .5s ease-in; -} - -.fade-enter.fade-enter-active { - opacity: 1; -} - -.fade-leave { - opacity: 1; - transition: opacity .5s ease-in; -} - -.fade-leave.fade-leave-active { - opacity: 0.01; -} diff --git a/c/index.js b/c/index.js deleted file mode 100644 index d61f628..0000000 --- a/c/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../component'); diff --git a/common/index.js b/common/index.js deleted file mode 100644 index 0452f1a..0000000 --- a/common/index.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; -var path = require('path'); -var util = require('util'); -var yeoman = require('yeoman-generator'); - -var CommonGenerator = module.exports = function CommonGenerator(args, options, config) { - yeoman.generators.NamedBase.apply(this, arguments); -}; - -util.inherits(CommonGenerator, yeoman.generators.NamedBase); - -CommonGenerator.prototype.setupEnv = function setupEnv() { - // Copies the contents of the generator `templates` - // directory into your users new application path - this.sourceRoot(path.join(__dirname, '../templates/common')); - this.directory('root', '.', true); -}; diff --git a/component/index.js b/component/index.js deleted file mode 100644 index 28223e5..0000000 --- a/component/index.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict'; -var util = require('util'); -var ScriptBase = require('../script-base.js'); - -var ComponentGenerator = module.exports = function ComponentGenerator(args, options, config) { - ScriptBase.apply(this, arguments); -}; - -util.inherits(ComponentGenerator, ScriptBase); - -ComponentGenerator.prototype.createComponentFile = function createComponentFile() { - this.option('es6'); - this.option('rich'); - - this.es6 = this.options.es6; - this.rich = this.options.rich; - - this.generateComponentTestAndStyle( - 'Component', - 'spec/Component', - 'styles/Component', - 'components', - true - ); -}; diff --git a/generators/app/index.js b/generators/app/index.js new file mode 100644 index 0000000..54de5ab --- /dev/null +++ b/generators/app/index.js @@ -0,0 +1,124 @@ +'use strict'; +let generator = require('yeoman-generator'); +let utils = require('../../utils/all'); +let prompts = require('./prompts'); +let path = require('path'); +let fs = require('fs'); + +// Set the base root directory for our files +let baseRootPath = path.join(__dirname, '../../node_modules/react-webpack-template'); + +module.exports = generator.Base.extend({ + + constructor: function() { + generator.Base.apply(this, arguments); + + // Make options available + this.option('skip-welcome-message', { + desc: 'Skip the welcome message', + type: Boolean, + defaults: false + }); + this.option('skip-install'); + + // Use our plain template as source + this.sourceRoot(baseRootPath); + + this.config.save(); + }, + + initializing: function() { + if(!this.options['skip-welcome-message']) { + this.log(require('yeoman-welcome')); + this.log('Out of the box I include Webpack and some default React components.\n'); + } + }, + + prompting: function() { + let done = this.async(); + this.prompt(prompts, function(props) { + + // Make sure to get the correct app name if it is not the default + if(props.appName !== utils.yeoman.getAppName()) { + props.appName = utils.yeoman.getAppName(props.appName); + } + + // Set needed global vars for yo + this.appName = props.appName; + this.style = props.style; + + // Set needed keys into config + this.config.set('appName', this.appName); + this.config.set('appPath', this.appPath); + this.config.set('style', this.style); + + done(); + }.bind(this)); + }, + + configuring: function() { + + // Generate our package.json. Make sure to also include the required dependencies for styles + let defaultSettings = this.fs.readJSON(path.join(baseRootPath, 'package.json')); + let packageSettings = { + name: this.appName, + private: true, + version: '0.0.1', + description: 'YOUR DESCRIPTION - Generated by generator-react-webpack', + main: '', + scripts: defaultSettings.scripts, + repository: '', + keywords: [], + author: 'Your name here', + devDependencies: defaultSettings.devDependencies, + dependencies: defaultSettings.dependencies + }; + + // Add needed loaders if we have special styles + let styleConfig = utils.config.getChoiceByKey('style', this.style); + if(styleConfig && styleConfig.packages) { + + for(let dependency of styleConfig.packages) { + packageSettings.dependencies[dependency.name] = dependency.version; + } + } + + this.fs.writeJSON(this.destinationPath('package.json'), packageSettings); + }, + + writing: function() { + + let excludeList = [ + 'LICENCE', + 'README.md', + 'node_modules', + 'package.json' + ]; + + // Get all files in our repo and copy the ones we should + fs.readdir(this.sourceRoot(), (err, items) => { + + for(let item of items) { + + // Skip the item if it is in our exclude list + if(excludeList.indexOf(item) !== -1) { + continue; + } + + // Copy all items to our root + let fullPath = path.join(baseRootPath, item); + if(fs.lstatSync(fullPath).isDirectory()) { + this.bulkDirectory(item, item); + } else { + this.copy(item, item); + } + } + }); + }, + + install: function() { + if(!this.options['skip-install']) { + this.installDependencies({ bower: false }); + } + } +}); diff --git a/generators/app/prompts.js b/generators/app/prompts.js new file mode 100644 index 0000000..5c447ea --- /dev/null +++ b/generators/app/prompts.js @@ -0,0 +1,18 @@ +'use strict'; +let utils = require('../../utils/all'); + +module.exports = [ + { + type: 'input', + name: 'appName', + message: 'Please choose your application name', + default: utils.yeoman.getAppName() + }, + { + type: 'list', + name: 'style', + message: 'Which styles language you want to use?', + choices: utils.config.getChoices('style'), + default: utils.config.getDefaultChoice('style') + } +]; diff --git a/generators/component/index.js b/generators/component/index.js new file mode 100644 index 0000000..93fae40 --- /dev/null +++ b/generators/component/index.js @@ -0,0 +1,36 @@ +'use strict'; +let generator = require('yeoman-generator'); +let utils = require('../../utils/all'); + +module.exports = generator.NamedBase.extend({ + + constructor: function() { + generator.NamedBase.apply(this, arguments); + }, + + writing: function() { + + let settings = utils.yeoman.getAllSettingsFromComponentName(this.name, this.config.get('style')); + + // Create the style template + this.fs.copyTpl( + this.templatePath(`styles/Component${settings.style.suffix}`), + this.destinationPath(settings.style.path + settings.style.fileName), + settings + ); + + // Create the component + this.fs.copyTpl( + this.templatePath('components/Base.js'), + this.destinationPath(settings.component.path + settings.component.fileName), + settings + ); + + // Create the unit test + this.fs.copyTpl( + this.templatePath('tests/Base.js'), + this.destinationPath(settings.test.path + settings.test.fileName), + settings + ); + } +}); diff --git a/generators/component/templates/components/Base.js b/generators/component/templates/components/Base.js new file mode 100644 index 0000000..27f20e6 --- /dev/null +++ b/generators/component/templates/components/Base.js @@ -0,0 +1,21 @@ +'use strict'; + +import React from 'react/addons'; + +require('<%= style.webpackPath %>'); + +class <%= component.className %> extends React.Component { + render() { + return ( +
+ Please edit <%= component.path %>/<%= component.fileName %> to update this component! +
+ ); + } +} + +// Uncomment properties you need +// <%= component.className %>.propTypes = {}; +// <%= component.className %>.defaultProps = {}; + +export default <%= component.className %>; diff --git a/templates/styles/Component.css b/generators/component/templates/styles/Component.css similarity index 52% rename from templates/styles/Component.css rename to generators/component/templates/styles/Component.css index 912c840..0a1af92 100644 --- a/templates/styles/Component.css +++ b/generators/component/templates/styles/Component.css @@ -1,3 +1,3 @@ -.<%= classedName %> { +.<%= style.className %> { border: 1px dashed #f00; } diff --git a/templates/styles/Component.less b/generators/component/templates/styles/Component.less similarity index 52% rename from templates/styles/Component.less rename to generators/component/templates/styles/Component.less index 912c840..0a1af92 100644 --- a/templates/styles/Component.less +++ b/generators/component/templates/styles/Component.less @@ -1,3 +1,3 @@ -.<%= classedName %> { +.<%= style.className %> { border: 1px dashed #f00; } diff --git a/templates/styles/Component.sass b/generators/component/templates/styles/Component.sass similarity index 52% rename from templates/styles/Component.sass rename to generators/component/templates/styles/Component.sass index 9811abb..9e9d772 100644 --- a/templates/styles/Component.sass +++ b/generators/component/templates/styles/Component.sass @@ -1,2 +1,2 @@ -.<%= classedName %> +.<%= style.className %> border: 1px dashed #f00 diff --git a/templates/styles/Component.scss b/generators/component/templates/styles/Component.scss similarity index 52% rename from templates/styles/Component.scss rename to generators/component/templates/styles/Component.scss index 912c840..0a1af92 100644 --- a/templates/styles/Component.scss +++ b/generators/component/templates/styles/Component.scss @@ -1,3 +1,3 @@ -.<%= classedName %> { +.<%= style.className %> { border: 1px dashed #f00; } diff --git a/templates/styles/Component.styl b/generators/component/templates/styles/Component.styl similarity index 51% rename from templates/styles/Component.styl rename to generators/component/templates/styles/Component.styl index 4cc329f..f37b496 100644 --- a/templates/styles/Component.styl +++ b/generators/component/templates/styles/Component.styl @@ -1,2 +1,2 @@ -.<%= classedName %> +.<%= style.className %> border 1px dashed #f00 diff --git a/generators/component/templates/tests/Base.js b/generators/component/templates/tests/Base.js new file mode 100644 index 0000000..4282623 --- /dev/null +++ b/generators/component/templates/tests/Base.js @@ -0,0 +1,23 @@ +/*eslint-env node, mocha */ +/*global expect */ +/*eslint no-console: 0*/ +'use strict'; + +// Uncomment the following lines to use the react test utilities +// import React from 'react/addons'; +// const TestUtils = React.addons.TestUtils; +import createComponent from 'helpers/shallowRenderHelper'; + +import <%= component.className %> from '<%= component.webpackPath %>'; + +describe('<%= component.className %>', () => { + let component; + + beforeEach(() => { + component = createComponent(<%= component.className %>); + }); + + it('should have its component name as default className', () => { + expect(component._store.props.className).to.equal('<%= style.className %>'); + }); +}); diff --git a/main/index.js b/main/index.js deleted file mode 100644 index 0317a5e..0000000 --- a/main/index.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -var util = require('util'); -var ScriptBase = require('../script-base.js'); - -var MainGenerator = module.exports = function MainGenerator(args, options, config) { - ScriptBase.apply(this, arguments); -}; - -util.inherits(MainGenerator, ScriptBase); - -MainGenerator.prototype.createAppFile = function createAppFile(scriptAppName) { - this.reactRouter = this.env.options.reactRouter; - this.scriptAppName = scriptAppName; - this.appTemplate('App', 'components/' + scriptAppName); - this.testTemplate('spec/App', 'components/' + scriptAppName); -}; - -MainGenerator.prototype.createMainFile = function createMainFile() { - if(this.env.options.reactRouter) { - this.appTemplate('main', 'components/main'); - } -}; - -MainGenerator.prototype.createDispatcher = function createDispatcher() { - if(this.env.options.architecture=='flux') { - this.appTemplate('Dispatcher', 'dispatcher/' + this.scriptAppName + 'Dispatcher'); - } -}; - -MainGenerator.prototype.createAltjsFile = function createAltjsFile() { - if(this.env.options.architecture=='alt') { - this.appTemplate('alt', 'alt'); - } -}; diff --git a/package.json b/package.json index dcef9c6..06dc699 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "generator-react-webpack", - "version": "1.2.13", + "version": "1.2.23", "description": "Yeoman generator for ReactJS and Webpack", "keywords": [ "yeoman-generator", @@ -19,7 +19,14 @@ "email": "simon@newtriks.com", "url": "https://github.com/newtriks" }, - "main": "app/index.js", + "contributors": [ + { + "name": "Christian Schilling", + "email": "cs@weblogixx.de", + "url": "https://github.com/weblogixx" + } + ], + "main": "generators/app/index.js", "repository": { "type": "git", "url": "git://github.com/newtriks/generator-react-webpack.git" @@ -28,24 +35,18 @@ "test": "mocha" }, "dependencies": { - "yeoman-generator": "^0.17.6" + "react-webpack-template": "^0.1.0", + "underscore.string": "^3.2.2", + "yeoman-generator": "^0.20.3", + "yeoman-welcome": "^1.0.1" }, "devDependencies": { - "mocha": "^1.21.4", - "underscore.string": "^2.3.3", - "grunt": "^0.4.5", - "grunt-cli": "^0.1.13" - }, - "peerDependencies": { - "yo": ">=1.2.0" + "chai": "^3.2.0", + "mocha": "^2.3.2" }, "engines": { - "node": ">=0.10.0", + "node": ">=4.0.0", "iojs": ">=1.1.0" }, - "licenses": [ - { - "type": "MIT" - } - ] + "license": "MIT" } diff --git a/s/index.js b/s/index.js deleted file mode 100644 index 4b2cf54..0000000 --- a/s/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../store'); diff --git a/script-base.js b/script-base.js deleted file mode 100644 index 6d16036..0000000 --- a/script-base.js +++ /dev/null @@ -1,118 +0,0 @@ -'use strict'; -var util = require('util'); -var path = require('path'); -var yeoman = require('yeoman-generator'); -var generalUtils = require('./util.js'); - -var Generator = module.exports = function Generator() { - yeoman.generators.NamedBase.apply(this, arguments); - - // Add capitalize mixin - this._.mixin({ 'capitalize': generalUtils.capitalize }); - this._.mixin({ 'capitalizeFile': generalUtils.capitalizeFile }); - this._.mixin({ 'capitalizeClass': generalUtils.capitalizeClass }); - this._.mixin({ 'lowercase': generalUtils.lowercase }); - - this.appname = path.basename(process.cwd()); - - this.appname = this._.slugify(this._.humanize(this.appname)); - this.scriptAppName = this._.camelize(this._.capitalize(this.appname)) + generalUtils.appName(this); - this.classedFileName = this._.capitalizeFile(this.name); - this.classedName = this._.capitalizeClass(this.name); - this.stylesLanguage = this.config.get('styles-language'); - this.architecture = this.config.get('architecture'); - - if (typeof this.options.appPath === 'undefined') { - this.options.appPath = this.options.appPath || 'src'; - } - - if (typeof this.options.testPath === 'undefined') { - this.options.testPath = this.options.testPath || 'test/spec'; - } - - if (typeof this.options.stylesPath === 'undefined') { - this.options.stylesPath = this.options.stylesPath || 'src/styles'; - } - - var sourceRoot = '/templates/'; - this.scriptSuffix = '.js'; - this.reactSuffix = '.js'; - - // Add support for generated file legacy fallback - // @see https://github.com/newtriks/generator-react-webpack/issues/99 - this.reactComponentSuffix = this.config.get('component-suffix'); - switch(this.reactComponentSuffix) { - case 'jsx': - this.reactComponentSuffix = '.jsx'; - break; - default: - this.reactComponentSuffix = '.js'; - } - - this.stylesSuffix = '.css'; - - switch(this.stylesLanguage) { - case 'sass': - this.stylesSuffix = '.sass'; - break; - case 'scss': - this.stylesSuffix = '.scss'; - break; - case 'less': - this.stylesSuffix = '.less'; - break; - case 'stylus': - this.stylesSuffix = '.styl'; - break; - } - - this.sourceRoot(path.join(__dirname, sourceRoot)); -}; - -util.inherits(Generator, yeoman.generators.NamedBase); - -Generator.prototype.appTemplate = function (src, dest) { - yeoman.generators.Base.prototype.template.apply(this, [ - path.join('javascript', src + this.scriptSuffix), - path.join(this.options.appPath, dest) + this.scriptSuffix - ]); -}; - -Generator.prototype.reactComponentTemplate = function (src, dest) { - yeoman.generators.Base.prototype.template.apply(this, [ - path.join('javascript', src + this.reactSuffix), - path.join(this.options.appPath, dest) + this.reactComponentSuffix - ]); -}; - -Generator.prototype.testTemplate = function (src, dest) { - yeoman.generators.Base.prototype.template.apply(this, [ - src + this.scriptSuffix, - path.join(this.options.testPath, dest) + this.scriptSuffix - ]); -}; - -Generator.prototype.stylesTemplate = function (src, dest) { - yeoman.generators.Base.prototype.template.apply(this, [ - src + this.stylesSuffix, - path.join(this.options.stylesPath, dest) + this.stylesSuffix - ]); -}; - -Generator.prototype.htmlTemplate = function (src, dest) { - yeoman.generators.Base.prototype.template.apply(this, [ - src, - path.join(this.options.appPath, dest.toLowerCase()) - ]); -}; - -Generator.prototype.generateSourceAndTest = function (appTemplate, testTemplate, targetDirectory) { - this.appTemplate(appTemplate, path.join(targetDirectory, this._.capitalizeFile(this.name))); - this.testTemplate(testTemplate, path.join(targetDirectory, this._.capitalizeFile(this.name))); -}; - -Generator.prototype.generateComponentTestAndStyle = function (componentTemplate, testTemplate, stylesTemplate, targetDirectory) { - this.reactComponentTemplate(componentTemplate, path.join(targetDirectory, this._.capitalizeFile(this.name))); - this.testTemplate(testTemplate, path.join(targetDirectory, this._.capitalizeFile(this.name))); - this.stylesTemplate(stylesTemplate, path.join(this._.capitalizeFile(this.name))); -}; diff --git a/store/index.js b/store/index.js deleted file mode 100644 index 98db828..0000000 --- a/store/index.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict'; -var util = require('util'); -var ScriptBase = require('../script-base.js'); - -var StoreGenerator = module.exports = function StoreGenerator(args, options, config) { - if (!args[0]) console.log('\n Please specify a name for this store \n'); - else { - args[0] += 'Store'; - ScriptBase.apply(this, arguments) - } -}; - -util.inherits(StoreGenerator, ScriptBase); - -StoreGenerator .prototype.createStoreFile = function createStoreFile() { - this.option('es6'); - - this.es6 = this.options.es6; - - var storeTemplate; - switch (this.architecture){ - case 'flux': - storeTemplate = 'FluxStore'; - this.dispatcherName = this._.capitalizeFile(this.config.get('app-name')) + 'AppDispatcher'; - break; - case 'reflux': - storeTemplate = 'RefluxStore'; - break; - case 'alt': - storeTemplate = 'AltStore'; - break; - } - - console.log('Creating ' + this.architecture + ' store'); - - - this.generateSourceAndTest( - storeTemplate, - 'spec/Store', - 'stores' - ); -}; diff --git a/templates/common/Gruntfile.js b/templates/common/Gruntfile.js deleted file mode 100644 index 0f61f8a..0000000 --- a/templates/common/Gruntfile.js +++ /dev/null @@ -1,125 +0,0 @@ -'use strict'; - -var mountFolder = function (connect, dir) { - return connect.static(require('path').resolve(dir)); -}; - -var webpackDistConfig = require('./webpack.dist.config.js'), - webpackDevConfig = require('./webpack.config.js'); - -module.exports = function (grunt) { - // Let *load-grunt-tasks* require everything - require('load-grunt-tasks')(grunt); - - // Read configuration from package.json - var pkgConfig = grunt.file.readJSON('package.json'); - - grunt.initConfig({ - pkg: pkgConfig, - - webpack: { - options: webpackDistConfig, - dist: { - cache: false - } - }, - - 'webpack-dev-server': { - options: { - hot: true, - port: 8000, - webpack: webpackDevConfig, - publicPath: '/assets/', - contentBase: './<%= pkg.src %>/' - }, - - start: { - keepAlive: true - } - }, - - connect: { - options: { - port: 8000 - }, - - dist: { - options: { - keepalive: true, - middleware: function (connect) { - return [ - mountFolder(connect, pkgConfig.dist) - ]; - } - } - } - }, - - open: { - options: { - delay: 500 - }, - dev: { - path: 'http://localhost:<%= connect.options.port %>/webpack-dev-server/' - }, - dist: { - path: 'http://localhost:<%= connect.options.port %>/' - } - }, - - karma: { - unit: { - configFile: 'karma.conf.js' - } - }, - - copy: { - dist: { - files: [ - // includes files within path - { - flatten: true, - expand: true, - src: ['<%= pkg.src %>/*'], - dest: '<%= pkg.dist %>/', - filter: 'isFile' - }, - { - flatten: true, - expand: true, - src: ['<%= pkg.src %>/images/*'], - dest: '<%= pkg.dist %>/images/' - } - ] - } - }, - - clean: { - dist: { - files: [{ - dot: true, - src: [ - '<%= pkg.dist %>' - ] - }] - } - } - }); - - grunt.registerTask('serve', function (target) { - if (target === 'dist') { - return grunt.task.run(['build', 'open:dist', 'connect:dist']); - } - - grunt.task.run([ - 'open:dev', - 'webpack-dev-server' - ]); - }); - - grunt.registerTask('test', ['karma']); - - grunt.registerTask('build', ['clean', 'copy', 'webpack']); - - grunt.registerTask('default', []); -}; diff --git a/templates/common/_package.json b/templates/common/_package.json deleted file mode 100644 index 573c48d..0000000 --- a/templates/common/_package.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "name": "<%= _.slugify(appname) %>", - "version": "0.0.0", - "description": "", - "repository": "", - "private": true, - "src": "src", - "test": "test", - "dist": "dist", - "mainInput": "<% if (reactRouter) { %>main<% } else { %><%= scriptAppName %><% } %>", - "mainOutput": "main", - "dependencies": {<% if (reactRouter) { %> - "react-router": "0.13.x",<% } if (architecture === 'flux') { %> - "flux": "^2.0.1", - "events": "^1.0.2", - "object-assign": "^2.0.0", <% } if (architecture === 'reflux') {%> - "reflux": "^0.2.7", <% } if (architecture === 'alt') { %> - "alt": "^0.16.5", <% } %> - "react": "0.13.x", - "normalize.css": "~3.0.3" - }, - "devDependencies": { - "babel": "^5.0.0", - "babel-loader": "^5.0.0", - "grunt": "~0.4.5", - "eslint": "^0.21.2", - "eslint-loader": "^0.11.2", - "eslint-plugin-react": "^2.4.0", - "load-grunt-tasks": "~0.6.0", - "grunt-contrib-connect": "~0.8.0", - "grunt-webpack": "~1.0.8", - "jasmine-core": "^2.3.4", - "karma": "~0.13.9", - "karma-jasmine": "^0.3.5", - "karma-phantomjs-launcher": "~0.2.1", - "karma-script-launcher": "~0.1.0", - "karma-webpack": "^1.7.0", - "style-loader": "~0.8.0", - "url-loader": "~0.5.5", - "css-loader": "~0.9.0", - "grunt-karma": "~0.12.1", - "grunt-open": "~0.2.3", - "grunt-contrib-copy": "~0.5.0", - "grunt-contrib-clean": "~0.6.0",<% if (stylesLanguage.match(/s[ac]ss/)) { %> - "sass-loader": "^1.0.1",<% } %><% if (stylesLanguage === 'less') { %> - "less-loader": "^2.0.0",<% } %><% if (stylesLanguage === 'stylus') { %> - "stylus-loader": "^0.5.0",<% } %> - "react-hot-loader": "^1.0.7", - "webpack": "~1.10.0", - "webpack-dev-server": "~1.10.0" - } -} diff --git a/templates/common/_webpack.config.js b/templates/common/_webpack.config.js deleted file mode 100644 index 84d8810..0000000 --- a/templates/common/_webpack.config.js +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Webpack development server configuration - * - * This file is set up for serving the webpack-dev-server, which will watch for changes and recompile as required if - * the subfolder /webpack-dev-server/ is visited. Visiting the root will not automatically reload. - */ -'use strict'; -var webpack = require('webpack'); - -module.exports = { - - output: { - filename: 'main.js', - publicPath: '/assets/' - }, - - cache: true, - debug: true, - devtool: 'sourcemap', - entry: [ - 'webpack/hot/only-dev-server', - './src/components/<% if (reactRouter) { %>main<% } else { %><%= scriptAppName %><% } %>.js' - ], - - stats: { - colors: true, - reasons: true - }, - - resolve: { - extensions: ['', '.js', '.jsx'], - alias: { - 'styles': __dirname + '/src/styles', - 'mixins': __dirname + '/src/mixins', - 'components': __dirname + '/src/components/'<% if(architecture==='flux'||architecture=='reflux') { %>, - 'stores': __dirname + '/src/stores/', - 'actions': __dirname + '/src/actions/'<% } %> - } - }, - module: { - preLoaders: [{ - test: /\.(js|jsx)$/, - exclude: /node_modules/, - loader: 'eslint-loader' - }], - loaders: [{ - test: /\.(js|jsx)$/, - exclude: /node_modules/, - loader: 'react-hot!babel-loader' - },<% if (stylesLanguage === 'sass') { %> { - test: /\.sass/, - loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded&indentedSyntax' - },<% } %><% if (stylesLanguage === 'scss') { %> { - test: /\.scss/, - loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded' - },<% } %><% if (stylesLanguage === 'less') { %> { - test: /\.less/, - loader: 'style-loader!css-loader!less-loader' - },<% } %><% if (stylesLanguage === 'stylus') { %> { - test: /\.styl/, - loader: 'style-loader!css-loader!stylus-loader' - },<% } %> { - test: /\.css$/, - loader: 'style-loader!css-loader' - }, { - test: /\.(png|jpg|woff|woff2)$/, - loader: 'url-loader?limit=8192' - }] - }, - - plugins: [ - new webpack.HotModuleReplacementPlugin() - ] - -}; diff --git a/templates/common/_webpack.dist.config.js b/templates/common/_webpack.dist.config.js deleted file mode 100644 index c502608..0000000 --- a/templates/common/_webpack.dist.config.js +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Webpack distribution configuration - * - * This file is set up for serving the distribution version. It will be compiled to dist/ by default - */ - -'use strict'; - -var webpack = require('webpack'); - -module.exports = { - - output: { - publicPath: '/assets/', - path: 'dist/assets/', - filename: 'main.js' - }, - - debug: false, - devtool: false, - entry: './src/components/<% if (reactRouter) { %>main<% } else { %><%= scriptAppName %><% } %>.js', - - stats: { - colors: true, - reasons: false - }, - - plugins: [ - new webpack.optimize.DedupePlugin(), - new webpack.optimize.UglifyJsPlugin(), - new webpack.optimize.OccurenceOrderPlugin(), - new webpack.optimize.AggressiveMergingPlugin(), - new webpack.NoErrorsPlugin() - ], - - resolve: { - extensions: ['', '.js', '.jsx'], - alias: { - 'styles': __dirname + '/src/styles', - 'mixins': __dirname + '/src/mixins', - 'components': __dirname + '/src/components/'<% if(architecture==='flux'||architecture=='reflux') { %>, - 'stores': __dirname + '/src/stores/', - 'actions': __dirname + '/src/actions/'<% } %> - } - }, - - module: { - preLoaders: [{ - test: /\.(js|jsx)$/, - exclude: /node_modules/, - loader: 'eslint-loader' - }], - loaders: [{ - test: /\.(js|jsx)$/, - exclude: /node_modules/, - loader: 'babel-loader' - }, { - test: /\.css$/, - loader: 'style-loader!css-loader' - },<% if (stylesLanguage === 'sass') { %> { - test: /\.sass/, - loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded&indentedSyntax' - },<% } %><% if (stylesLanguage === 'scss') { %> { - test: /\.scss/, - loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded' - },<% } %><% if (stylesLanguage === 'less') { %> { - test: /\.less/, - loader: 'style-loader!css-loader!less-loader' - },<% } %><% if (stylesLanguage === 'stylus') { %> { - test: /\.styl/, - loader: 'style-loader!css-loader!stylus-loader' - },<% } %> { - test: /\.(png|jpg|woff|woff2)$/, - loader: 'url-loader?limit=8192' - }] - } -}; diff --git a/templates/common/gitignore b/templates/common/gitignore deleted file mode 100644 index ce592cc..0000000 --- a/templates/common/gitignore +++ /dev/null @@ -1,33 +0,0 @@ -### SublimeText ### -*.sublime-workspace - -### OSX ### -.DS_Store -.AppleDouble -.LSOverride -Icon - -# Thumbnails -._* - -# Files that might appear on external disk -.Spotlight-V100 -.Trashes - -### Windows ### -# Windows image file caches -Thumbs.db -ehthumbs.db - -# Folder config file -Desktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# App specific - -node_modules/ -.tmp -dist -/src/main.js diff --git a/templates/common/index.html b/templates/common/index.html deleted file mode 100644 index 7d321af..0000000 --- a/templates/common/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - -
-

If you can see this, something is broken (or JS is not enabled)!!.

-
- - - - - diff --git a/templates/common/karma.conf.js b/templates/common/karma.conf.js deleted file mode 100644 index 5e0f0e4..0000000 --- a/templates/common/karma.conf.js +++ /dev/null @@ -1,93 +0,0 @@ -'use strict'; - -var path = require('path'); - -module.exports = function (config) { - config.set({ - basePath: '', - frameworks: ['jasmine'], - files: [ - 'test/helpers/pack/**/*.js', - 'test/helpers/react/**/*.js', - 'test/spec/components/**/*.js'<% if(architecture === 'flux'||architecture === 'reflux') { %>, - 'test/spec/stores/**/*.js', - 'test/spec/actions/**/*.js'<% } %> - ], - preprocessors: { - 'test/helpers/createComponent.js': ['webpack'], - 'test/spec/components/**/*.js': ['webpack'], - 'test/spec/components/**/*.jsx': ['webpack']<% if(architecture === 'flux'||architecture === 'reflux') { %>, - 'test/spec/stores/**/*.js': ['webpack'], - 'test/spec/actions/**/*.js': ['webpack']<% } %> - }, - webpack: { - cache: true, - module: { - loaders: [{ - test: /\.gif/, - loader: 'url-loader?limit=10000&mimetype=image/gif' - }, { - test: /\.jpg/, - loader: 'url-loader?limit=10000&mimetype=image/jpg' - }, { - test: /\.png/, - loader: 'url-loader?limit=10000&mimetype=image/png' - }, { - test: /\.(js|jsx)$/, - loader: 'babel-loader', - exclude: /node_modules/ - },<% if (stylesLanguage === 'sass') { %> { - test: /\.sass/, - loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded' - },<% } %><% if (stylesLanguage === 'scss') { %> { - test: /\.scss/, - loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded' - },<% } %><% if (stylesLanguage === 'less') { %> { - test: /\.less/, - loader: 'style-loader!css-loader!less-loader' - },<% } %><% if (stylesLanguage === 'stylus') { %> { - test: /\.styl/, - loader: 'style-loader!css-loader!stylus-loader' - },<% } %> { - test: /\.css$/, - loader: 'style-loader!css-loader' - }, { - test: /\.woff/, - loader: 'url-loader?limit=10000&mimetype=application/font-woff' - }, { - test: /\.woff2/, - loader: 'url-loader?limit=10000&mimetype=application/font-woff2' - }] - }, - resolve: { - alias: { - 'styles': path.join(process.cwd(), './src/styles/'), - 'components': path.join(process.cwd(), './src/components/')<% if(architecture === 'flux'||architecture === 'reflux') { %>, - 'stores': '../../../src/stores/', - 'actions': '../../../src/actions/'<% } %>, - 'helpers': path.join(process.cwd(), './test/helpers/') - } - } - }, - webpackMiddleware: { - noInfo: true, - stats: { - colors: true - } - }, - exclude: [], - port: 8080, - logLevel: config.LOG_INFO, - colors: true, - autoWatch: false, - browsers: ['PhantomJS'], - reporters: ['dots'], - captureTimeout: 60000, - singleRun: true, - plugins: [ - require('karma-webpack'), - require('karma-jasmine'), - require('karma-phantomjs-launcher') - ] - }); -}; diff --git a/templates/common/root/.editorconfig b/templates/common/root/.editorconfig deleted file mode 100644 index c308ed0..0000000 --- a/templates/common/root/.editorconfig +++ /dev/null @@ -1,13 +0,0 @@ -# http://editorconfig.org -root = true - -[*] -indent_style = space -indent_size = 4 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.md] -trim_trailing_whitespace = false diff --git a/templates/common/root/.eslintrc b/templates/common/root/.eslintrc deleted file mode 100644 index 94df36a..0000000 --- a/templates/common/root/.eslintrc +++ /dev/null @@ -1,21 +0,0 @@ -{ - "plugins": [ - "react" - ], - "ecmaFeatures": { - "jsx": true, - "modules": true - }, - "env": { - "browser": true, - "amd": true, - "es6": true - }, - "rules": { - "quotes": [ 1, "single" ], - "no-undef": false, - "global-strict": false, - "no-extra-semi": 1, - "no-underscore-dangle": false - } -} diff --git a/templates/common/root/.jshintrc b/templates/common/root/.jshintrc deleted file mode 100644 index 2f22258..0000000 --- a/templates/common/root/.jshintrc +++ /dev/null @@ -1,27 +0,0 @@ -{ - "node": true, - "browser": true, - "esnext": true, - "bitwise": true, - "camelcase": false, - "curly": true, - "eqeqeq": true, - "immed": true, - "indent": 2, - "latedef": true, - "newcap": true, - "noarg": true, - "quotmark": "false", - "regexp": true, - "undef": true, - "unused": false, - "strict": true, - "trailing": true, - "smarttabs": true, - "white": true, - "newcap": false, - "globals": { - "React": true - } -} - diff --git a/templates/common/root/src/favicon.ico b/templates/common/root/src/favicon.ico deleted file mode 100644 index 6527905..0000000 Binary files a/templates/common/root/src/favicon.ico and /dev/null differ diff --git a/templates/common/root/test/.jshintrc b/templates/common/root/test/.jshintrc deleted file mode 100644 index baa5704..0000000 --- a/templates/common/root/test/.jshintrc +++ /dev/null @@ -1,40 +0,0 @@ -{ - "node": true, - "browser": true, - "esnext": true, - "bitwise": true, - "camelcase": false, - "curly": true, - "eqeqeq": true, - "immed": true, - "indent": 2, - "latedef": true, - "newcap": true, - "noarg": true, - "quotmark": "false", - "regexp": true, - "undef": true, - "unused": false, - "strict": true, - "trailing": true, - "smarttabs": true, - "white": true, - "newcap": false, - "globals": { - "after": false, - "afterEach": false, - "react": false, - "before": false, - "beforeEach": false, - "browser": false, - "describe": false, - "expect": false, - "inject": false, - "it": false, - "spyOn": false, - "jasmine": false, - "spyOnConstructor": false, - "React": true - } -} - diff --git a/templates/common/root/test/helpers/createComponent.js b/templates/common/root/test/helpers/createComponent.js deleted file mode 100644 index 8a0c1da..0000000 --- a/templates/common/root/test/helpers/createComponent.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Function to get the shallow output for a given component - * As we are using phantom.js, we also need to include the fn.proto.bind shim! - * - * @see http://simonsmith.io/unit-testing-react-components-without-a-dom/ - * @author somonsmith - */ - -// Add missing methods to phantom.js -import './pack/phantomjs-shims'; - -import React from 'react/addons'; -const TestUtils = React.addons.TestUtils; - -/** - * Get the shallow rendered component - * - * @param {Object} component The component to return the output for - * @param {Object} props [optional] The components properties - * @param {Mixed} ...children [optional] List of children - * @return {Object} Shallow rendered output - */ -export default function createComponent(component, props = {}, ...children) { - const shallowRenderer = TestUtils.createRenderer(); - shallowRenderer.render(React.createElement(component, props, children.length > 1 ? children : children[0])); - return shallowRenderer.getRenderOutput(); -} diff --git a/templates/common/root/test/helpers/pack/phantomjs-shims.js b/templates/common/root/test/helpers/pack/phantomjs-shims.js deleted file mode 100644 index 7da307d..0000000 --- a/templates/common/root/test/helpers/pack/phantomjs-shims.js +++ /dev/null @@ -1,34 +0,0 @@ -(function() { - -var Ap = Array.prototype; -var slice = Ap.slice; -var Fp = Function.prototype; - -if (!Fp.bind) { - // PhantomJS doesn't support Function.prototype.bind natively, so - // polyfill it whenever this module is required. - Fp.bind = function(context) { - var func = this; - var args = slice.call(arguments, 1); - - function bound() { - var invokedAsConstructor = func.prototype && (this instanceof func); - return func.apply( - // Ignore the context parameter when invoking the bound function - // as a constructor. Note that this includes not only constructor - // invocations using the new keyword but also calls to base class - // constructors such as BaseClass.call(this, ...) or super(...). - !invokedAsConstructor && context || this, - args.concat(slice.call(arguments)) - ); - } - - // The bound function must share the .prototype of the unbound - // function so that any object created by one constructor will count - // as an instance of both constructors. - bound.prototype = func.prototype; - - return bound; - }; -} -})(); diff --git a/templates/common/root/test/helpers/react/addons.js b/templates/common/root/test/helpers/react/addons.js deleted file mode 100755 index 0bf44fe..0000000 --- a/templates/common/root/test/helpers/react/addons.js +++ /dev/null @@ -1,16336 +0,0 @@ -/** - * React (with addons) v0.9.0-alpha - */ -!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.React=e():"undefined"!=typeof global?global.React=e():"undefined"!=typeof self&&(self.React=e())}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o -1; -} - -var CSSCore = { - - /** - * Adds the class passed in to the element if it doesn't already have it. - * - * @param {DOMElement} element the element to set the class on - * @param {string} className the CSS className - * @return {DOMElement} the element passed in - */ - addClass: function(element, className) { - ("production" !== "development" ? invariant( - !/\s/.test(className), - 'CSSCore.addClass takes only a single class name. "%s" contains ' + - 'multiple classes.', className - ) : invariant(!/\s/.test(className))); - - if (className) { - if (element.classList) { - element.classList.add(className); - } else if (!hasClass(element, className)) { - element.className = element.className + ' ' + className; - } - } - return element; - }, - - /** - * Removes the class passed in from the element - * - * @param {DOMElement} element the element to set the class on - * @param {string} className the CSS className - * @return {DOMElement} the element passed in - */ - removeClass: function(element, className) { - ("production" !== "development" ? invariant( - !/\s/.test(className), - 'CSSCore.removeClass takes only a single class name. "%s" contains ' + - 'multiple classes.', className - ) : invariant(!/\s/.test(className))); - - if (className) { - if (element.classList) { - element.classList.remove(className); - } else if (hasClass(element, className)) { - element.className = element.className - .replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)', 'g'), '$1') - .replace(/\s+/g, ' ') // multiple spaces to one - .replace(/^\s*|\s*$/g, ''); // trim the ends - } - } - return element; - }, - - /** - * Helper to add or remove a class from an element based on a condition. - * - * @param {DOMElement} element the element to set the class on - * @param {string} className the CSS className - * @param {*} bool condition to whether to add or remove the class - * @return {DOMElement} the element passed in - */ - conditionClass: function(element, className, bool) { - return (bool ? CSSCore.addClass : CSSCore.removeClass)(element, className); - } -}; - -module.exports = CSSCore; - -},{"./invariant":113}],3:[function(require,module,exports){ -/** - * Copyright 2013 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @providesModule CSSProperty - */ - -"use strict"; - -/** - * CSS properties which accept numbers but are not in units of "px". - */ -var isUnitlessNumber = { - fillOpacity: true, - fontWeight: true, - lineHeight: true, - opacity: true, - orphans: true, - zIndex: true, - zoom: true -}; - -/** - * Most style properties can be unset by doing .style[prop] = '' but IE8 - * doesn't like doing that with shorthand properties so for the properties that - * IE8 breaks on, which are listed here, we instead unset each of the - * individual properties. See http://bugs.jquery.com/ticket/12385. - * The 4-value 'clock' properties like margin, padding, border-width seem to - * behave without any problems. Curiously, list-style works too without any - * special prodding. - */ -var shorthandPropertyExpansions = { - background: { - backgroundImage: true, - backgroundPosition: true, - backgroundRepeat: true, - backgroundColor: true - }, - border: { - borderWidth: true, - borderStyle: true, - borderColor: true - }, - borderBottom: { - borderBottomWidth: true, - borderBottomStyle: true, - borderBottomColor: true - }, - borderLeft: { - borderLeftWidth: true, - borderLeftStyle: true, - borderLeftColor: true - }, - borderRight: { - borderRightWidth: true, - borderRightStyle: true, - borderRightColor: true - }, - borderTop: { - borderTopWidth: true, - borderTopStyle: true, - borderTopColor: true - }, - font: { - fontStyle: true, - fontVariant: true, - fontWeight: true, - fontSize: true, - lineHeight: true, - fontFamily: true - } -}; - -var CSSProperty = { - isUnitlessNumber: isUnitlessNumber, - shorthandPropertyExpansions: shorthandPropertyExpansions -}; - -module.exports = CSSProperty; - -},{}],4:[function(require,module,exports){ -/** - * Copyright 2013 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @providesModule CSSPropertyOperations - * @typechecks static-only - */ - -"use strict"; - -var CSSProperty = require("./CSSProperty"); - -var dangerousStyleValue = require("./dangerousStyleValue"); -var escapeTextForBrowser = require("./escapeTextForBrowser"); -var hyphenate = require("./hyphenate"); -var memoizeStringOnly = require("./memoizeStringOnly"); - -var processStyleName = memoizeStringOnly(function(styleName) { - return escapeTextForBrowser(hyphenate(styleName)); -}); - -/** - * Operations for dealing with CSS properties. - */ -var CSSPropertyOperations = { - - /** - * Serializes a mapping of style properties for use as inline styles: - * - * > createMarkupForStyles({width: '200px', height: 0}) - * "width:200px;height:0;" - * - * Undefined values are ignored so that declarative programming is easier. - * - * @param {object} styles - * @return {?string} - */ - createMarkupForStyles: function(styles) { - var serialized = ''; - for (var styleName in styles) { - if (!styles.hasOwnProperty(styleName)) { - continue; - } - var styleValue = styles[styleName]; - if (styleValue != null) { - serialized += processStyleName(styleName) + ':'; - serialized += dangerousStyleValue(styleName, styleValue) + ';'; - } - } - return serialized || null; - }, - - /** - * Sets the value for multiple styles on a node. If a value is specified as - * '' (empty string), the corresponding style property will be unset. - * - * @param {DOMElement} node - * @param {object} styles - */ - setValueForStyles: function(node, styles) { - var style = node.style; - for (var styleName in styles) { - if (!styles.hasOwnProperty(styleName)) { - continue; - } - var styleValue = dangerousStyleValue(styleName, styles[styleName]); - if (styleValue) { - style[styleName] = styleValue; - } else { - var expansion = CSSProperty.shorthandPropertyExpansions[styleName]; - if (expansion) { - // Shorthand property that IE8 won't like unsetting, so unset each - // component to placate it - for (var individualStyleName in expansion) { - style[individualStyleName] = ''; - } - } else { - style[styleName] = ''; - } - } - } - } - -}; - -module.exports = CSSPropertyOperations; - -},{"./CSSProperty":3,"./dangerousStyleValue":97,"./escapeTextForBrowser":99,"./hyphenate":112,"./memoizeStringOnly":121}],5:[function(require,module,exports){ -/** - * Copyright 2013 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @providesModule CallbackRegistry - * @typechecks static-only - */ - -"use strict"; - -var listenerBank = {}; - -/** - * Stores "listeners" by `registrationName`/`id`. There should be at most one - * "listener" per `registrationName`/`id` in the `listenerBank`. - * - * Access listeners via `listenerBank[registrationName][id]`. - * - * @class CallbackRegistry - * @internal - */ -var CallbackRegistry = { - - /** - * Stores `listener` at `listenerBank[registrationName][id]`. Is idempotent. - * - * @param {string} id ID of the DOM element. - * @param {string} registrationName Name of listener (e.g. `onClick`). - * @param {?function} listener The callback to store. - */ - putListener: function(id, registrationName, listener) { - var bankForRegistrationName = - listenerBank[registrationName] || (listenerBank[registrationName] = {}); - bankForRegistrationName[id] = listener; - }, - - /** - * @param {string} id ID of the DOM element. - * @param {string} registrationName Name of listener (e.g. `onClick`). - * @return {?function} The stored callback. - */ - getListener: function(id, registrationName) { - var bankForRegistrationName = listenerBank[registrationName]; - return bankForRegistrationName && bankForRegistrationName[id]; - }, - - /** - * Deletes a listener from the registration bank. - * - * @param {string} id ID of the DOM element. - * @param {string} registrationName Name of listener (e.g. `onClick`). - */ - deleteListener: function(id, registrationName) { - var bankForRegistrationName = listenerBank[registrationName]; - if (bankForRegistrationName) { - delete bankForRegistrationName[id]; - } - }, - - /** - * Deletes all listeners for the DOM element with the supplied ID. - * - * @param {string} id ID of the DOM element. - */ - deleteAllListeners: function(id) { - for (var registrationName in listenerBank) { - delete listenerBank[registrationName][id]; - } - }, - - /** - * This is needed for tests only. Do not use! - */ - __purge: function() { - listenerBank = {}; - } - -}; - -module.exports = CallbackRegistry; - -},{}],6:[function(require,module,exports){ -/** - * Copyright 2013 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @providesModule ChangeEventPlugin - */ - -"use strict"; - -var EventConstants = require("./EventConstants"); -var EventPluginHub = require("./EventPluginHub"); -var EventPropagators = require("./EventPropagators"); -var ExecutionEnvironment = require("./ExecutionEnvironment"); -var SyntheticEvent = require("./SyntheticEvent"); - -var isEventSupported = require("./isEventSupported"); -var isTextInputElement = require("./isTextInputElement"); -var keyOf = require("./keyOf"); - -var topLevelTypes = EventConstants.topLevelTypes; - -var eventTypes = { - change: { - phasedRegistrationNames: { - bubbled: keyOf({onChange: null}), - captured: keyOf({onChangeCapture: null}) - } - } -}; - -/** - * For IE shims - */ -var activeElement = null; -var activeElementID = null; -var activeElementValue = null; -var activeElementValueProp = null; - -/** - * SECTION: handle `change` event - */ -function shouldUseChangeEvent(elem) { - return ( - elem.nodeName === 'SELECT' || - (elem.nodeName === 'INPUT' && elem.type === 'file') - ); -} - -var doesChangeEventBubble = false; -if (ExecutionEnvironment.canUseDOM) { - // See `handleChange` comment below - doesChangeEventBubble = isEventSupported('change') && ( - !('documentMode' in document) || document.documentMode > 8 - ); -} - -function manualDispatchChangeEvent(nativeEvent) { - var event = SyntheticEvent.getPooled( - eventTypes.change, - activeElementID, - nativeEvent - ); - EventPropagators.accumulateTwoPhaseDispatches(event); - - // If change bubbled, we'd just bind to it like all the other events - // and have it go through ReactEventTopLevelCallback. Since it doesn't, we - // manually listen for the change event and so we have to enqueue and - // process the abstract event manually. - EventPluginHub.enqueueEvents(event); - EventPluginHub.processEventQueue(); -} - -function startWatchingForChangeEventIE8(target, targetID) { - activeElement = target; - activeElementID = targetID; - activeElement.attachEvent('onchange', manualDispatchChangeEvent); -} - -function stopWatchingForChangeEventIE8() { - if (!activeElement) { - return; - } - activeElement.detachEvent('onchange', manualDispatchChangeEvent); - activeElement = null; - activeElementID = null; -} - -function getTargetIDForChangeEvent( - topLevelType, - topLevelTarget, - topLevelTargetID) { - if (topLevelType === topLevelTypes.topChange) { - return topLevelTargetID; - } -} -function handleEventsForChangeEventIE8( - topLevelType, - topLevelTarget, - topLevelTargetID) { - if (topLevelType === topLevelTypes.topFocus) { - // stopWatching() should be a noop here but we call it just in case we - // missed a blur event somehow. - stopWatchingForChangeEventIE8(); - startWatchingForChangeEventIE8(topLevelTarget, topLevelTargetID); - } else if (topLevelType === topLevelTypes.topBlur) { - stopWatchingForChangeEventIE8(); - } -} - - -/** - * SECTION: handle `input` event - */ -var isInputEventSupported = false; -if (ExecutionEnvironment.canUseDOM) { - // IE9 claims to support the input event but fails to trigger it when - // deleting text, so we ignore its input events - isInputEventSupported = isEventSupported('input') && ( - !('documentMode' in document) || document.documentMode > 9 - ); -} - -/** - * (For old IE.) Replacement getter/setter for the `value` property that gets - * set on the active element. - */ -var newValueProp = { - get: function() { - return activeElementValueProp.get.call(this); - }, - set: function(val) { - // Cast to a string so we can do equality checks. - activeElementValue = '' + val; - activeElementValueProp.set.call(this, val); - } -}; - -/** - * (For old IE.) Starts tracking propertychange events on the passed-in element - * and override the value property so that we can distinguish user events from - * value changes in JS. - */ -function startWatchingForValueChange(target, targetID) { - activeElement = target; - activeElementID = targetID; - activeElementValue = target.value; - activeElementValueProp = Object.getOwnPropertyDescriptor( - target.constructor.prototype, - 'value' - ); - - Object.defineProperty(activeElement, 'value', newValueProp); - activeElement.attachEvent('onpropertychange', handlePropertyChange); -} - -/** - * (For old IE.) Removes the event listeners from the currently-tracked element, - * if any exists. - */ -function stopWatchingForValueChange() { - if (!activeElement) { - return; - } - - // delete restores the original property definition - delete activeElement.value; - activeElement.detachEvent('onpropertychange', handlePropertyChange); - - activeElement = null; - activeElementID = null; - activeElementValue = null; - activeElementValueProp = null; -} - -/** - * (For old IE.) Handles a propertychange event, sending a `change` event if - * the value of the active element has changed. - */ -function handlePropertyChange(nativeEvent) { - if (nativeEvent.propertyName !== 'value') { - return; - } - var value = nativeEvent.srcElement.value; - if (value === activeElementValue) { - return; - } - activeElementValue = value; - - manualDispatchChangeEvent(nativeEvent); -} - -/** - * If a `change` event should be fired, returns the target's ID. - */ -function getTargetIDForInputEvent( - topLevelType, - topLevelTarget, - topLevelTargetID) { - if (topLevelType === topLevelTypes.topInput) { - // In modern browsers (i.e., not IE8 or IE9), the input event is exactly - // what we want so fall through here and trigger an abstract event - return topLevelTargetID; - } -} - -// For IE8 and IE9. -function handleEventsForInputEventIE( - topLevelType, - topLevelTarget, - topLevelTargetID) { - if (topLevelType === topLevelTypes.topFocus) { - // In IE8, we can capture almost all .value changes by adding a - // propertychange handler and looking for events with propertyName - // equal to 'value' - // In IE9, propertychange fires for most input events but is buggy and - // doesn't fire when text is deleted, but conveniently, selectionchange - // appears to fire in all of the remaining cases so we catch those and - // forward the event if the value has changed - // In either case, we don't want to call the event handler if the value - // is changed from JS so we redefine a setter for `.value` that updates - // our activeElementValue variable, allowing us to ignore those changes - // - // stopWatching() should be a noop here but we call it just in case we - // missed a blur event somehow. - stopWatchingForValueChange(); - startWatchingForValueChange(topLevelTarget, topLevelTargetID); - } else if (topLevelType === topLevelTypes.topBlur) { - stopWatchingForValueChange(); - } -} - -// For IE8 and IE9. -function getTargetIDForInputEventIE( - topLevelType, - topLevelTarget, - topLevelTargetID) { - if (topLevelType === topLevelTypes.topSelectionChange || - topLevelType === topLevelTypes.topKeyUp || - topLevelType === topLevelTypes.topKeyDown) { - // On the selectionchange event, the target is just document which isn't - // helpful for us so just check activeElement instead. - // - // 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire - // propertychange on the first input event after setting `value` from a - // script and fires only keydown, keypress, keyup. Catching keyup usually - // gets it and catching keydown lets us fire an event for the first - // keystroke if user does a key repeat (it'll be a little delayed: right - // before the second keystroke). Other input methods (e.g., paste) seem to - // fire selectionchange normally. - if (activeElement && activeElement.value !== activeElementValue) { - activeElementValue = activeElement.value; - return activeElementID; - } - } -} - - -/** - * SECTION: handle `click` event - */ -function shouldUseClickEvent(elem) { - // Use the `click` event to detect changes to checkbox and radio inputs. - // This approach works across all browsers, whereas `change` does not fire - // until `blur` in IE8. - return ( - elem.nodeName === 'INPUT' && - (elem.type === 'checkbox' || elem.type === 'radio') - ); -} - -function getTargetIDForClickEvent( - topLevelType, - topLevelTarget, - topLevelTargetID) { - if (topLevelType === topLevelTypes.topClick) { - return topLevelTargetID; - } -} - -/** - * This plugin creates an `onChange` event that normalizes change events - * across form elements. This event fires at a time when it's possible to - * change the element's value without seeing a flicker. - * - * Supported elements are: - * - input (see `isTextInputElement`) - * - textarea - * - select - */ -var ChangeEventPlugin = { - - eventTypes: eventTypes, - - /** - * @param {string} topLevelType Record from `EventConstants`. - * @param {DOMEventTarget} topLevelTarget The listening component root node. - * @param {string} topLevelTargetID ID of `topLevelTarget`. - * @param {object} nativeEvent Native browser event. - * @return {*} An accumulation of synthetic events. - * @see {EventPluginHub.extractEvents} - */ - extractEvents: function( - topLevelType, - topLevelTarget, - topLevelTargetID, - nativeEvent) { - - var getTargetIDFunc, handleEventFunc; - if (shouldUseChangeEvent(topLevelTarget)) { - if (doesChangeEventBubble) { - getTargetIDFunc = getTargetIDForChangeEvent; - } else { - handleEventFunc = handleEventsForChangeEventIE8; - } - } else if (isTextInputElement(topLevelTarget)) { - if (isInputEventSupported) { - getTargetIDFunc = getTargetIDForInputEvent; - } else { - getTargetIDFunc = getTargetIDForInputEventIE; - handleEventFunc = handleEventsForInputEventIE; - } - } else if (shouldUseClickEvent(topLevelTarget)) { - getTargetIDFunc = getTargetIDForClickEvent; - } - - if (getTargetIDFunc) { - var targetID = getTargetIDFunc( - topLevelType, - topLevelTarget, - topLevelTargetID - ); - if (targetID) { - var event = SyntheticEvent.getPooled( - eventTypes.change, - targetID, - nativeEvent - ); - EventPropagators.accumulateTwoPhaseDispatches(event); - return event; - } - } - - if (handleEventFunc) { - handleEventFunc( - topLevelType, - topLevelTarget, - topLevelTargetID - ); - } - } - -}; - -module.exports = ChangeEventPlugin; - -},{"./EventConstants":15,"./EventPluginHub":17,"./EventPropagators":20,"./ExecutionEnvironment":21,"./SyntheticEvent":80,"./isEventSupported":114,"./isTextInputElement":116,"./keyOf":120}],7:[function(require,module,exports){ -/** - * Copyright 2013 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @providesModule CompositionEventPlugin - * @typechecks static-only - */ - -"use strict"; - -var EventConstants = require("./EventConstants"); -var EventPropagators = require("./EventPropagators"); -var ExecutionEnvironment = require("./ExecutionEnvironment"); -var ReactInputSelection = require("./ReactInputSelection"); -var SyntheticCompositionEvent = require("./SyntheticCompositionEvent"); - -var getTextContentAccessor = require("./getTextContentAccessor"); -var keyOf = require("./keyOf"); - -var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space -var START_KEYCODE = 229; - -var useCompositionEvent = ExecutionEnvironment.canUseDOM && - 'CompositionEvent' in window; -var topLevelTypes = EventConstants.topLevelTypes; -var currentComposition = null; - -// Events and their corresponding property names. -var eventTypes = { - compositionEnd: { - phasedRegistrationNames: { - bubbled: keyOf({onCompositionEnd: null}), - captured: keyOf({onCompositionEndCapture: null}) - } - }, - compositionStart: { - phasedRegistrationNames: { - bubbled: keyOf({onCompositionStart: null}), - captured: keyOf({onCompositionStartCapture: null}) - } - }, - compositionUpdate: { - phasedRegistrationNames: { - bubbled: keyOf({onCompositionUpdate: null}), - captured: keyOf({onCompositionUpdateCapture: null}) - } - } -}; - -/** - * Translate native top level events into event types. - * - * @param {string} topLevelType - * @return {object} - */ -function getCompositionEventType(topLevelType) { - switch (topLevelType) { - case topLevelTypes.topCompositionStart: - return eventTypes.compositionStart; - case topLevelTypes.topCompositionEnd: - return eventTypes.compositionEnd; - case topLevelTypes.topCompositionUpdate: - return eventTypes.compositionUpdate; - } -} - -/** - * Does our fallback best-guess model think this event signifies that - * composition has begun? - * - * @param {string} topLevelType - * @param {object} nativeEvent - * @return {boolean} - */ -function isFallbackStart(topLevelType, nativeEvent) { - return ( - topLevelType === topLevelTypes.topKeyDown && - nativeEvent.keyCode === START_KEYCODE - ); -} - -/** - * Does our fallback mode think that this event is the end of composition? - * - * @param {string} topLevelType - * @param {object} nativeEvent - * @return {boolean} - */ -function isFallbackEnd(topLevelType, nativeEvent) { - switch (topLevelType) { - case topLevelTypes.topKeyUp: - // Command keys insert or clear IME input. - return (END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1); - case topLevelTypes.topKeyDown: - // Expect IME keyCode on each keydown. If we get any other - // code we must have exited earlier. - return (nativeEvent.keyCode !== START_KEYCODE); - case topLevelTypes.topKeyPress: - case topLevelTypes.topMouseDown: - case topLevelTypes.topBlur: - // Events are not possible without cancelling IME. - return true; - default: - return false; - } -} - -/** - * Helper class stores information about selection and document state - * so we can figure out what changed at a later date. - * - * @param {DOMEventTarget} root - */ -function FallbackCompositionState(root) { - this.root = root; - this.startSelection = ReactInputSelection.getSelection(root); - this.startValue = this.getText(); -} - -/** - * Get current text of input. - * - * @return {string} - */ -FallbackCompositionState.prototype.getText = function() { - return this.root.value || this.root[getTextContentAccessor()]; -}; - -/** - * Text that has changed since the start of composition. - * - * @return {string} - */ -FallbackCompositionState.prototype.getData = function() { - var endValue = this.getText(); - var prefixLength = this.startSelection.start; - var suffixLength = this.startValue.length - this.startSelection.end; - - return endValue.substr( - prefixLength, - endValue.length - suffixLength - prefixLength - ); -}; - -/** - * This plugin creates `onCompositionStart`, `onCompositionUpdate` and - * `onCompositionEnd` events on inputs, textareas and contentEditable - * nodes. - */ -var CompositionEventPlugin = { - - eventTypes: eventTypes, - - /** - * @param {string} topLevelType Record from `EventConstants`. - * @param {DOMEventTarget} topLevelTarget The listening component root node. - * @param {string} topLevelTargetID ID of `topLevelTarget`. - * @param {object} nativeEvent Native browser event. - * @return {*} An accumulation of synthetic events. - * @see {EventPluginHub.extractEvents} - */ - extractEvents: function( - topLevelType, - topLevelTarget, - topLevelTargetID, - nativeEvent) { - - var eventType; - var data; - - if (useCompositionEvent) { - eventType = getCompositionEventType(topLevelType); - } else if (!currentComposition) { - if (isFallbackStart(topLevelType, nativeEvent)) { - eventType = eventTypes.start; - currentComposition = new FallbackCompositionState(topLevelTarget); - } - } else if (isFallbackEnd(topLevelType, nativeEvent)) { - eventType = eventTypes.compositionEnd; - data = currentComposition.getData(); - currentComposition = null; - } - - if (eventType) { - var event = SyntheticCompositionEvent.getPooled( - eventType, - topLevelTargetID, - nativeEvent - ); - if (data) { - // Inject data generated from fallback path into the synthetic event. - // This matches the property of native CompositionEventInterface. - event.data = data; - } - EventPropagators.accumulateTwoPhaseDispatches(event); - return event; - } - } -}; - -module.exports = CompositionEventPlugin; - -},{"./EventConstants":15,"./EventPropagators":20,"./ExecutionEnvironment":21,"./ReactInputSelection":51,"./SyntheticCompositionEvent":79,"./getTextContentAccessor":110,"./keyOf":120}],8:[function(require,module,exports){ -/** - * Copyright 2013 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @providesModule DOMChildrenOperations - * @typechecks static-only - */ - -"use strict"; - -var Danger = require("./Danger"); -var ReactMultiChildUpdateTypes = require("./ReactMultiChildUpdateTypes"); - -var getTextContentAccessor = require("./getTextContentAccessor"); - -/** - * The DOM property to use when setting text content. - * - * @type {string} - * @private - */ -var textContentAccessor = getTextContentAccessor() || 'NA'; - -/** - * Inserts `childNode` as a child of `parentNode` at the `index`. - * - * @param {DOMElement} parentNode Parent node in which to insert. - * @param {DOMElement} childNode Child node to insert. - * @param {number} index Index at which to insert the child. - * @internal - */ -function insertChildAt(parentNode, childNode, index) { - var childNodes = parentNode.childNodes; - if (childNodes[index] === childNode) { - return; - } - // If `childNode` is already a child of `parentNode`, remove it so that - // computing `childNodes[index]` takes into account the removal. - if (childNode.parentNode === parentNode) { - parentNode.removeChild(childNode); - } - if (index >= childNodes.length) { - parentNode.appendChild(childNode); - } else { - parentNode.insertBefore(childNode, childNodes[index]); - } -} - -/** - * Operations for updating with DOM children. - */ -var DOMChildrenOperations = { - - dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup, - - /** - * Updates a component's children by processing a series of updates. The - * update configurations are each expected to have a `parentNode` property. - * - * @param {array} updates List of update configurations. - * @param {array} markupList List of markup strings. - * @internal - */ - processUpdates: function(updates, markupList) { - var update; - // Mapping from parent IDs to initial child orderings. - var initialChildren = null; - // List of children that will be moved or removed. - var updatedChildren = null; - - for (var i = 0; update = updates[i]; i++) { - if (update.type === ReactMultiChildUpdateTypes.MOVE_EXISTING || - update.type === ReactMultiChildUpdateTypes.REMOVE_NODE) { - var updatedIndex = update.fromIndex; - var updatedChild = update.parentNode.childNodes[updatedIndex]; - var parentID = update.parentID; - - initialChildren = initialChildren || {}; - initialChildren[parentID] = initialChildren[parentID] || []; - initialChildren[parentID][updatedIndex] = updatedChild; - - updatedChildren = updatedChildren || []; - updatedChildren.push(updatedChild); - } - } - - var renderedMarkup = Danger.dangerouslyRenderMarkup(markupList); - - // Remove updated children first so that `toIndex` is consistent. - if (updatedChildren) { - for (var j = 0; j < updatedChildren.length; j++) { - updatedChildren[j].parentNode.removeChild(updatedChildren[j]); - } - } - - for (var k = 0; update = updates[k]; k++) { - switch (update.type) { - case ReactMultiChildUpdateTypes.INSERT_MARKUP: - insertChildAt( - update.parentNode, - renderedMarkup[update.markupIndex], - update.toIndex - ); - break; - case ReactMultiChildUpdateTypes.MOVE_EXISTING: - insertChildAt( - update.parentNode, - initialChildren[update.parentID][update.fromIndex], - update.toIndex - ); - break; - case ReactMultiChildUpdateTypes.TEXT_CONTENT: - update.parentNode[textContentAccessor] = update.textContent; - break; - case ReactMultiChildUpdateTypes.REMOVE_NODE: - // Already removed by the for-loop above. - break; - } - } - } - -}; - -module.exports = DOMChildrenOperations; - -},{"./Danger":11,"./ReactMultiChildUpdateTypes":58,"./getTextContentAccessor":110}],9:[function(require,module,exports){ -/** - * Copyright 2013 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @providesModule DOMProperty - * @typechecks static-only - */ - -/*jslint bitwise: true */ - -"use strict"; - -var invariant = require("./invariant"); - -var DOMPropertyInjection = { - /** - * Mapping from normalized, camelcased property names to a configuration that - * specifies how the associated DOM property should be accessed or rendered. - */ - MUST_USE_ATTRIBUTE: 0x1, - MUST_USE_PROPERTY: 0x2, - HAS_SIDE_EFFECTS: 0x4, - HAS_BOOLEAN_VALUE: 0x8, - HAS_POSITIVE_NUMERIC_VALUE: 0x10, - - /** - * Inject some specialized knowledge about the DOM. This takes a config object - * with the following properties: - * - * isCustomAttribute: function that given an attribute name will return true - * if it can be inserted into the DOM verbatim. Useful for data-* or aria-* - * attributes where it's impossible to enumerate all of the possible - * attribute names, - * - * Properties: object mapping DOM property name to one of the - * DOMPropertyInjection constants or null. If your attribute isn't in here, - * it won't get written to the DOM. - * - * DOMAttributeNames: object mapping React attribute name to the DOM - * attribute name. Attribute names not specified use the **lowercase** - * normalized name. - * - * DOMPropertyNames: similar to DOMAttributeNames but for DOM properties. - * Property names not specified use the normalized name. - * - * DOMMutationMethods: Properties that require special mutation methods. If - * `value` is undefined, the mutation method should unset the property. - * - * @param {object} domPropertyConfig the config as described above. - */ - injectDOMPropertyConfig: function(domPropertyConfig) { - var Properties = domPropertyConfig.Properties || {}; - var DOMAttributeNames = domPropertyConfig.DOMAttributeNames || {}; - var DOMPropertyNames = domPropertyConfig.DOMPropertyNames || {}; - var DOMMutationMethods = domPropertyConfig.DOMMutationMethods || {}; - - if (domPropertyConfig.isCustomAttribute) { - DOMProperty._isCustomAttributeFunctions.push( - domPropertyConfig.isCustomAttribute - ); - } - - for (var propName in Properties) { - ("production" !== "development" ? invariant( - !DOMProperty.isStandardName[propName], - 'injectDOMPropertyConfig(...): You\'re trying to inject DOM property ' + - '\'%s\' which has already been injected. You may be accidentally ' + - 'injecting the same DOM property config twice, or you may be ' + - 'injecting two configs that have conflicting property names.', - propName - ) : invariant(!DOMProperty.isStandardName[propName])); - - DOMProperty.isStandardName[propName] = true; - - var lowerCased = propName.toLowerCase(); - DOMProperty.getPossibleStandardName[lowerCased] = propName; - - var attributeName = DOMAttributeNames[propName]; - if (attributeName) { - DOMProperty.getPossibleStandardName[attributeName] = propName; - } - - DOMProperty.getAttributeName[propName] = attributeName || lowerCased; - - DOMProperty.getPropertyName[propName] = - DOMPropertyNames[propName] || propName; - - var mutationMethod = DOMMutationMethods[propName]; - if (mutationMethod) { - DOMProperty.getMutationMethod[propName] = mutationMethod; - } - - var propConfig = Properties[propName]; - DOMProperty.mustUseAttribute[propName] = - propConfig & DOMPropertyInjection.MUST_USE_ATTRIBUTE; - DOMProperty.mustUseProperty[propName] = - propConfig & DOMPropertyInjection.MUST_USE_PROPERTY; - DOMProperty.hasSideEffects[propName] = - propConfig & DOMPropertyInjection.HAS_SIDE_EFFECTS; - DOMProperty.hasBooleanValue[propName] = - propConfig & DOMPropertyInjection.HAS_BOOLEAN_VALUE; - DOMProperty.hasPositiveNumericValue[propName] = - propConfig & DOMPropertyInjection.HAS_POSITIVE_NUMERIC_VALUE; - - ("production" !== "development" ? invariant( - !DOMProperty.mustUseAttribute[propName] || - !DOMProperty.mustUseProperty[propName], - 'DOMProperty: Cannot require using both attribute and property: %s', - propName - ) : invariant(!DOMProperty.mustUseAttribute[propName] || - !DOMProperty.mustUseProperty[propName])); - ("production" !== "development" ? invariant( - DOMProperty.mustUseProperty[propName] || - !DOMProperty.hasSideEffects[propName], - 'DOMProperty: Properties that have side effects must use property: %s', - propName - ) : invariant(DOMProperty.mustUseProperty[propName] || - !DOMProperty.hasSideEffects[propName])); - ("production" !== "development" ? invariant( - !DOMProperty.hasBooleanValue[propName] || - !DOMProperty.hasPositiveNumericValue[propName], - 'DOMProperty: Cannot have both boolean and positive numeric value: %s', - propName - ) : invariant(!DOMProperty.hasBooleanValue[propName] || - !DOMProperty.hasPositiveNumericValue[propName])); - } - } -}; -var defaultValueCache = {}; - -/** - * DOMProperty exports lookup objects that can be used like functions: - * - * > DOMProperty.isValid['id'] - * true - * > DOMProperty.isValid['foobar'] - * undefined - * - * Although this may be confusing, it performs better in general. - * - * @see http://jsperf.com/key-exists - * @see http://jsperf.com/key-missing - */ -var DOMProperty = { - - /** - * Checks whether a property name is a standard property. - * @type {Object} - */ - isStandardName: {}, - - /** - * Mapping from lowercase property names to the properly cased version, used - * to warn in the case of missing properties. - * @type {Object} - */ - getPossibleStandardName: {}, - - /** - * Mapping from normalized names to attribute names that differ. Attribute - * names are used when rendering markup or with `*Attribute()`. - * @type {Object} - */ - getAttributeName: {}, - - /** - * Mapping from normalized names to properties on DOM node instances. - * (This includes properties that mutate due to external factors.) - * @type {Object} - */ - getPropertyName: {}, - - /** - * Mapping from normalized names to mutation methods. This will only exist if - * mutation cannot be set simply by the property or `setAttribute()`. - * @type {Object} - */ - getMutationMethod: {}, - - /** - * Whether the property must be accessed and mutated as an object property. - * @type {Object} - */ - mustUseAttribute: {}, - - /** - * Whether the property must be accessed and mutated using `*Attribute()`. - * (This includes anything that fails ` in `.) - * @type {Object} - */ - mustUseProperty: {}, - - /** - * Whether or not setting a value causes side effects such as triggering - * resources to be loaded or text selection changes. We must ensure that - * the value is only set if it has changed. - * @type {Object} - */ - hasSideEffects: {}, - - /** - * Whether the property should be removed when set to a falsey value. - * @type {Object} - */ - hasBooleanValue: {}, - - /** - * Whether the property must be positive numeric or parse as a positive - * numeric and should be removed when set to a falsey value. - * @type {Object} - */ - hasPositiveNumericValue: {}, - - /** - * All of the isCustomAttribute() functions that have been injected. - */ - _isCustomAttributeFunctions: [], - - /** - * Checks whether a property name is a custom attribute. - * @method - */ - isCustomAttribute: function(attributeName) { - return DOMProperty._isCustomAttributeFunctions.some( - function(isCustomAttributeFn) { - return isCustomAttributeFn.call(null, attributeName); - } - ); - }, - - /** - * Returns the default property value for a DOM property (i.e., not an - * attribute). Most default values are '' or false, but not all. Worse yet, - * some (in particular, `type`) vary depending on the type of element. - * - * TODO: Is it better to grab all the possible properties when creating an - * element to avoid having to create the same element twice? - */ - getDefaultValueForProperty: function(nodeName, prop) { - var nodeDefaults = defaultValueCache[nodeName]; - var testElement; - if (!nodeDefaults) { - defaultValueCache[nodeName] = nodeDefaults = {}; - } - if (!(prop in nodeDefaults)) { - testElement = document.createElement(nodeName); - nodeDefaults[prop] = testElement[prop]; - } - return nodeDefaults[prop]; - }, - - injection: DOMPropertyInjection -}; - -module.exports = DOMProperty; - -},{"./invariant":113}],10:[function(require,module,exports){ -/** - * Copyright 2013 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @providesModule DOMPropertyOperations - * @typechecks static-only - */ - -"use strict"; - -var DOMProperty = require("./DOMProperty"); - -var escapeTextForBrowser = require("./escapeTextForBrowser"); -var memoizeStringOnly = require("./memoizeStringOnly"); - -function shouldIgnoreValue(name, value) { - return value == null || - DOMProperty.hasBooleanValue[name] && !value || - DOMProperty.hasPositiveNumericValue[name] && (isNaN(value) || value < 1); -} - -var processAttributeNameAndPrefix = memoizeStringOnly(function(name) { - return escapeTextForBrowser(name) + '="'; -}); - -if ("production" !== "development") { - var reactProps = { - children: true, - dangerouslySetInnerHTML: true, - key: true, - ref: true - }; - var warnedProperties = {}; - - var warnUnknownProperty = function(name) { - if (reactProps[name] || warnedProperties[name]) { - return; - } - - warnedProperties[name] = true; - var lowerCasedName = name.toLowerCase(); - - // data-* attributes should be lowercase; suggest the lowercase version - var standardName = DOMProperty.isCustomAttribute(lowerCasedName) ? - lowerCasedName : DOMProperty.getPossibleStandardName[lowerCasedName]; - - // For now, only warn when we have a suggested correction. This prevents - // logging too much when using transferPropsTo. - if (standardName != null) { - console.warn( - 'Unknown DOM property ' + name + '. Did you mean ' + standardName + '?' - ); - } - - }; -} - -/** - * Operations for dealing with DOM properties. - */ -var DOMPropertyOperations = { - - /** - * Creates markup for a property. - * - * @param {string} name - * @param {*} value - * @return {?string} Markup string, or null if the property was invalid. - */ - createMarkupForProperty: function(name, value) { - if (DOMProperty.isStandardName[name]) { - if (shouldIgnoreValue(name, value)) { - return ''; - } - var attributeName = DOMProperty.getAttributeName[name]; - return processAttributeNameAndPrefix(attributeName) + - escapeTextForBrowser(value) + '"'; - } else if (DOMProperty.isCustomAttribute(name)) { - if (value == null) { - return ''; - } - return processAttributeNameAndPrefix(name) + - escapeTextForBrowser(value) + '"'; - } else if ("production" !== "development") { - warnUnknownProperty(name); - } - return null; - }, - - /** - * Sets the value for a property on a node. - * - * @param {DOMElement} node - * @param {string} name - * @param {*} value - */ - setValueForProperty: function(node, name, value) { - if (DOMProperty.isStandardName[name]) { - var mutationMethod = DOMProperty.getMutationMethod[name]; - if (mutationMethod) { - mutationMethod(node, value); - } else if (shouldIgnoreValue(name, value)) { - this.deleteValueForProperty(node, name); - } else if (DOMProperty.mustUseAttribute[name]) { - node.setAttribute(DOMProperty.getAttributeName[name], '' + value); - } else { - var propName = DOMProperty.getPropertyName[name]; - if (!DOMProperty.hasSideEffects[name] || node[propName] !== value) { - node[propName] = value; - } - } - } else if (DOMProperty.isCustomAttribute(name)) { - if (value == null) { - node.removeAttribute(DOMProperty.getAttributeName[name]); - } else { - node.setAttribute(name, '' + value); - } - } else if ("production" !== "development") { - warnUnknownProperty(name); - } - }, - - /** - * Deletes the value for a property on a node. - * - * @param {DOMElement} node - * @param {string} name - */ - deleteValueForProperty: function(node, name) { - if (DOMProperty.isStandardName[name]) { - var mutationMethod = DOMProperty.getMutationMethod[name]; - if (mutationMethod) { - mutationMethod(node, undefined); - } else if (DOMProperty.mustUseAttribute[name]) { - node.removeAttribute(DOMProperty.getAttributeName[name]); - } else { - var propName = DOMProperty.getPropertyName[name]; - var defaultValue = DOMProperty.getDefaultValueForProperty( - node.nodeName, - name - ); - if (!DOMProperty.hasSideEffects[name] || - node[propName] !== defaultValue) { - node[propName] = defaultValue; - } - } - } else if (DOMProperty.isCustomAttribute(name)) { - node.removeAttribute(name); - } else if ("production" !== "development") { - warnUnknownProperty(name); - } - } - -}; - -module.exports = DOMPropertyOperations; - -},{"./DOMProperty":9,"./escapeTextForBrowser":99,"./memoizeStringOnly":121}],11:[function(require,module,exports){ -/** - * Copyright 2013 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @providesModule Danger - * @typechecks static-only - */ - -/*jslint evil: true, sub: true */ - -"use strict"; - -var ExecutionEnvironment = require("./ExecutionEnvironment"); - -var createNodesFromMarkup = require("./createNodesFromMarkup"); -var emptyFunction = require("./emptyFunction"); -var getMarkupWrap = require("./getMarkupWrap"); -var invariant = require("./invariant"); -var mutateHTMLNodeWithMarkup = require("./mutateHTMLNodeWithMarkup"); - -var OPEN_TAG_NAME_EXP = /^(<[^ \/>]+)/; -var RESULT_INDEX_ATTR = 'data-danger-index'; - -/** - * Extracts the `nodeName` from a string of markup. - * - * NOTE: Extracting the `nodeName` does not require a regular expression match - * because we make assumptions about React-generated markup (i.e. there are no - * spaces surrounding the opening tag and there is at least one attribute). - * - * @param {string} markup String of markup. - * @return {string} Node name of the supplied markup. - * @see http://jsperf.com/extract-nodename - */ -function getNodeName(markup) { - return markup.substring(1, markup.indexOf(' ')); -} - -var Danger = { - - /** - * Renders markup into an array of nodes. The markup is expected to render - * into a list of root nodes. Also, the length of `resultList` and - * `markupList` should be the same. - * - * @param {array} markupList List of markup strings to render. - * @return {array} List of rendered nodes. - * @internal - */ - dangerouslyRenderMarkup: function(markupList) { - ("production" !== "development" ? invariant( - ExecutionEnvironment.canUseDOM, - 'dangerouslyRenderMarkup(...): Cannot render markup in a Worker ' + - 'thread. This is likely a bug in the framework. Please report ' + - 'immediately.' - ) : invariant(ExecutionEnvironment.canUseDOM)); - var nodeName; - var markupByNodeName = {}; - // Group markup by `nodeName` if a wrap is necessary, else by '*'. - for (var i = 0; i < markupList.length; i++) { - ("production" !== "development" ? invariant( - markupList[i], - 'dangerouslyRenderMarkup(...): Missing markup.' - ) : invariant(markupList[i])); - nodeName = getNodeName(markupList[i]); - nodeName = getMarkupWrap(nodeName) ? nodeName : '*'; - markupByNodeName[nodeName] = markupByNodeName[nodeName] || []; - markupByNodeName[nodeName][i] = markupList[i]; - } - var resultList = []; - var resultListAssignmentCount = 0; - for (nodeName in markupByNodeName) { - if (!markupByNodeName.hasOwnProperty(nodeName)) { - continue; - } - var markupListByNodeName = markupByNodeName[nodeName]; - - // This for-in loop skips the holes of the sparse array. The order of - // iteration should follow the order of assignment, which happens to match - // numerical index order, but we don't rely on that. - for (var resultIndex in markupListByNodeName) { - if (markupListByNodeName.hasOwnProperty(resultIndex)) { - var markup = markupListByNodeName[resultIndex]; - - // Push the requested markup with an additional RESULT_INDEX_ATTR - // attribute. If the markup does not start with a < character, it - // will be discarded below (with an appropriate console.error). - markupListByNodeName[resultIndex] = markup.replace( - OPEN_TAG_NAME_EXP, - // This index will be parsed back out below. - '$1 ' + RESULT_INDEX_ATTR + '="' + resultIndex + '" ' - ); - } - } - - // Render each group of markup with similar wrapping `nodeName`. - var renderNodes = createNodesFromMarkup( - markupListByNodeName.join(''), - emptyFunction // Do nothing special with