diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7e3649a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab diff --git a/.gitignore b/.gitignore index 7595163..56b6cc8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,14 @@ -.DS_Store +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# dependencies node_modules + +# testing +coverage + +# production +build + +# misc +.DS_Store npm-debug.log diff --git a/.jscsrc b/.jscsrc deleted file mode 100644 index f1f3b40..0000000 --- a/.jscsrc +++ /dev/null @@ -1,32 +0,0 @@ -{ - "preset": "airbnb", - "validateIndentation": 4, - "excludeFiles": ["**/node_modules/**"], - "disallowMixedSpacesAndTabs": true, - "disallowMultipleLineBreaks": true, - "disallowMultipleLineStrings": true, - "disallowMultipleSpaces": true, - "disallowMultipleVarDecl": true, - "disallowNewlineBeforeBlockStatements": true, - "disallowOperatorBeforeLineBreak": [ - "+", - "." - ], - "requireTrailingComma": false, - "validateParameterSeparator": ", ", - "validateQuoteMarks": "'", - "requireSpaceBetweenArguments": true, - "requirePaddingNewLinesAfterBlocks": { - "allExcept": [ - "inCallExpressions", - "inArrayExpressions", - "inProperties" - ] - }, - "disallowPaddingNewlinesBeforeKeywords": [ - "else", - "catch", - "void", - "typeof" - ] -} \ No newline at end of file diff --git a/.scss-lint.yml b/.scss-lint.yml index 0384c19..129253f 100644 --- a/.scss-lint.yml +++ b/.scss-lint.yml @@ -21,8 +21,6 @@ linters: DuplicateProperty: enabled: true - exclude: - - bower_components/**/*.scss EmptyLineBetweenBlocks: enabled: false @@ -38,7 +36,7 @@ linters: Indentation: enabled: true - width: 4 + width: 2 LeadingZero: enabled: true diff --git a/README.md b/README.md index cd4abd8..79884de 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,22 @@ -# NEVERBUILD generator +# Neverbuild V2 -[Yeoman](http://yeoman.io) generator that scaffolds out a front-end web app. +Kickstarts a new React app ## Features -* Grunt - - Automagically compile your SCSS - - Image optimization -* libsass -* Bourbon for Sass - A simple and lightweight mixin library for SASS -* Neat for Bourbon - A lightweight semantic grid framework for SASS and Bourbon -* jQuery -* Basic .editorconfig file for consistency across editors/IDEs -* jscsrc and scss-lint rules +* React +* React Router +* Webpack +* SCSS -For more information on what `generator-neverbuild` can do for you, take a look at the [Grunt tasks](https://github.com/vslio/generator-neverbuild/blob/master/app/templates/_package.json) used in the `package.json`. - - -## Getting Started - -- Install: `npm install -g generator-neverbuild` -- Create project directory and cd into it: `mkdir project-name && cd $_` -- Run: `yo neverbuild` -- Run `grunt` for building & watch + compile your SCSS files +## Getting started +- Install: `npm install` +- Start dev: `npm start` +- Production build: `npm run build` ## License -[MIT license](http://opensource.org/licenses/MIT) \ No newline at end of file +[MIT license](http://opensource.org/licenses/MIT) + +This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app). diff --git a/app/index.js b/app/index.js deleted file mode 100644 index 1d111fe..0000000 --- a/app/index.js +++ /dev/null @@ -1,56 +0,0 @@ -'use strict'; - -var path = require('path'); -var yeoman = require('yeoman-generator'); -var chalk = require('chalk'); -var mkdirp = require('mkdirp'); -var _s = require('underscore.string'); - -var NEVERGenerator = yeoman.generators.Base.extend({ - init: function() { - this.pkg = require('../package.json'); - }, - - app: function() { - this.slugName = _s.slugify(this.appname); - - this.imagesFolder = 'images'; - this.scssFolder = 'scss'; - this.cssFolder = 'css'; - this.jsFolder = 'javascript'; - - this.log(chalk.magenta( - '[ Creating the structure. ]' + - ' ' - )); - - // Creating all the project folders - mkdirp(this.imagesFolder); - mkdirp(this.scssFolder); - mkdirp(this.cssFolder); - mkdirp(this.jsFolder); - - // Moving the right files/folders to the right folders - this.template('_package.json', 'package.json'); - this.copy('_Gruntfile.js', 'Gruntfile.js'); - this.copy('_gitignore', '.gitignore'); - this.copy('_editorconfig', '.editorconfig'); - this.copy('_jscsrc', '.jscsrc'); - this.copy('_scss-lint.yml', '.scss-lint.yml'); - }, - - projectfiles: function() { - this.template('_index.html', 'index.html'); - this.directory('_scss', this.scssFolder); - }, - - install: function() { - this.log(chalk.green( - '[ Cool. ]' - )); - - this.installDependencies(); - } -}); - -module.exports = NEVERGenerator; diff --git a/app/templates/_Gruntfile.js b/app/templates/_Gruntfile.js deleted file mode 100644 index 6b97217..0000000 --- a/app/templates/_Gruntfile.js +++ /dev/null @@ -1,95 +0,0 @@ -module.exports = function(grunt) { - grunt.project = { - name: '<%= slugName %>', - assetsFolder: '' - }; - - grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), - connect: { - dist: { - options: { - port: 8888, - - // Change this to '0.0.0.0' to access the server from outside - hostname: 'localhost' - } - } - }, - sass: { - dev: { - options: { - outputStyle: 'nested', - sourcemap: true - }, - files: [{ - expand: true, - cwd: grunt.project.assetsFolder + 'scss', - src: ['app.scss'], - dest: grunt.project.assetsFolder + 'css', - ext: '.css' - }] - }, - dist: { - options: { - outputStyle: 'compressed', - sourcemap: false - }, - files: [{ - expand: true, - cwd: grunt.project.assetsFolder + 'scss', - src: ['app.scss'], - dest: grunt.project.assetsFolder + 'css', - ext: '.css' - }] - } - }, - stripCssComments: { - dist: { - files: { - 'css/app.css': 'css/app.css' - } - } - }, - watch: { - options: { - livereload: true - }, - sass: { - files: grunt.project.assetsFolder + 'scss/{,**/}*.scss', - tasks: ['sass:dev'] - } - }, - imagemin: { - dist: { - files: [{ - expand: true, - cwd: grunt.project.assetsFolder + 'images', - src: '{,*/}*.{gif,jpeg,jpg,png}', - dest: grunt.project.assetsFolder + 'images' - }] - } - } - }); - - require('load-grunt-tasks')(grunt); - - // Time how long tasks take. Can help when optimizing build times - require('time-grunt')(grunt); - - grunt.registerTask('default', [ - 'sass:dev', - 'watch' - ]); - - grunt.registerTask('dist', [ - 'sass:dist', - 'stripCssComments:dist' - ]); - - grunt.registerTask('serve', [ - 'sass:dev', - 'connect:dist', - 'watch' - ]); -}; diff --git a/app/templates/_editorconfig b/app/templates/_editorconfig deleted file mode 100644 index 304901d..0000000 --- a/app/templates/_editorconfig +++ /dev/null @@ -1,30 +0,0 @@ -# This is our root editor config file -root = true - -# All files -# Linefeeds please -# Always end on a new line -# Always do UTF-8 -# Indent with 4 spaces -# No messy whitespace at the end of lines -[*] -end_of_line = lf -insert_final_newline = true -charset = utf-8 -indent_style = space -indent_size = 4 -trim_trailing_whitespace = true - -# YAML files only -# Indent with 2 spaces in YAML, easier to read -[*.yml] -indent_size = 2 - -# Don't strip trailing whitespace in MarkDown files -[*.md] -trim_trailing_whitespace = false - -# Makefiles -# make requires tabs -[Makefile] -indent_style = tab diff --git a/app/templates/_gitignore b/app/templates/_gitignore deleted file mode 100644 index 5d36fab..0000000 --- a/app/templates/_gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules -dist -.tmp -.sass-cache -bower_components \ No newline at end of file diff --git a/app/templates/_index.html b/app/templates/_index.html deleted file mode 100755 index 95012dc..0000000 --- a/app/templates/_index.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - <%= slugName %> - - - - - - - -
- - - - - - - diff --git a/app/templates/_jscsrc b/app/templates/_jscsrc deleted file mode 100644 index f1f3b40..0000000 --- a/app/templates/_jscsrc +++ /dev/null @@ -1,32 +0,0 @@ -{ - "preset": "airbnb", - "validateIndentation": 4, - "excludeFiles": ["**/node_modules/**"], - "disallowMixedSpacesAndTabs": true, - "disallowMultipleLineBreaks": true, - "disallowMultipleLineStrings": true, - "disallowMultipleSpaces": true, - "disallowMultipleVarDecl": true, - "disallowNewlineBeforeBlockStatements": true, - "disallowOperatorBeforeLineBreak": [ - "+", - "." - ], - "requireTrailingComma": false, - "validateParameterSeparator": ", ", - "validateQuoteMarks": "'", - "requireSpaceBetweenArguments": true, - "requirePaddingNewLinesAfterBlocks": { - "allExcept": [ - "inCallExpressions", - "inArrayExpressions", - "inProperties" - ] - }, - "disallowPaddingNewlinesBeforeKeywords": [ - "else", - "catch", - "void", - "typeof" - ] -} \ No newline at end of file diff --git a/app/templates/_package.json b/app/templates/_package.json deleted file mode 100644 index 6057fd7..0000000 --- a/app/templates/_package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "<%= slugName %>", - "version": "0.0.1", - "license": "MIT", - "engines": { - "node": ">=0.11.0" - }, - "dependencies": {}, - "devDependencies": { - "grunt-cli": "~0.1.13", - "grunt": "~0.4.5", - "grunt-sass": "^1.1.0", - "grunt-contrib-watch": "~0.6.1", - "load-grunt-tasks": "~3.1.0", - "grunt-concurrent": "^1.0.0", - "grunt-contrib-connect": "~0.9.0", - "grunt-contrib-imagemin": "^0.9.3", - "grunt-strip-css-comments": "~1.2.0", - "time-grunt": "^1.0.0", - "bourbon": "~4.2.6", - "bourbon-neat": "~1.7.2" - } -} diff --git a/app/templates/_scss-lint.yml b/app/templates/_scss-lint.yml deleted file mode 100644 index 0384c19..0000000 --- a/app/templates/_scss-lint.yml +++ /dev/null @@ -1,106 +0,0 @@ -# Documentation: https://github.com/brigade/scss-lint/blob/master/lib/scss_lint/linter/README.md -linters: - BorderZero: - enabled: true - convention: none - - ColorKeyword: - enabled: true - - ColorVariable: - enabled: false - - Comment: - enabled: false - - DebugStatement: - enabled: true - - DeclarationOrder: - enabled: true - - DuplicateProperty: - enabled: true - exclude: - - bower_components/**/*.scss - - EmptyLineBetweenBlocks: - enabled: false - - EmptyRule: - enabled: true - - HexNotation: - enabled: false - - ImportantRule: - enabled: false - - Indentation: - enabled: true - width: 4 - - LeadingZero: - enabled: true - style: include_zero - - NestingDepth: - enabled: true - - PlaceholderInExtend: - enabled: true - - PropertySortOrder: - enabled: false - - QualifyingElement: - enabled: true - allow_element_with_attribute: true - - SelectorDepth: - enabled: true - - SelectorFormat: - enabled: true - convention: hyphenated_BEM - - SingleLinePerProperty: - enabled: true - allow_single_line_rule_sets: false - - SingleLinePerSelector: - enabled: true - - SpaceAfterComma: - enabled: true - - SpaceAfterPropertyColon: - enabled: true - style: one_space - - SpaceBeforeBrace: - enabled: true - allow_single_line_padding: true - style: space - - SpaceBetweenParens: - enabled: true - spaces: 0 - - StringQuotes: - enabled: true - style: double_quotes - - TrailingSemicolon: - enabled: true - - UnnecessaryMantissa: - enabled: true - - UrlQuotes: - enabled: true - - VariableForProperty: - enabled: true - properties: - - color diff --git a/app/templates/_scss/_base/_grid-settings.scss b/app/templates/_scss/_base/_grid-settings.scss deleted file mode 100755 index 3268210..0000000 --- a/app/templates/_scss/_base/_grid-settings.scss +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Base styles ~ Grid options - */ -$gutter: 0.3em; -$border-box-sizing: true; -$grid-columns: 12; -$max-width: none; -$visual-grid: false; -$visual-grid-color: darken(#EFF7FB, 10%); diff --git a/app/templates/_scss/_base/_grid.scss b/app/templates/_scss/_base/_grid.scss deleted file mode 100644 index f991afe..0000000 --- a/app/templates/_scss/_base/_grid.scss +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Base styles ~ Grid - */ -// .row { @include row; } -// .one { @include span-columns(1); } -// .two { @include span-columns(2); } -// .three { @include span-columns(3); } -// .four { @include span-columns(4); } -// .five { @include span-columns(5); } -// .six { @include span-columns(6); } -// .seven { @include span-columns(7); } -// .eight { @include span-columns(8); } -// .nine { @include span-columns(9); } -// .ten { @include span-columns(10); } -// .eleven { @include span-columns(11); } -// .twelve { @include span-columns(12); } \ No newline at end of file diff --git a/app/templates/_scss/_base/_type.scss b/app/templates/_scss/_base/_type.scss deleted file mode 100644 index b695b8c..0000000 --- a/app/templates/_scss/_base/_type.scss +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Base ~ Type - */ - -/** - * Type variables - */ -$base-font: sans-serif !default; -$alt-font: sans-serif !default; - -/** - * Type styles - */ -h1, -.h1, -%h1, -h2, -.h2, -%h2, -h3, -.h3, -%h3, -h4, -.h4, -%h4, -h5, -.h5, -%h5, -h6, -.h6, -%h6 { - margin: 14px 0; - font-family: $alt-font; - font-weight: normal; - font-style: normal; - color: $heading-colour; - text-rendering: optimizeLegibility; -} - -h1, -.h1, -%h1 { - font-size: rem(44); - line-height: 48.4px; -} - -h2, -.h2, -%h2 { - font-size: rem(37); - line-height: 40.7px; -} - -h3, -.h3, -%h3 { - font-size: rem(27); - line-height: 29.7px; -} - -h4, -.h4, -%h4 { - font-size: rem(23); - line-height: 25.3px; -} - -h5, -.h5, -%h5 { - font-size: rem(17); - line-height: 18.7px; -} - -h6, -.h6, -%h6 { - font-size: rem(14); - line-height: 15.8px; -} - -a { - color: $link-colour; - text-decoration: none; -} - -p { - margin-bottom: 17px; - font-family: inherit; - font-weight: normal; -} - -hr { - margin: 22px 0 21px; - height: 0; - border: solid $hr-colour; - border-width: 1px 0 0; - clear: both; -} - -em, -i { - font-style: italic; - line-height: inherit; -} - -strong, -b { - font-weight: bold; - line-height: inherit; -} - -small { - font-size: 60%; - line-height: inherit; -} diff --git a/app/templates/_scss/app.scss b/app/templates/_scss/app.scss deleted file mode 100644 index 5f37112..0000000 --- a/app/templates/_scss/app.scss +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Main Project imports - */ - -/** - * Various project settings that need to be set globally - */ - -/** - * Bourbon & Neat Libraries - */ -@import "../node_modules/bourbon/app/assets/stylesheets/_bourbon"; - -// Uncomment to enable Neat -// @import "_base/_grid-settings"; -// @import "../node_modules/bourbon-neat/app/assets/stylesheets/_neat-helpers"; -// @import "../node_modules/bourbon-neat/app/assets/stylesheets/_neat"; - -/** - * Base styles - */ -@import "_base/_colours"; -@import "_base/_easings"; -@import "_base/_normalize"; -@import "_base/_type"; -@import "_base/_grid"; -@import "_base/_forms"; -@import "_base/_base"; - -/** - * Reusable Component styles (header/footer/dropdowns etc) - * Put them inside "common" folder - */ - - -/** - * Module styles (individual page styles) - * Put them inside "modules" folder - */ - - -/** - * Plugin styles - * Put them inside "plugins" folder - */ diff --git a/config/babel.dev.js b/config/babel.dev.js new file mode 100644 index 0000000..b40ff97 --- /dev/null +++ b/config/babel.dev.js @@ -0,0 +1,40 @@ +var path = require('path'); +var findCacheDir = require('find-cache-dir'); + +module.exports = { + // Don't try to find .babelrc because we want to force this configuration. + babelrc: false, + // This is a feature of `babel-loader` for webpack (not Babel itself). + // It enables caching results in ./node_modules/.cache/react-scripts/ + // directory for faster rebuilds. We use findCacheDir() because of: + // https://github.com/facebookincubator/create-react-app/issues/483 + cacheDirectory: findCacheDir({ + name: 'react-scripts' + }), + presets: [ + // Latest stable ECMAScript features + require.resolve('babel-preset-latest'), + // JSX, Flow + require.resolve('babel-preset-react') + ], + plugins: [ + // class { handleClick = () => { } } + require.resolve('babel-plugin-transform-class-properties'), + // { ...todo, completed: true } + require.resolve('babel-plugin-transform-object-rest-spread'), + // function* () { yield 42; yield 43; } + [require.resolve('babel-plugin-transform-regenerator'), { + // Async functions are converted to generators by babel-preset-latest + async: false + }], + // Polyfills the runtime needed for async/await and generators + [require.resolve('babel-plugin-transform-runtime'), { + helpers: false, + polyfill: false, + regenerator: true, + // Resolve the Babel runtime relative to the config. + // You can safely remove this after ejecting: + moduleName: path.dirname(require.resolve('babel-runtime/package')) + }] + ] +}; diff --git a/config/babel.prod.js b/config/babel.prod.js new file mode 100644 index 0000000..28fe233 --- /dev/null +++ b/config/babel.prod.js @@ -0,0 +1,38 @@ +var path = require('path'); + +module.exports = { + // Don't try to find .babelrc because we want to force this configuration. + babelrc: false, + presets: [ + // Latest stable ECMAScript features + require.resolve('babel-preset-latest'), + // JSX, Flow + require.resolve('babel-preset-react') + ], + plugins: [ + // class { handleClick = () => { } } + require.resolve('babel-plugin-transform-class-properties'), + // { ...todo, completed: true } + require.resolve('babel-plugin-transform-object-rest-spread'), + // function* () { yield 42; yield 43; } + [require.resolve('babel-plugin-transform-regenerator'), { + // Async functions are converted to generators by babel-preset-latest + async: false + }], + // Polyfills the runtime needed for async/await and generators + [require.resolve('babel-plugin-transform-runtime'), { + helpers: false, + polyfill: false, + regenerator: true, + // Resolve the Babel runtime relative to the config. + // You can safely remove this after ejecting: + moduleName: path.dirname(require.resolve('babel-runtime/package')) + }], + // Optimization: hoist JSX that never changes out of render() + // Disabled because of issues: + // * https://github.com/facebookincubator/create-react-app/issues/525 + // * https://phabricator.babeljs.io/search/query/pCNlnC2xzwzx/ + // TODO: Enable again when these issues are resolved. + // require.resolve('babel-plugin-transform-react-constant-elements') + ] +}; diff --git a/config/env.js b/config/env.js new file mode 100644 index 0000000..846077f --- /dev/null +++ b/config/env.js @@ -0,0 +1,15 @@ +// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be +// injected into the application via DefinePlugin in Webpack configuration. + +var REACT_APP = /^REACT_APP_/i; +var NODE_ENV = JSON.stringify(process.env.NODE_ENV || 'development'); + +module.exports = Object + .keys(process.env) + .filter(key => REACT_APP.test(key)) + .reduce((env, key) => { + env['process.env.' + key] = JSON.stringify(process.env[key]); + return env; + }, { + 'process.env.NODE_ENV': NODE_ENV + }); diff --git a/config/eslint.js b/config/eslint.js new file mode 100644 index 0000000..53361db --- /dev/null +++ b/config/eslint.js @@ -0,0 +1,200 @@ +// Inspired by https://github.com/airbnb/javascript but less opinionated. + +// We use eslint-loader so even warnings are very visibile. +// This is why we only use "WARNING" level for potential errors, +// and we don't use "ERROR" level at all. + +// In the future, we might create a separate list of rules for production. +// It would probably be more strict. + +module.exports = { + root: true, + + parser: 'babel-eslint', + + // import plugin is temporarily disabled, scroll below to see why + plugins: [/*'import', */'flowtype', 'jsx-a11y', 'react'], + + env: { + browser: true, + commonjs: true, + es6: true, + jest: true, + node: true + }, + + parserOptions: { + ecmaVersion: 6, + sourceType: 'module', + ecmaFeatures: { + jsx: true, + generators: true, + experimentalObjectRestSpread: true + } + }, + + settings: { + 'import/ignore': [ + 'node_modules', + '\\.(json|css|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$', + ], + 'import/extensions': ['.js'], + 'import/resolver': { + node: { + extensions: ['.js', '.json'] + } + } + }, + + rules: { + // http://eslint.org/docs/rules/ + 'array-callback-return': 'warn', + 'default-case': ['warn', { commentPattern: '^no default$' }], + 'dot-location': ['warn', 'property'], + eqeqeq: ['warn', 'allow-null'], + 'guard-for-in': 'warn', + 'new-parens': 'warn', + 'no-array-constructor': 'warn', + 'no-caller': 'warn', + 'no-cond-assign': ['warn', 'always'], + 'no-const-assign': 'warn', + 'no-control-regex': 'warn', + 'no-delete-var': 'warn', + 'no-dupe-args': 'warn', + 'no-dupe-class-members': 'warn', + 'no-dupe-keys': 'warn', + 'no-duplicate-case': 'warn', + 'no-empty-character-class': 'warn', + 'no-empty-pattern': 'warn', + 'no-eval': 'warn', + 'no-ex-assign': 'warn', + 'no-extend-native': 'warn', + 'no-extra-bind': 'warn', + 'no-extra-label': 'warn', + 'no-fallthrough': 'warn', + 'no-func-assign': 'warn', + 'no-implied-eval': 'warn', + 'no-invalid-regexp': 'warn', + 'no-iterator': 'warn', + 'no-label-var': 'warn', + 'no-labels': ['warn', { allowLoop: false, allowSwitch: false }], + 'no-lone-blocks': 'warn', + 'no-loop-func': 'warn', + 'no-mixed-operators': ['warn', { + groups: [ + ['&', '|', '^', '~', '<<', '>>', '>>>'], + ['==', '!=', '===', '!==', '>', '>=', '<', '<='], + ['&&', '||'], + ['in', 'instanceof'] + ], + allowSamePrecedence: false + }], + 'no-multi-str': 'warn', + 'no-native-reassign': 'warn', + 'no-negated-in-lhs': 'warn', + 'no-new-func': 'warn', + 'no-new-object': 'warn', + 'no-new-symbol': 'warn', + 'no-new-wrappers': 'warn', + 'no-obj-calls': 'warn', + 'no-octal': 'warn', + 'no-octal-escape': 'warn', + 'no-redeclare': 'warn', + 'no-regex-spaces': 'warn', + 'no-restricted-syntax': [ + 'warn', + 'LabeledStatement', + 'WithStatement', + ], + 'no-script-url': 'warn', + 'no-self-assign': 'warn', + 'no-self-compare': 'warn', + 'no-sequences': 'warn', + 'no-shadow-restricted-names': 'warn', + 'no-sparse-arrays': 'warn', + 'no-template-curly-in-string': 'warn', + 'no-this-before-super': 'warn', + 'no-throw-literal': 'warn', + 'no-undef': 'error', + 'no-unexpected-multiline': 'warn', + 'no-unreachable': 'warn', + 'no-unused-expressions': 'warn', + 'no-unused-labels': 'warn', + 'no-unused-vars': ['warn', { + vars: 'local', + varsIgnorePattern: '^_', + args: 'none' + }], + 'no-use-before-define': ['warn', 'nofunc'], + 'no-useless-computed-key': 'warn', + 'no-useless-concat': 'warn', + 'no-useless-constructor': 'warn', + 'no-useless-escape': 'warn', + 'no-useless-rename': ['warn', { + ignoreDestructuring: false, + ignoreImport: false, + ignoreExport: false, + }], + 'no-with': 'warn', + 'no-whitespace-before-property': 'warn', + 'operator-assignment': ['warn', 'always'], + radix: 'warn', + 'require-yield': 'warn', + 'rest-spread-spacing': ['warn', 'never'], + strict: ['warn', 'never'], + 'unicode-bom': ['warn', 'never'], + 'use-isnan': 'warn', + 'valid-typeof': 'warn', + + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/ + + // TODO: import rules are temporarily disabled because they don't play well + // with how eslint-loader only checks the file you change. So if module A + // imports module B, and B is missing a default export, the linter will + // record this as an issue in module A. Now if you fix module B, the linter + // will not be aware that it needs to re-lint A as well, so the error + // will stay until the next restart, which is really confusing. + + // This is probably fixable with a patch to eslint-loader. + // When file A is saved, we want to invalidate all files that import it + // *and* that currently have lint errors. This should fix the problem. + + // 'import/default': 'warn', + // 'import/export': 'warn', + // 'import/named': 'warn', + // 'import/namespace': 'warn', + // 'import/no-amd': 'warn', + // 'import/no-duplicates': 'warn', + // 'import/no-extraneous-dependencies': 'warn', + // 'import/no-named-as-default': 'warn', + // 'import/no-named-as-default-member': 'warn', + // 'import/no-unresolved': ['warn', { commonjs: true }], + + // https://github.com/yannickcr/eslint-plugin-react/tree/master/docs/rules + 'react/jsx-equals-spacing': ['warn', 'never'], + 'react/jsx-no-duplicate-props': ['warn', { ignoreCase: true }], + 'react/jsx-no-undef': 'warn', + 'react/jsx-pascal-case': ['warn', { + allowAllCaps: true, + ignore: [], + }], + 'react/jsx-uses-react': 'warn', + 'react/jsx-uses-vars': 'warn', + 'react/no-deprecated': 'warn', + 'react/no-direct-mutation-state': 'warn', + 'react/no-is-mounted': 'warn', + 'react/react-in-jsx-scope': 'warn', + 'react/require-render-return': 'warn', + + // https://github.com/evcohen/eslint-plugin-jsx-a11y/tree/master/docs/rules + 'jsx-a11y/aria-role': 'warn', + 'jsx-a11y/img-has-alt': 'warn', + 'jsx-a11y/img-redundant-alt': 'warn', + 'jsx-a11y/no-access-key': 'warn', + + // https://github.com/gajus/eslint-plugin-flowtype + 'flowtype/define-flow-type': 'warn', + 'flowtype/require-valid-file-annotation': 'warn', + 'flowtype/use-flow-type': 'warn' + } +}; diff --git a/config/flow/css.js.flow b/config/flow/css.js.flow new file mode 100644 index 0000000..46e7f7c --- /dev/null +++ b/config/flow/css.js.flow @@ -0,0 +1 @@ +// @flow diff --git a/config/flow/file.js.flow b/config/flow/file.js.flow new file mode 100644 index 0000000..701b670 --- /dev/null +++ b/config/flow/file.js.flow @@ -0,0 +1,2 @@ +// @flow +declare export default string; diff --git a/config/jest/CSSStub.js b/config/jest/CSSStub.js new file mode 100644 index 0000000..f053ebf --- /dev/null +++ b/config/jest/CSSStub.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/config/jest/FileStub.js b/config/jest/FileStub.js new file mode 100644 index 0000000..0a445d0 --- /dev/null +++ b/config/jest/FileStub.js @@ -0,0 +1 @@ +module.exports = "test-file-stub"; diff --git a/config/jest/transform.js b/config/jest/transform.js new file mode 100644 index 0000000..75f893c --- /dev/null +++ b/config/jest/transform.js @@ -0,0 +1,4 @@ +const babelDev = require('../babel.dev'); +const babelJest = require('babel-jest'); + +module.exports = babelJest.createTransformer(babelDev); diff --git a/config/paths.js b/config/paths.js new file mode 100644 index 0000000..4f721ef --- /dev/null +++ b/config/paths.js @@ -0,0 +1,38 @@ +var path = require('path'); +var fs = require('fs'); + +// Make sure any symlinks in the project folder are resolved: +// https://github.com/facebookincubator/create-react-app/issues/637 +var appDirectory = fs.realpathSync(process.cwd()); +function resolveApp(relativePath) { + return path.resolve(appDirectory, relativePath); +} + +// We support resolving modules according to `NODE_PATH`. +// This lets you use absolute paths in imports inside large monorepos: +// https://github.com/facebookincubator/create-react-app/issues/253. + +// It works similar to `NODE_PATH` in Node itself: +// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders + +// We will export `nodePaths` as an array of absolute paths. +// It will then be used by Webpack configs. +// Jest doesn’t need this because it already handles `NODE_PATH` out of the box. + +var nodePaths = (process.env.NODE_PATH || '') + .split(process.platform === 'win32' ? ';' : ':') + .filter(Boolean) + .map(resolveApp); + +// config after eject: we're in ./config/ +module.exports = { + appBuild: resolveApp('build'), + appHtml: resolveApp('index.html'), + appIndexJs: resolveApp('src/index.js'), + appPackageJson: resolveApp('package.json'), + appSrc: resolveApp('src'), + testsSetup: resolveApp('src/setupTests.js'), + appNodeModules: resolveApp('node_modules'), + ownNodeModules: resolveApp('node_modules'), + nodePaths: nodePaths +}; diff --git a/config/polyfills.js b/config/polyfills.js new file mode 100644 index 0000000..7e60150 --- /dev/null +++ b/config/polyfills.js @@ -0,0 +1,14 @@ +if (typeof Promise === 'undefined') { + // Rejection tracking prevents a common issue where React gets into an + // inconsistent state due to an error, but it gets swallowed by a Promise, + // and the user has no idea what causes React's erratic future behavior. + require('promise/lib/rejection-tracking').enable(); + window.Promise = require('promise/lib/es6-extensions.js'); +} + +// fetch() polyfill for making API calls. +require('whatwg-fetch'); + +// Object.assign() is commonly used with React. +// It will use the native implementation if it's present and isn't buggy. +Object.assign = require('object-assign'); diff --git a/config/webpack.config.dev.js b/config/webpack.config.dev.js new file mode 100644 index 0000000..87e5859 --- /dev/null +++ b/config/webpack.config.dev.js @@ -0,0 +1,214 @@ +var path = require('path'); +var autoprefixer = require('autoprefixer'); +var postcssFlexbugsFixes = require('postcss-flexbugs-fixes'); +var webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var ExtractTextPlugin = require("extract-text-webpack-plugin"); +var CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); +var WatchMissingNodeModulesPlugin = require('../scripts/utils/WatchMissingNodeModulesPlugin'); +var CopyWebpackPlugin = require('copy-webpack-plugin'); +var paths = require('./paths'); +var env = require('./env'); + +var contentful = require('contentful'); + +// This is the development configuration. +// It is focused on developer experience and fast rebuilds. +// The production configuration is different and lives in a separate file. +module.exports = { + // This makes the bundle appear split into separate modules in the devtools. + // We don't use source maps here because they can be confusing: + // https://github.com/facebookincubator/create-react-app/issues/343#issuecomment-237241875 + // You may want 'cheap-module-source-map' instead if you prefer source maps. + devtool: 'source-map', + // These are the "entry points" to our application. + // This means they will be the "root" imports that are included in JS bundle. + // The first two entry points enable "hot" CSS and auto-refreshes for JS. + entry: [ + // Include WebpackDevServer client. It connects to WebpackDevServer via + // sockets and waits for recompile notifications. When WebpackDevServer + // recompiles, it sends a message to the client by socket. If only CSS + // was changed, the app reload just the CSS. Otherwise, it will refresh. + // The "?/" bit at the end tells the client to look for the socket at + // the root path, i.e. /sockjs-node/. Otherwise visiting a client-side + // route like /todos/42 would make it wrongly request /todos/42/sockjs-node. + // The socket server is a part of WebpackDevServer which we are using. + // The /sockjs-node/ path I'm referring to is hardcoded in WebpackDevServer. + require.resolve('webpack-dev-server/client') + '?/', + // Include Webpack hot module replacement runtime. Webpack is pretty + // low-level so we need to put all the pieces together. The runtime listens + // to the events received by the client above, and applies updates (such as + // new CSS) to the running application. + require.resolve('webpack/hot/dev-server'), + // We ship a few polyfills by default. + require.resolve('./polyfills'), + // Finally, this is your app's code: + paths.appIndexJs + // We include the app code last so that if there is a runtime error during + // initialization, it doesn't blow up the WebpackDevServer client, and + // changing JS code would still trigger a refresh. + ], + output: { + // Next line is not used in dev but WebpackDevServer crashes without it: + path: paths.appBuild, + // Add /* filename */ comments to generated require()s in the output. + pathinfo: true, + // This does not produce a real file. It's just the virtual path that is + // served by WebpackDevServer in development. This is the JS bundle + // containing code from all our entry points, and the Webpack runtime. + filename: 'static/js/bundle.js', + // In development, we always serve from the root. This makes config easier. + publicPath: '/' + }, + resolve: { + // This allows you to set a fallback for where Webpack should look for modules. + // We read `NODE_PATH` environment variable in `paths.js` and pass paths here. + // We use `fallback` instead of `root` because we want `node_modules` to "win" + // if there any conflicts. This matches Node resolution mechanism. + // https://github.com/facebookincubator/create-react-app/issues/253 + fallback: paths.nodePaths, + // These are the reasonable defaults supported by the Node ecosystem. + // We also include JSX as a common component filename extension to support + // some tools, although we do not recommend using it, see: + // https://github.com/facebookincubator/create-react-app/issues/290 + extensions: ['.js', '.json', '.jsx', ''], + alias: { + // Support React Native Web + // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ + 'react-native': 'react-native-web' + } + }, + // Resolve loaders (webpack plugins for CSS, images, transpilation) from the + // directory of `react-scripts` itself rather than the project directory. + // You can remove this after ejecting. + module: { + // First, run the linter. + // It's important to do this before Babel processes the JS. + preLoaders: [ + { + test: /\.(js|jsx)$/, + loader: 'eslint', + include: paths.appSrc, + } + ], + loaders: [ + { + test: /\.scss$/, + include: paths.appSrc, + + // loader: 'style!css!postcss!sass' + loader: ExtractTextPlugin.extract('style', '!css!postcss!sass') + }, + // Process JS with Babel. + { + test: /\.(js|jsx)$/, + include: paths.appSrc, + loader: 'babel', + query: require('./babel.dev') + }, + + // JSON is not enabled by default in Webpack but both Node and Browserify + // allow it implicitly so we also enable it. + { + test: /\.json$/, + loader: 'json' + }, + // "file" loader makes sure those assets get served by WebpackDevServer. + // When you `import` an asset, you get its (virtual) filename. + // In production, they would get copied to the `build` folder. + { + test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/, + exclude: /\/favicon.ico$/, + loader: 'file', + query: { + name: 'static/media/[name].[hash:8].[ext]' + } + }, + // A special case for favicon.ico to place it into build root directory. + { + test: /\/favicon.ico$/, + include: [paths.appSrc], + loader: 'file', + query: { + name: 'favicon.ico?[hash:8]' + } + }, + // "url" loader works just like "file" loader but it also embeds + // assets smaller than specified size as data URLs to avoid requests. + { + test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/, + loader: 'url', + query: { + limit: 10000, + name: 'static/media/[name].[hash:8].[ext]' + } + }, + // "html" loader is used to process template page (index.html) to resolve + // resources linked with HTML tags. + { + test: /\.html$/, + loader: 'html', + query: { + attrs: ['link:href'], + } + } + ] + }, + // Point ESLint to our predefined config. + eslint: { + configFile: path.join(__dirname, 'eslint.js'), + useEslintrc: false + }, + // We use PostCSS for autoprefixing only. + postcss: function() { + return [ + autoprefixer({ + browsers: [ + '>1%', + 'last 4 versions', + 'Firefox ESR', + 'not ie < 9', // React doesn't support IE8 anyway + ] + }), + postcssFlexbugsFixes + ]; + }, + plugins: [ + new ExtractTextPlugin('app-[hash].css', { + allChunks: true + }), + + // Generates an `index.html` file with the