├── .gitignore ├── .jshintignore ├── .jshintrc ├── .travis.yml ├── LICENSE ├── README.md ├── cli.js ├── jsxhint.js ├── package.json ├── test ├── .jshintrc ├── fixtures │ ├── jshint_parity.js │ ├── test_article.js │ ├── test_article.jsx │ ├── test_article_without_pragma.js │ ├── test_es6module.jsx │ ├── test_es7classproperties.jsx │ ├── test_es7exponentiation.jsx │ ├── test_flow.js │ ├── test_flow_import.js │ ├── test_harmony.js │ ├── test_malformed.js │ ├── test_malformed.jsx │ └── test_overrides.js └── test.js └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | *node_modules* 2 | test1 3 | test-2 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | test/fixtures 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": false, 3 | "noempty": true, 4 | "newcap": true, 5 | "eqeqeq": true, 6 | "eqnull": true, 7 | "esnext": true, 8 | "undef": true, 9 | "devel": true, 10 | "node": true, 11 | "browser": true, 12 | "evil": false, 13 | "latedef": "nofunc", 14 | "nonew": true, 15 | "trailing": true, 16 | "immed": true, 17 | "smarttabs": true, 18 | "strict": true, 19 | "globals": { 20 | "define": true 21 | }, 22 | "predef": [ "-Promise" ] 23 | } 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | - "0.12" 5 | 6 | # See http://docs.travis-ci.com/user/workers/container-based-infrastructure/ 7 | sudo: false 8 | 9 | before_install: 10 | - npm install -g npm@~1.4.6 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Samuel Reed 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![build status](https://secure.travis-ci.org/STRML/JSXHint.png)](http://travis-ci.org/STRML/JSXHint) 2 | 3 | ## Deprecated 4 | 5 | JSXHint is, and always was, a bad hack to make JSX lintable in the early days of React. It will always be 6 | inferior to proper linters like [ESLint](http://eslint.org/) because it is not able to lint JSX or ES6 syntax, 7 | it instead compiles to ES5 then lints. You may get weird linter errors, especially with more involved 8 | transforms. 9 | 10 | Don't use JSXHint anymore if you can switch to [ESLint](http://eslint.org/). 11 | 12 | ------ 13 | 14 | # JSXHint 15 | A wrapper around JSHint to allow linting of files containing JSX syntax. 16 | 17 | Accepts the same input as JSHint and emits the same output. Switches sent to jsxhint 18 | are forwarded on to jshint. 19 | 20 | Glob parsing, ignores, and jshintrc parsing are all performed by jshint. 21 | 22 | Automatically lints all files with the `.js` and `.jsx` extensions. 23 | 24 | ## Rationale 25 | 26 | This module is intended for use as part of 27 | [SublimeLinter-jsxhint](https://github.com/SublimeLinter/SublimeLinter-jsxhint), 28 | but it can be used separately as a smart replacement for JSHint. It will automatically convert any file with 29 | the `jsx` extension into JS using `react-tools`, then present the file to JSHint for validation. 30 | 31 | Note that as of React 0.12, [it is recommended](https://github.com/facebook/react/issues/832) to use the `.jsx` 32 | file extension rather than the pragma in `.js` files. If you are already using the `.jsx` extension, add the option 33 | `--jsx-only`. This will skip attempted conversion of `.js` files to JSX, but still lint those files. 34 | This is useful for running an entire project. 35 | 36 | `JSXHint` is safe to use as a drop-in replacement for `JSHint`, even when JSX files are not present in a project. 37 | 38 | `JSXHint` offers a simple workflow that accepts JSX files without modification. 39 | Additionally, `JSXHint` actually lints the object definitions generated by the JSX compiler, allowing you to catch 40 | mistakes in your templates (such as undefined variables, syntax errors, and missing modules). 41 | 42 | `JSXHint` cli allows for pattern arrays via [glob-all](https://github.com/jpillora/node-glob-all) 43 | 44 | ## Examples 45 | 46 | ``` 47 | # Lint entire project. JSXHint will only lint .js and .jsx files. 48 | jsxhint . 49 | 50 | # Lint files in `src` folder only. 51 | jsxhint src 52 | 53 | # Lint Project using only `.jsx` extension for JSX. 54 | # Will lint .js files with jshint, .jsx files with jsxhint. 55 | jsxhint --jsx-only . 56 | 57 | # Basic globbing 58 | jsxhint --config ./other-directory/.jshintrc src/foo/*.jsx 59 | 60 | # Multiple patterns 61 | jsxhint 'jsx/**/*' '!scripts/**/*' 'scripts/outlier.jsx' 62 | 63 | # Common multiple patterns usecase - lint .js and .jsx, ignore modules and build 64 | jsxhint '**/*.js*' '!node_modules/**/*' '!build/**/*' 65 | 66 | # Accepts stdin with '-' 67 | jsxhint - < src/file.jsx 68 | 69 | # Exclude files 70 | jsxhint --exclude excludeme.jsx src/foo/*.jsx 71 | 72 | # Lint project using babel (previously 6to5) 73 | # Note that you must explicitly install `babel` if you wish to use it. 74 | jsxhint --babel src 75 | ``` 76 | 77 | ## Installation 78 | `npm install -g jsxhint` 79 | 80 | ## Usage 81 | 82 | ``` 83 | Usage: 84 | jsxhint[OPTIONS] [ARGS] 85 | 86 | Options: 87 | -c, --config STRING Custom configuration file 88 | --reporter STRING Custom reporter (|jslint|checkstyle|unix) 89 | --exclude STRING Exclude files matching the given filename pattern 90 | (same as .jshintignore) 91 | --exclude-path STRINGPass in a custom jshintignore file path 92 | --filename STRING Pass in a filename when using STDIN to emulate config 93 | lookup for that file name 94 | --verbose Show message codes 95 | --show-non-errors Show additional data generated by jshint 96 | -e, --extra-ext STRING Comma-separated list of file extensions to use 97 | (default is .js) 98 | --extract [STRING] Extract inline scripts contained in HTML 99 | (auto|always|never, default to never) (Default is never) 100 | --jslint-reporter Use a jslint compatible reporter (DEPRECATED, use 101 | --reporter=jslint instead) 102 | --checkstyle-reporter Use a CheckStyle compatible XML reporter 103 | (DEPRECATED, use --reporter=checkstyle 104 | instead) 105 | -v, --version Display the current version 106 | -h, --help Display help and usage details 107 | 108 | The above options are native to JSHint, which JSXHint extends. 109 | 110 | JSXHint Options: 111 | --jsx-only Only transform files with the .jsx extension. 112 | Will run somewhat faster. 113 | --babel Use babel (6to5) instead of react esprima. 114 | Useful if you are using es6-module, etc. You must 115 | install the module `babel` manually with npm. 116 | --babel-experimental Use babel with experimental support for ES7. 117 | Useful if you are using es7-async, etc. 118 | --harmony Use react esprima with ES6 transformation support. 119 | Useful if you are using both es6-class and react. 120 | --es6module Pass the --es6module flag to react tools. 121 | --non-strict-es6module Pass this flag to react tools. 122 | ``` 123 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * JSXHint CLI tool 4 | * 5 | * Copyright 2013 (c) Condé Nast 6 | * 7 | * Please see LICENSE for details 8 | * 9 | */ 10 | 11 | 'use strict'; 12 | 13 | var jsxhint = require('./jsxhint'); 14 | var jshintcli = require('jshint/src/cli'); 15 | var fork = require('child_process').fork; 16 | var through = require('through'); 17 | var glob = require('glob-all'); 18 | var fs = require('fs'); 19 | var debug = require('debug')('jsxhint'); 20 | 21 | // Mapping from jsxhint options to: 22 | // - false if no argument is expected. 23 | // - true if an argument is expected. 24 | // - array if one of an enumeration is expected. 25 | // The jshint call will throw an error if it encounters an option that it does 26 | // not recognize. Therefore, we need to filter out jsxhint options before 27 | // calling jshint. Because jsxhint is run in part of a callback of jshint after 28 | // this check, we need to store jsxhint options someplace globally so we can 29 | // access them inside the callback. 30 | var acceptedJSXHintOptions = { 31 | '--babel': false, 32 | '--babel-experimental': false, 33 | '--es6module': false, 34 | '--harmony': false, 35 | '--jsx-only': false, 36 | '--non-strict-es6module': false, 37 | '--transform-errors': ['always', 'jsx', 'never'] 38 | }; 39 | var jsxhintOptions = {}; 40 | 41 | /** 42 | * Intercept -h to show jshint help. 43 | */ 44 | function showHelp(){ 45 | var jshint_proc = fork(require.resolve('jshint/bin/jshint'), ['-h'], {silent: true}); 46 | var ts = through(function write(chunk){ 47 | this.queue(chunk.toString().replace(/(jshint)\b/g, 'jsxhint')); 48 | }, function end() { 49 | // This feels like a hack. There might be a better way to do this. 50 | this.queue('\nThe above options are native to JSHint, which JSXHint extends.\n'); 51 | this.queue('\x1b[1m'); // bold 52 | this.queue('\nJSXHint Options:\n'); 53 | this.queue('\x1b[0m'); 54 | this.queue(' --jsx-only Only transform files with the .jsx extension.\n' + 55 | ' Will run somewhat faster.\n'); 56 | this.queue(' --babel Use babel (6to5) instead of react esprima.\n' + 57 | ' Useful if you are using es6-module, etc. You must \n' + 58 | ' install the module `babel` manually with npm.\n'); 59 | this.queue(' --babel-experimental Use babel with experimental support for ES7.\n' + 60 | ' Useful if you are using es7-async, etc.\n'); 61 | this.queue(' --harmony Use react esprima with ES6 transformation support.\n' + 62 | ' Useful if you are using both es6-class and react.\n'); 63 | this.queue(' --es6module Pass the --es6module flag to react tools.\n'); 64 | this.queue(' --non-strict-es6module Pass this flag to react tools.\n'); 65 | this.queue(' --transform-errors STRING Whether to fail on transform errors.\n' + 66 | ' Valid: always, jsx, never (default: jsx)'); 67 | }); 68 | jshint_proc.stderr.pipe(ts).pipe(process.stderr); 69 | } 70 | 71 | /** 72 | * Intercept -v, shows jsxhint and jshint versions. 73 | */ 74 | function showVersion(){ 75 | var jshint_proc = fork(__dirname + '/node_modules/jshint/bin/jshint', ['-v'], {silent: true}); 76 | var ts = through(function write(chunk){ 77 | this.queue("JSXHint v" + require('./package.json').version + " (" + 78 | chunk.toString().replace("\n", "") + ")\n"); 79 | }); 80 | jshint_proc.stderr.pipe(ts).pipe(process.stderr); 81 | } 82 | 83 | /** 84 | * Wrapper around globbing function. Ignores '.' which has special meaning in jshint. 85 | * @param {Array} args File/glob list. 86 | * @param {Object} opts Glob options. 87 | * @param {Function} cb Callback. 88 | */ 89 | function runGlob(args, opts, cb) { 90 | var dotIdx = args.indexOf('.'); 91 | if (dotIdx !== -1) { 92 | args.splice(dotIdx, 1); 93 | } 94 | glob(args, opts, function(err, globbed) { 95 | if (Array.isArray(globbed) && dotIdx !== -1) globbed.push('.'); 96 | cb(err, globbed); 97 | }); 98 | } 99 | 100 | /** 101 | * Proxy run function. Reaches out to jsxhint to transform 102 | * incoming stream or read files & transform. 103 | * @param {Object} opts Opts as created by JSHint. 104 | * @param {Function} cb Callback. 105 | */ 106 | function run(opts, cb){ 107 | opts.extensions = opts.extensions ? opts.extensions + ',.jsx' : '.jsx'; 108 | 109 | // glob-all fixes windows glob issues and provides array support 110 | // i.e. jsxhint ['jsx/**/*','!scripts/**/*','scripts/outlier.jsx'] 111 | runGlob(opts.args, {nodir: true}, function(err, globbed) { 112 | if (err) return cb(err); 113 | 114 | // Reassign the globbed files back to opts.args, so it looks like we just 115 | // fed jshint a big list of files. 116 | opts.args = globbed; 117 | var files = jshintcli.gather(opts); 118 | 119 | if (opts.useStdin) { 120 | jsxhint.transformStream(process.stdin, jsxhintOptions, opts, cb); 121 | } else { 122 | jsxhint.transformFiles(files, jsxhintOptions, opts, cb); 123 | } 124 | }); 125 | } 126 | 127 | /** 128 | * Intercept configured reporter and change file names so it looks 129 | * like nothing happened. 130 | * @param {Reporter} reporter JSHint reporter 131 | * @param {Object} filesMap Map related transformed files to original file paths. 132 | * @return {Function} Wrapper around configured reporter. Same arity as reporter. 133 | */ 134 | function interceptReporter(reporter, filesMap){ 135 | if(!reporter) reporter = require('jshint/src/reporters/default').reporter; 136 | return function(results, data, opts){ 137 | if (filesMap) { 138 | results.forEach(function(result){ 139 | result.file = filesMap[result.file]; 140 | }); 141 | } 142 | return reporter(results, data, opts); 143 | }; 144 | } 145 | 146 | /** 147 | * Unlink temporary files created by JSX processor. 148 | */ 149 | function unlinkTemp(){ 150 | try { 151 | fs.removeSync(jsxhint.tmpdir); 152 | } catch(e) { 153 | // ignore 154 | } 155 | } 156 | 157 | 158 | // Run program. Intercept JSHintCLI.run to process JSX files. 159 | try { 160 | if (process.argv.indexOf('-h') !== -1 || process.argv.indexOf('--help') !== -1){ 161 | showHelp(); 162 | } else if (process.argv.indexOf('-v') !== -1 || process.argv.indexOf('--version') !== -1){ 163 | showVersion(); 164 | } else { 165 | jshintcli.originalRun = jshintcli.run; 166 | jshintcli.run = function(opts, cb){ 167 | // Files can either be string data (from stdin), or an object 168 | // where keys are the original file name and values are the temporary file 169 | // name where the transformed source is written. 170 | run(opts, function(err, files){ 171 | opts.reporter = interceptReporter(opts.reporter, files); 172 | 173 | // always false, stdin is never going to be usable as we may have read from it for the 174 | // transform. 175 | opts.useStdin = false; 176 | 177 | if (err) { 178 | if (!err.error) { 179 | console.error("Runtime error:", err.stack); 180 | } else { 181 | opts.reporter([err], {}, opts); 182 | } 183 | return process.exit(1); 184 | } 185 | 186 | opts.args = Object.keys(files); 187 | debug("Running JSHint with options: %j", opts); 188 | 189 | // Weird sync/async function, jshint oddity 190 | var done = function(passed){ 191 | if (passed == null) return; 192 | unlinkTemp(); 193 | cb(passed); 194 | }; 195 | done(jshintcli.originalRun(opts, done)); 196 | }); 197 | }; 198 | 199 | var argv = process.argv.filter(function(value, ii, args) { 200 | if (acceptedJSXHintOptions.hasOwnProperty(value)) { 201 | var argValue = true; 202 | var config = acceptedJSXHintOptions[value]; 203 | if (config) { 204 | var nextValue = args[ii + 1]; 205 | if (!nextValue || nextValue.charAt(0) === '-') { 206 | throw new Error('ERROR: ' + value + ' requires an argument.'); 207 | } 208 | if (Array.isArray(config) && config.indexOf(nextValue) < 0) { 209 | throw new Error('ERROR: ' + value + ' must be one of: ' + config.join(', ')); 210 | } 211 | argValue = nextValue; 212 | } 213 | 214 | // Store the jsxhint specific option globally so we can access it when 215 | // we run the transformation. 216 | jsxhintOptions[value] = argValue; 217 | 218 | // Need to filter out jsxhint specific options so jshint doesn't throw 219 | // an unknown option error. 220 | return false; 221 | } 222 | 223 | return true; 224 | }); 225 | 226 | jshintcli.interpret(argv); 227 | } 228 | } catch (e){ 229 | console.log(e.message.replace(/cli\.js/, 'jsxhint')); 230 | unlinkTemp(); 231 | process.exit(1); 232 | } 233 | -------------------------------------------------------------------------------- /jsxhint.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JSXHint CLI tool 3 | * 4 | * Copyright 2013 (c) Samuel Reed 5 | * Inspired by and based on JSXHint by Conde Nast 6 | * 7 | * Please see LICENSE for details 8 | * 9 | */ 10 | 11 | 'use strict'; 12 | 13 | var fs = require('fs-extra'); 14 | var path = require('path'); 15 | var Promise = require('bluebird'); 16 | var utils = require('./utils'); 17 | var debug = require('debug')('jsxhint'); 18 | 19 | var jstransform = require('jstransform/simple'); 20 | try { 21 | var babel = require('babel-core'); 22 | } catch(e) { 23 | // ignore 24 | } 25 | 26 | /** 27 | * Transform a JSX file into a JS file for linting. 28 | * @async 29 | * @param {String} fileStream Readable stream containing file contents. 30 | * @param {String} fileName Name of the file; "stdin" if reading from stdio. 31 | * @param {Object} opts Options. 32 | * @param {Function} cb The callback to call when it's ready. 33 | */ 34 | function transformJSX(fileStream, fileName, opts, cb){ 35 | 36 | // Allow omitting filename 37 | if (typeof fileName === "object"){ 38 | cb = opts; 39 | opts = fileName; 40 | fileName = fileStream; 41 | } 42 | 43 | if (!babel && (opts['--babel'] || opts['--babel-experimental'])) { 44 | throw new Error("Optional babel parser not installed. Please `npm install [-g] babel-core`."); 45 | } 46 | 47 | // Allow passing strings into this method e.g. when using it as a lib 48 | if (typeof fileStream === "string"){ 49 | fileStream = fs.createReadStream(fileStream, {encoding: "utf8"}); 50 | } 51 | 52 | return utils.drainStream(fileStream) 53 | .then(function processFile(source) { 54 | var hasExtension = /\.jsx$/.exec(fileName) || fileName === "stdin"; 55 | // console.error('has extension', hasExtension); 56 | // console.log(fileName); 57 | if ((opts['--jsx-only'] && hasExtension) || !opts['--jsx-only']) { 58 | try { 59 | return transformSource(source, opts); 60 | } catch(e) { 61 | // Seems that esprima has some problems with some js syntax. 62 | if (opts['--transform-errors'] === 'always' || 63 | (opts['--transform-errors'] !== 'never' && hasExtension)) { 64 | console.error("Error while transforming file " + fileName + "\n", e.stack); 65 | throw utils.transformError(fileName, e, opts); 66 | } 67 | } 68 | } 69 | return source; 70 | }) 71 | .nodeify(cb); 72 | } 73 | 74 | function transformSource(source, opts){ 75 | if (opts['--babel'] || opts['--babel-experimental']) { 76 | return babel.transform(source, {stage: opts['--babel-experimental'] ? 0 : 2, retainLines: true}).code; 77 | } else { 78 | return jstransform.transform(source, { 79 | react: true, 80 | harmony: opts['--harmony'], 81 | stripTypes: true, 82 | nonStrictEs6module: opts['--non-strict-es6module'] || false, 83 | es6module: opts['--es6module'] || false 84 | }).code; 85 | } 86 | } 87 | 88 | /** 89 | * Transform a list of files from jsx. Calls back with a map relating 90 | * the new files (temp files) to the old file names, e.g. 91 | * {tempFile: originalFileName} 92 | * 93 | * @param {Array} files File paths to transform. 94 | * @param {Object} jsxhintOpts Options for JSXHint. 95 | * @param {Object} jshintOpts Options for JSHint. 96 | * @param {Function} cb Callback. 97 | */ 98 | function transformFiles(files, jsxhintOpts, jshintOpts, cb){ 99 | return Promise.map(files, function(fileName) { 100 | return transformJSX(fileName, jsxhintOpts); 101 | }) 102 | .then(function(filesContents) { 103 | debug("Successfully transformed %d files to JSX.", files.length); 104 | 105 | var tempFileNames = utils.createTempFiles(files, filesContents); 106 | debug("Moved %d files to temp directory at %s.", files.length, exports.tmpdir); 107 | // Create map of temp file names to original file names 108 | var fileNameMap = {}; 109 | files.forEach(function(fileName, index){ 110 | fileNameMap[tempFileNames[index]] = fileName; 111 | }); 112 | return fileNameMap; 113 | }) 114 | .nodeify(cb); 115 | } 116 | 117 | /** 118 | * Given a stream (stdin), transform and save to a temporary file so it can be piped 119 | * into JSHint. 120 | * JSHint in stream mode attempts to read from process.stdin. 121 | * Since we can't reload process.stdin with the new transformed data (without forking), 122 | * we instead just write to a temp file and load it into JSHint. 123 | * 124 | * @param {ReadableStream} fileStream Readable stream containing data to transform. 125 | * @param {Object} jsxhintOpts Options for JSXHint. 126 | * @param {Object} jshintOpts Options for JSHint. 127 | * @param {Function} cb Callback. 128 | */ 129 | function transformStream(fileStream, jsxhintOpts, jshintOpts, cb){ 130 | // JSHint now supports a '--filename' option for stdin, allowing overrides to work properly. 131 | var fileName = jshintOpts && jshintOpts.filename.replace(process.cwd(), '') || 'stdin'; 132 | 133 | return transformJSX(fileStream, fileName, jsxhintOpts) 134 | .then(function(contents){ 135 | var tempFileName = utils.createTempFile(path.join(process.cwd(), fileName), contents); 136 | var out = {}; 137 | out[tempFileName] = fileName; 138 | return out; 139 | }) 140 | .nodeify(cb); 141 | } 142 | 143 | exports.tmpdir = utils.tmpdir; 144 | exports.transformJSX = transformJSX; 145 | exports.transformFiles = transformFiles; 146 | exports.transformStream = transformStream; 147 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsxhint", 3 | "version": "0.15.1", 4 | "description": "Wrapper for JSHint to allow hinting of JSX files", 5 | "main": "jsxhint.js", 6 | "bin": "./cli.js", 7 | "engine": "node >= 0.8", 8 | "scripts": { 9 | "test": "node ./node_modules/tap/bin/tap test/test.js", 10 | "lint": "jshint" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "STRML/JSXHint" 15 | }, 16 | "directories": { 17 | "test": "test" 18 | }, 19 | "prefer_global": true, 20 | "keywords": [ 21 | "jshint", 22 | "jsx", 23 | "react", 24 | "lint", 25 | "jslint", 26 | "reactjs" 27 | ], 28 | "author": "Samuel Reed", 29 | "contributors": [ 30 | "Casey Foster " 31 | ], 32 | "license": "MIT", 33 | "dependencies": { 34 | "bluebird": "^2.9.14", 35 | "debug": "~2.1.0", 36 | "fs-extra": "^0.16.5", 37 | "glob-all": "^3.0.1", 38 | "jshint": "^2.6.0", 39 | "jstransform": "^11.0.2", 40 | "through": "~2.3.6" 41 | }, 42 | "devDependencies": { 43 | "babel": "^5.4.0", 44 | "precommit-hook": "~1.0.7", 45 | "tap": "~0.4.13" 46 | }, 47 | "publishConfig": { 48 | "registry": "https://registry.npmjs.org" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": false, 3 | "noempty": true, 4 | "newcap": true, 5 | "eqeqeq": true, 6 | "eqnull": true, 7 | "esnext": true, 8 | "undef": true, 9 | "devel": true, 10 | "node": true, 11 | "browser": true, 12 | "evil": false, 13 | "latedef": "nofunc", 14 | "nonew": true, 15 | "trailing": true, 16 | "immed": true, 17 | "smarttabs": true, 18 | "strict": true, 19 | "globals": { 20 | "define": true 21 | }, 22 | "overrides": { 23 | // Comments should be fine in this file 24 | "fixtures/**/**_overrides.js": { 25 | "strict": false 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/fixtures/jshint_parity.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var React = require('react-tools/build/modules/React'); 4 | 5 | var statusForm = React.createClass({displayName: 'statusForm', 6 | render: function(){ 7 | if (var foo = true){ 8 | // linter will hate this 9 | } 10 | return React.DOM.form( {class:"form-horizontal", role:"form"}, 11 | React.DOM.div( {class:"form-group"}, 12 | React.DOM.label( {for:"brand-lead", class:"col-sm-4 control-label"}, "Brand lead:"), 13 | React.DOM.div( {class:"col-sm-8"}, 14 | React.DOM.input( {class:"form-control", 15 | placeholder:"brand lead", 16 | type:"text", 17 | id:"brand-lead", 18 | ref:"brandLead", 19 | 'data-state':"brandLead"} ) 20 | ) 21 | ), 22 | React.DOM.div( {class:"form-group"}, 23 | React.DOM.label( {for:"status-text", class:"col-sm-4 control-label"}, "Status:"), 24 | React.DOM.div( {class:"col-sm-8"}, 25 | React.DOM.textarea( 26 | {id:"status-text", 27 | class:"form-control", 28 | rows:"40", 29 | ref:"statusText", 30 | 'data-state':"statusText"}) 31 | ) 32 | ), 33 | 34 | React.DOM.input( {class:"btn btn-success", type:"submit", value:"Save status"}) 35 | ) 36 | } 37 | }); 38 | 39 | module.exports = statusForm; 40 | -------------------------------------------------------------------------------- /test/fixtures/test_article.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var React = require('react-tools/build/modules/React'); 4 | 5 | var statusForm = React.createClass({ 6 | render: function(){ 7 | return
8 |
9 | 10 |
11 | 17 |
18 |
19 |
20 | 21 |
22 |