├── .browserslistrc ├── .editorconfig ├── .eslintrc ├── .gitignore ├── .prettierrc ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── client ├── .babelrc ├── .eslintrc ├── README.md ├── __setup__ │ ├── enzyme.js │ └── shim.js ├── components │ ├── __mocks__ │ │ ├── context-hoc.js │ │ └── styles.js │ ├── about │ │ ├── About.jsx │ │ ├── About.spec.js │ │ ├── __snapshots__ │ │ │ └── About.spec.js.snap │ │ ├── about.scss │ │ └── index.js │ ├── app │ │ ├── App.jsx │ │ ├── App.spec.js │ │ ├── __snapshots__ │ │ │ └── App.spec.js.snap │ │ ├── app.scss │ │ └── index.js │ ├── clickable │ │ ├── Clickable.jsx │ │ ├── Clickable.spec.js │ │ ├── __snapshots__ │ │ │ └── Clickable.spec.js.snap │ │ ├── clickable.scss │ │ └── index.js │ ├── counter │ │ ├── Counter.jsx │ │ ├── Counter.spec.js │ │ ├── __snapshots__ │ │ │ └── Counter.spec.js.snap │ │ ├── counter.scss │ │ └── index.js │ ├── nav │ │ ├── Nav.jsx │ │ ├── Nav.spec.js │ │ ├── __snapshots__ │ │ │ └── Nav.spec.js.snap │ │ ├── index.js │ │ └── nav.scss │ └── not-found │ │ ├── NotFound.jsx │ │ ├── NotFound.spec.js │ │ ├── __snapshots__ │ │ └── NotFound.spec.js.snap │ │ ├── index.js │ │ └── not-found.scss ├── config │ ├── webpack.config.common.js │ ├── webpack.config.dev.js │ ├── webpack.config.prod.js │ └── webpack.config.test.js ├── favicon.ico ├── index.jsx ├── index.scss ├── package.json ├── postcss.config.js ├── routes │ ├── Root.js │ └── links.js ├── styles │ ├── _mixins.scss │ └── _palette.scss ├── utils │ ├── __mocks__ │ │ └── fetch-response.mock.js │ ├── __tests__ │ │ ├── classes.spec.js │ │ ├── fetch.spec.js │ │ └── noop.spec.js │ ├── classes.js │ ├── fetch.js │ └── noop.js ├── webpack.config.js └── yarn.lock ├── docs ├── issue_template.md ├── pull_request_template.md ├── setup.md └── vscode.md ├── package.json ├── plop-templates └── component │ ├── {{ dashCase name }}.scss │ ├── {{ pascalCase name }}.jsx │ └── {{ pascalCase name }}.spec.js ├── plopfile.js ├── server ├── README.md ├── app.dev.js ├── app.prod.js ├── app.test.js ├── config │ └── index.js ├── datastore │ ├── connection │ │ └── knexfile.js │ ├── constants │ │ └── postgres-errors.js │ ├── create │ │ ├── knexfile.js │ │ └── seeds │ │ │ └── create.js │ ├── migrations │ │ ├── 20170518181737_create_sample_table.js │ │ └── 20170613154653_create_lookup_table.js │ ├── seeds │ │ └── populate_sample_table.js │ ├── server │ │ └── docker-compose.yml │ └── stores │ │ ├── base.store.js │ │ ├── lookup.store.js │ │ └── sample.store.js ├── exceptions │ ├── bad-request.exception.js │ ├── generic.exception.js │ ├── index.js │ ├── not-found.exception.js │ └── validation.exception.js ├── index.js ├── package.json ├── routes │ ├── index.js │ └── sample.route.js ├── services │ └── sample.service.js ├── tests │ ├── config.js │ └── sample.test.js ├── utils │ ├── app.configure.js │ └── logger.js └── yarn.lock └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | # Supported browsers 2 | last 1 versions 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = tab 7 | indent_size = 4 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | indent_style = space 15 | indent_size = 4 16 | 17 | [yarn.lock] 18 | end_of_line = crlf 19 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended" 4 | ], 5 | "parser": "babel-eslint", 6 | "parserOptions": { 7 | "ecmaVersion": 6, 8 | "sourceType": "module" 9 | }, 10 | "env": { 11 | "node": true, 12 | "es6": true 13 | }, 14 | "rules": { 15 | "no-console": [2, { 16 | "allow": ["error", "warn", "info"] 17 | }], 18 | 19 | // Possible Errors 20 | "comma-dangle": 1, // disallow trailing commas in object literals 21 | "no-cond-assign": 1, // disallow assignment in conditional expressions 22 | "no-constant-condition": 0, // disallow use of constant expressions in conditions 23 | "no-control-regex": 0, // disallow control characters in regular expressions 24 | "no-debugger": 1, // disallow use of debugger 25 | "no-dupe-keys": 1, // disallow duplicate keys when creating object literals 26 | "no-empty": 1, // disallow empty statements 27 | "no-empty-character-class": 1, // disallow the use of empty character classes in regular expressions 28 | "no-ex-assign": 0, // disallow assigning to the exception in a catch block 29 | "no-extra-boolean-cast": 0, // disallow double-negation boolean casts in a boolean context 30 | "no-extra-parens": 0, // disallow unnecessary parentheses (off by default) 31 | "no-extra-semi": 1, // disallow unnecessary semicolons 32 | "no-func-assign": 0, // disallow overwriting functions written as function declarations 33 | "no-inner-declarations": 0, // disallow function or variable declarations in nested blocks 34 | "no-invalid-regexp": 0, // disallow invalid regular expression strings in the RegExp constructor 35 | "no-irregular-whitespace": 1, // disallow irregular whitespace outside of strings and comments 36 | "no-negated-in-lhs": 0, // disallow negation of the left operand of an in expression 37 | "no-obj-calls": 0, // disallow the use of object properties of the global object (Math and JSON) as functions 38 | "no-regex-spaces": 0, // disallow multiple spaces in a regular expression literal 39 | "quote-props": 0, // disallow reserved words being used as object literal keys (off by default) 40 | "no-sparse-arrays": 0, // disallow sparse arrays 41 | "no-unreachable": 0, // disallow unreachable statements after a return, throw, continue, or break statement 42 | "use-isnan": 0, // disallow comparisons with the value NaN 43 | "valid-jsdoc": 0, // Ensure JSDoc comments are valid (off by default) 44 | "valid-typeof": 0, // Ensure that the results of typeof are compared against a valid string 45 | // Best Practices 46 | "block-scoped-var": 1, // treat var statements as if they were block scoped (off by default) 47 | "complexity": 0, // specify the maximum cyclomatic complexity allowed in a program (off by default) 48 | "consistent-return": 0, // require return statements to either always or never specify values 49 | "curly": 1, // specify curly brace conventions for all control statements 50 | "default-case": 1, // require default case in switch statements (off by default) 51 | "dot-notation": 0, // encourages use of dot notation whenever possible 52 | "eqeqeq": 1, // require the use of === and !== 53 | "guard-for-in": 0, // make sure for-in loops have an if statement (off by default) 54 | "no-alert": 0, // disallow the use of alert, confirm, and prompt 55 | "no-caller": 1, // disallow use of arguments.caller or arguments.callee 56 | "no-div-regex": 0, // disallow division operators explicitly at beginning of regular expression (off by default) 57 | "no-else-return": 0, // disallow else after a return in an if (off by default) 58 | "no-labels": 1, // disallow use of labels for anything other then loops and switches 59 | "no-eq-null": 1, // disallow comparisons to null without a type-checking operator (off by default) 60 | "no-eval": 1, // disallow use of eval() 61 | "no-extend-native": 0, // disallow adding to native types 62 | "no-extra-bind": 0, // disallow unnecessary function binding 63 | "no-fallthrough": 0, // disallow fallthrough of case statements 64 | "no-floating-decimal": 0, // disallow the use of leading or trailing decimal points in numeric literals (off by default) 65 | "no-implied-eval": 0, // disallow use of eval()-like methods 66 | "no-iterator": 0, // disallow usage of __iterator__ property 67 | "no-lone-blocks": 0, // disallow unnecessary nested blocks 68 | "no-loop-func": 1, // disallow creation of functions within loops 69 | "no-multi-spaces": 1, // disallow use of multiple spaces 70 | "no-multi-str": 1, // disallow use of multiline strings 71 | "no-native-reassign": 0, // disallow reassignments of native objects 72 | "no-new": 0, // disallow use of new operator when not part of the assignment or comparison 73 | "no-new-func": 0, // disallow use of new operator for Function object 74 | "no-new-wrappers": 0, // disallows creating new instances of String, Number, and Boolean 75 | "no-octal": 0, // disallow use of octal literals 76 | "no-octal-escape": 0, // disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251"; 77 | "no-process-env": 0, // disallow use of process.env (off by default) 78 | "no-proto": 0, // disallow usage of __proto__ property 79 | "no-redeclare": 1, // disallow declaring the same variable more then once 80 | "no-return-assign": 1, // disallow use of assignment in return statement 81 | "no-script-url": 0, // disallow use of javascript: urls. 82 | "no-self-compare": 1, // disallow comparisons where both sides are exactly the same (off by default) 83 | "no-sequences": 0, // disallow use of comma operator 84 | "no-unused-expressions": 0, // disallow usage of expressions in statement position 85 | "no-void": 0, // disallow use of void operator (off by default) 86 | "no-warning-comments": 0, // disallow usage of configurable warning terms in comments, e.g. TODO or FIXME (off by default) 87 | "no-with": 1, // disallow use of the with statement 88 | "radix": 0, // require use of the second argument for parseInt() (off by default) 89 | "vars-on-top": 0, // requires to declare all vars on top of their containing scope (off by default) 90 | "wrap-iife": 0, // require immediate function invocation to be wrapped in parentheses (off by default) 91 | "yoda": 1, // require or disallow Yoda conditions 92 | // Strict Mode 93 | "global-strict": 0, // (deprecated) require or disallow the "use strict" pragma in the global scope (off by default in the node environment) 94 | "no-extra-strict": 0, // (deprecated) disallow unnecessary use of "use strict"; when already in strict mode 95 | "strict": 0, // controls location of Use Strict Directives 96 | // Variables 97 | "no-catch-shadow": 0, // disallow the catch clause parameter name being the same as a variable in the outer scope (off by default in the node environment) 98 | "no-delete-var": 0, // disallow deletion of variables 99 | "no-label-var": 0, // disallow labels that share a name with a variable 100 | "no-shadow": 0, // disallow declaration of variables already declared in the outer scope 101 | "no-shadow-restricted-names": 0, // disallow shadowing of names such as arguments 102 | "no-undef": 0, // disallow use of undeclared variables unless mentioned in a /*global */ block 103 | "no-undef-init": 0, // disallow use of undefined when initializing variables 104 | "no-undefined": 0, // disallow use of undefined variable (off by default) 105 | "no-unused-vars": 2, // disallow declaration of variables that are not used in the code 106 | "no-use-before-define": 0, // disallow use of variables before they are defined 107 | // Stylistic Issues 108 | "brace-style": 1, // enforce one true brace style (off by default) 109 | "camelcase": 0, // require camel case names 110 | "comma-spacing": 0, // enforce spacing before and after comma 111 | "comma-style": 1, // enforce one true comma style (off by default) 112 | "consistent-this": 0, // enforces consistent naming when capturing the current execution context (off by default) 113 | "eol-last": 1, // enforce newline at the end of file, with no multiple empty lines 114 | "func-names": 0, // require function expressions to have a name (off by default) 115 | "func-style": 0, // enforces use of function declarations or expressions (off by default) 116 | "key-spacing": 0, // enforces spacing between keys and values in object literal properties 117 | "max-nested-callbacks": 0, // specify the maximum depth callbacks can be nested (off by default) 118 | "new-cap": 0, // require a capital letter for constructors 119 | "new-parens": 0, // disallow the omission of parentheses when invoking a constructor with no arguments 120 | "no-array-constructor": 0, // disallow use of the Array constructor 121 | "no-inline-comments": 0, // disallow comments inline after code (off by default) 122 | "no-lonely-if": 1, // disallow if as the only statement in an else block (off by default) 123 | "no-mixed-spaces-and-tabs": 1, // disallow mixed spaces and tabs for indentation 124 | "no-multiple-empty-lines": 1, // disallow multiple empty lines (off by default) 125 | "no-nested-ternary": 1, // disallow nested ternary expressions (off by default) 126 | "no-new-object": 0, // disallow use of the Object constructor 127 | "semi-spacing": 1, // disallow space before semicolon 128 | "no-spaced-func": 1, // disallow space between function identifier and application 129 | "no-ternary": 0, // disallow the use of ternary operators (off by default) 130 | "no-trailing-spaces": 1, // disallow trailing whitespace at the end of lines 131 | "no-underscore-dangle": 0, // disallow dangling underscores in identifiers 132 | "no-wrap-func": 0, // disallow wrapping of non-IIFE statements in parens 133 | "one-var": 0, // allow just one var statement per function (off by default) 134 | "operator-assignment": 0, // require assignment operator shorthand where possible or prohibit it entirely (off by default) 135 | "padded-blocks": 0, // enforce padding within blocks (off by default) 136 | "quotes": [2, "single", { 137 | "avoidEscape": true 138 | }], // specify whether double or single quotes should be used 139 | "semi": 0, // require or disallow use of semicolons instead of ASI 140 | "sort-vars": 0, // sort variables within the same declaration block (off by default) 141 | "space-after-function-name": 0, // require a space after function names (off by default) 142 | "space-after-keywords": 0, // require a space after certain keywords (off by default) 143 | "space-before-blocks": 2, // require or disallow space before blocks (off by default) 144 | "object-curly-spacing": 0, // require or disallow spaces inside brackets (off by default) 145 | "space-in-parens": 0, // require or disallow spaces inside parentheses (off by default) 146 | "space-infix-ops": 1, // require spaces around operators 147 | "space-return-throw-case": 0, // require a space after return, throw, and case 148 | "space-unary-ops": 0, // Require or disallow spaces before/after unary operators (words on by default, nonwords off by default) 149 | "spaced-comment": 1, // require or disallow a space immediately following the // in a line comment (off by default) 150 | "wrap-regex": 0, // require regex literals to be wrapped in parentheses (off by default) 151 | // ECMAScript 6 152 | "no-var": 0, // require let or const instead of var (off by default) 153 | "generator-star": 0, // enforce the position of the * in generator functions (off by default) 154 | // Legacy 155 | "max-depth": 0, // specify the maximum depth that blocks can be nested (off by default) 156 | "max-len": [1, 120, 4, { 157 | "ignoreUrls": true 158 | }], // specify the maximum length of a line in your program (off by default) 159 | "max-params": 0, // limits the number of parameters that can be used in the function declaration. (off by default) 160 | "max-statements": 0, // specify the maximum number of statement allowed in a function (off by default) 161 | "no-bitwise": 0, // disallow use of bitwise operators (off by default) 162 | "no-plusplus": 0 // disallow use of unary operators, ++ and -- (off by default) 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /build 6 | /tmp 7 | 8 | # dependencies 9 | /node_modules 10 | /client/node_modules 11 | /server/node_modules 12 | /bower_components 13 | 14 | # IDEs and editors 15 | /.idea 16 | /.vscode 17 | .project 18 | .classpath 19 | *.launch 20 | .settings/ 21 | 22 | # misc 23 | /.sass-cache 24 | /connect.lock 25 | /coverage/* 26 | client/coverage/* 27 | .coveralls.yml 28 | /libpeerconnection.log 29 | npm-debug.log 30 | yarn-error.log 31 | testem.log 32 | /typings 33 | stats.json 34 | 35 | # e2e 36 | /e2e/*.js 37 | /e2e/*.map 38 | 39 | # System Files 40 | .DS_Store 41 | Thumbs.db 42 | 43 | # cache 44 | .eslintcache 45 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "tabWidth": 4, 4 | "printWidth": 120, 5 | "jsxBracketSameLine": false, 6 | "semi": true, 7 | "singleQuote": true 8 | } 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: yarn 3 | node_js: 4 | - 8 5 | install: yarn 6 | script: yarn coveralls || yarn test:client 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing · `react-seed` 2 | 3 | First off, thank you for considering contributing to this *React Seed*. 4 | It’s people like you that helps to spread the knowledge and make this repo such a great tool... 5 | 6 | We are building it for both, people who want to learn more about these technologies and teams starting new projects. 7 | 8 | In order to achieve this goal, we are keeping the main branch (`master`) as basic as possible: 9 | * Adding only what we consider essential for starting projects and newcomers to the *React* ecosystem. 10 | * Leaving other features (more advanced ones) to the `seed/*` branches. (read README's [Seed Branches](./README.md#seed-branches)) 11 | 12 | What we try to do is to have a simple seed, to fit most of the starting projects. 13 | In our experience a lot of existing seeds have features we don’t need, and therefore we end up removing. 14 | 15 | All the contributors will be listed in the `README` to make public their help and express our gratitude. 16 | 17 | ## 1. Set up your environment 18 | 19 | Read our documentation on [docs/setup](docs/setup.md). 20 | Once you have the environment up and running, start playing around and build something new in top of it. 21 | 22 | ## 2. Create a Pull Request 23 | 24 | If you like to contribute, submit a new *PR*. We will get back to you as soon as possible. 25 | 26 | See README's [Coming up next](./README.md#coming-up-next) section to get some ideas/features we want to incorporate. 27 | 28 | You can also see our pull request template [here](docs/pull_request_template.md). 29 | 30 | ## 3. Report a Bug 31 | 32 | Let us know if you found a bug, have some new ideas to include, etc. 33 | 34 | See our issue template [here](docs/issue_template.md). 35 | 36 | ## Team 37 | 38 | We maintain this project in our free time. 39 | We enjoy to contribute to the community. 40 | 41 | If you liked the project, don't forget to star it! 42 | Any help from community is appreciated. 43 | 44 | Thanks! 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 UruIT 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-seed 2 | 3 | [](https://raw.githubusercontent.com/UruIT/react-seed/develop/LICENSE) 4 | [](https://travis-ci.org/UruIT/react-seed?branch=master) 5 | [](https://coveralls.io/github/UruIT/react-seed) 6 | [](https://github.com/UruIT/react-seed/releases) 7 | [](CONTRIBUTING.md#pull-requests) 8 | [](https://david-dm.org/uruit/react-seed?type=dev) 9 | 10 | [](https://twitter.com/UruIT/followers) 11 | [](https://twitter.com/intent/tweet?text=react-seed%20by%20%40UruIT%20on&url=https%3A%2F%2Fgithub.com%2Furuit%2Freact-seed&hashtags=react,uruit,dev,seed) 12 | 13 | 14 | UruIT seed project for ReactJS applications 15 | 16 | 17 | ## Stack 18 | 19 | * Webpack 20 | * ES6/7 21 | * ESLint 22 | * React 16 23 | * Fetch 24 | * React-Router 25 | * Jest 26 | * Sass 27 | * Express 28 | * Git hooks 29 | * Plop 30 | 31 | 32 | ## Secondary features 33 | 34 | * Redux 35 | * Redux-Segment 36 | * MongoDB 37 | * MSSQL 38 | * i18n 39 | * PDF 40 | 41 | 42 | ## Seed Branches 43 | 44 | ``` 45 | master * 46 | seed/ 47 | ./redux 48 | ./mongo 49 | ./mssql 50 | ./redux-i18n 51 | ./i18n 52 | ./offline 53 | ``` 54 | 55 | ### Coming up next 56 | 57 | * server side rendering 58 | * graphQL 59 | * storybook 60 | * bootstrap 61 | 62 | ## Structure 63 | 64 | ``` 65 | app 66 | ├── client 67 | │ ├── components 68 | │ │ └── home 69 | │ ├── config 70 | │ │ └── webpack 71 | │ ├── routes 72 | │ ├── styles 73 | │ └── utils 74 | ├── docs 75 | └── server 76 | ├── config 77 | ├── datastore 78 | ├── exceptions 79 | ├── routes 80 | ├── services 81 | ├── tests 82 | └── utils 83 | ``` 84 | 85 | ## Development 86 | 87 | Restore all packages and start development server: 88 | 89 | ```bash 90 | yarn 91 | yarn dev 92 | ``` 93 | 94 | Open browser on [localhost:4000](http://localhost:4000/) 95 | 96 | 97 | ## Docs 98 | 99 | * [Project setup info here](docs/setup.md) 100 | * [Client Readme](client/README.md) 101 | * [Server Readme](server/README.md) 102 | * [VS Code](docs/vscode.md) 103 | 104 | ## Contributors 105 | 106 | * [Arnold Gandarillas Castillo](https://github.com/arkgast) 107 | 108 | ## Authors 109 | 110 | [](https://github.com/carloluis) |[](https://github.com/matiasdelgado) |[](https://github.com/rrivem) |[](https://github.com/marina-acosta) | 111 | :---: |:---: |:---: |:---: | 112 | [carloluis](https://github.com/carloluis) |[matiasdelgado](https://github.com/matiasdelgado) |[rrivem](https://github.com/rrivem) |[marina-acosta](https://github.com/marina-acosta) 113 | 114 | ## License 115 | 116 | Licensed under the MIT License, Copyright © 2017 [UruIT](https://twitter.com/UruIT). 117 | 118 | See [LICENSE](./LICENSE) for more information. 119 | -------------------------------------------------------------------------------- /client/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "react", 5 | "survivejs-kanban" 6 | ], 7 | "env": { 8 | "start": { 9 | "presets": [ 10 | "react-hmre" 11 | ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", "plugin:react/recommended" 4 | ], 5 | "parser": "babel-eslint", 6 | "parserOptions": { 7 | "ecmaFeatures": { 8 | "jsx": true 9 | } 10 | }, 11 | "env": { 12 | "browser": true, 13 | "jest": true 14 | }, 15 | "plugins": [ 16 | "react" 17 | ], 18 | "rules": { 19 | "jsx-quotes": [1, "prefer-double"], 20 | 21 | "react/jsx-no-undef": 1, 22 | "react/jsx-uses-react": 1, 23 | "react/jsx-uses-vars": 1 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # React Seed Client 2 | 3 | 4 | ## Dependencies 5 | 6 | Add new packages to the `client` project: 7 | 8 | ```bash 9 | yarn add 10 | ``` 11 | -------------------------------------------------------------------------------- /client/__setup__/enzyme.js: -------------------------------------------------------------------------------- 1 | import './shim'; 2 | import Enzyme from 'enzyme'; 3 | import Adapter from 'enzyme-adapter-react-16'; 4 | 5 | Enzyme.configure({ adapter: new Adapter() }); 6 | -------------------------------------------------------------------------------- /client/__setup__/shim.js: -------------------------------------------------------------------------------- 1 | const raf = (global.requestAnimationFrame = cb => setTimeout(cb, 0)); 2 | 3 | export default raf; 4 | -------------------------------------------------------------------------------- /client/components/__mocks__/context-hoc.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const withContext = (context = {}) => Wrapped => { 5 | class Wrapper extends Component { 6 | constructor(props) { 7 | super(props); 8 | } 9 | getChildContext() { 10 | return context; 11 | } 12 | render() { 13 | return ; 14 | } 15 | } 16 | 17 | let contextProps = Object.keys(context); 18 | Wrapper.childContextTypes = Object.assign( 19 | ...contextProps.map(prop => ({ [prop]: PropTypes.any })) 20 | ); 21 | 22 | return Wrapper; 23 | }; 24 | 25 | export default withContext; 26 | -------------------------------------------------------------------------------- /client/components/__mocks__/styles.js: -------------------------------------------------------------------------------- 1 | const identityObject = new Proxy({}, { 2 | get: (target, prop) => prop 3 | }); 4 | 5 | export default identityObject; 6 | -------------------------------------------------------------------------------- /client/components/about/About.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Counter from '../counter'; 3 | import styles from './about.scss'; 4 | 5 | const About = () => ( 6 | 7 | React Seed 8 | Simple project to start with React! 9 | 10 | 11 | ); 12 | 13 | export default About; 14 | -------------------------------------------------------------------------------- /client/components/about/About.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ShallowRenderer from 'react-test-renderer/shallow'; 3 | import About from './About'; 4 | 5 | describe('', () => { 6 | it('snapshot', () => { 7 | const renderer = new ShallowRenderer(); 8 | const tree = renderer.render(); 9 | expect(tree).toMatchSnapshot(); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /client/components/about/__snapshots__/About.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` snapshot 1`] = ` 4 | 7 | 8 | React Seed 9 | 10 | 11 | Simple project to start with React! 12 | 13 | 14 | 15 | `; 16 | -------------------------------------------------------------------------------- /client/components/about/about.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/_palette'; 2 | 3 | .about { 4 | text-align: center; 5 | 6 | h1, h2 { 7 | color: $black-2; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /client/components/about/index.js: -------------------------------------------------------------------------------- 1 | import About from './About'; 2 | 3 | export default About; 4 | -------------------------------------------------------------------------------- /client/components/app/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { getJson } from 'utils/fetch'; 3 | import links from '../../routes/links'; 4 | import Clickable from '../clickable'; 5 | import styles from './app.scss'; 6 | import classes from 'utils/classes'; 7 | 8 | class App extends React.Component { 9 | constructor(props) { 10 | super(props); 11 | 12 | this.handleClick = this.handleClick.bind(this); 13 | this.state = { 14 | jokes: [], 15 | sample: [], 16 | error: '' 17 | }; 18 | } 19 | 20 | componentDidMount() { 21 | getJson(links.api.sample) 22 | .then(sample => this.setState({ sample })) 23 | .catch(error => this.setState({ error })); 24 | } 25 | 26 | handleClick() { 27 | return getJson(links.chucknorris).then(response => 28 | this.setState({ 29 | jokes: [response.value, ...this.state.jokes] 30 | }) 31 | ); 32 | } 33 | 34 | render() { 35 | const { jokes, sample, error } = this.state; 36 | const jsxJokes = jokes.map((joke, idx) => ); 37 | 38 | return ( 39 | 40 | 41 | + 42 | 43 | {jsxJokes} 44 | {this.renderApiTest(sample, error)} 45 | 46 | ); 47 | } 48 | 49 | renderApiTest(sample, error) { 50 | const response = JSON.stringify(sample.length ? sample : error); 51 | 52 | return ( 53 | 56 | ~{links.api.sample} response: 57 | {response} 58 | 59 | } 60 | /> 61 | ); 62 | } 63 | } 64 | 65 | export default App; 66 | -------------------------------------------------------------------------------- /client/components/app/App.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ShallowRenderer from 'react-test-renderer/shallow'; 3 | import { shallow, mount } from 'enzyme'; 4 | 5 | import { getJson } from 'utils/fetch'; 6 | import Clickable from '../clickable'; 7 | import links from '../../routes/links'; 8 | import App from './App'; 9 | 10 | jest.mock('utils/fetch', () => ({ 11 | getJson: jest.fn(url => new Promise((resolve, reject) => (url ? resolve({ value: 'another joke' }) : reject()))) 12 | })); 13 | 14 | describe('', () => { 15 | it('snapshot', () => { 16 | const renderer = new ShallowRenderer(); 17 | const tree = renderer.render(); 18 | expect(tree).toMatchSnapshot(); 19 | }); 20 | }); 21 | 22 | describe('App - button onClick', () => { 23 | const app = shallow(); 24 | 25 | beforeEach(() => { 26 | getJson.mockImplementation(() => Promise.resolve({ value: 'another joke' })); 27 | app.find('button').simulate('click'); 28 | }); 29 | 30 | it('should call ChuckNorris api', () => { 31 | expect(getJson).toBeCalledWith(links.chucknorris); 32 | }); 33 | 34 | it('should keep previous jokes', () => { 35 | expect(app.state().jokes.length).toBe(2); 36 | }); 37 | 38 | it('should update children', () => { 39 | return app 40 | .instance() 41 | .handleClick() 42 | .then(() => 43 | expect( 44 | app 45 | .find(Clickable) 46 | .first() 47 | .props().content 48 | ).toEqual('another joke') 49 | ); 50 | }); 51 | }); 52 | 53 | describe('App - mounting', () => { 54 | describe('server request succeeded', () => { 55 | const sample = [{ id: 1 }, { id: 2 }]; 56 | getJson.mockImplementation(() => Promise.resolve(sample)); 57 | const tree = mount(); 58 | 59 | it('should call getJson', () => { 60 | expect(getJson).toBeCalledWith(links.api.sample); 61 | }); 62 | 63 | it('should update state.sample', () => { 64 | expect(tree.instance().state).toMatchObject({ sample }); 65 | }); 66 | }); 67 | 68 | describe('server request error', () => { 69 | getJson.mockImplementation(() => Promise.reject({ code: 500 })); 70 | const app = mount(); 71 | 72 | it('should update state.error', () => { 73 | expect(app.instance().state).toMatchObject({ error: { code: 500 } }); 74 | }); 75 | 76 | it('should not have any joke', () => { 77 | expect(app.state().jokes).toEqual([]); 78 | }); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /client/components/app/__snapshots__/App.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` snapshot 1`] = ` 4 | 7 | 11 | + 12 | 13 | 16 | 21 | 22 | ~ 23 | /api/sample 24 | response: 25 | 26 | 27 | "" 28 | 29 | 30 | } 31 | onClick={[Function]} 32 | /> 33 | 34 | `; 35 | -------------------------------------------------------------------------------- /client/components/app/app.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/_mixins'; 2 | @import '~styles/_palette'; 3 | 4 | $narrow-screen: 420px; 5 | 6 | .shadow { 7 | @include box-shadow(1px, 1px, 4px, $slate); 8 | } 9 | 10 | .container { 11 | max-width: 400px; 12 | margin: 24px auto 8px; 13 | padding: 8px; 14 | text-align: center; 15 | 16 | .button { 17 | border: 2px solid $black; 18 | background-color: $gold; 19 | cursor: pointer; 20 | font-weight: 500; 21 | font-size: 2rem; 22 | width: 100%; 23 | 24 | &:focus { 25 | outline: 0; 26 | } 27 | } 28 | 29 | .jokes { 30 | margin: 16px 0; 31 | } 32 | } 33 | 34 | .api-response { 35 | padding: 4px; 36 | 37 | span { 38 | display: inline-block; 39 | text-decoration: underline; 40 | margin-bottom: 4px; 41 | } 42 | 43 | div { 44 | overflow: auto; 45 | } 46 | } 47 | 48 | @media screen and (min-width: $narrow-screen) { 49 | .container { 50 | padding: 4px; 51 | max-width: $narrow-screen; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /client/components/app/index.js: -------------------------------------------------------------------------------- 1 | import App from './App'; 2 | 3 | export default App; 4 | -------------------------------------------------------------------------------- /client/components/clickable/Clickable.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import noop from 'utils/noop'; 4 | import styles from './clickable.scss'; 5 | 6 | const Clickable = ({ content, onClick }) => ( 7 | 8 | {content} 9 | 10 | ); 11 | 12 | Clickable.propTypes = { 13 | content: PropTypes.node, 14 | onClick: PropTypes.func 15 | }; 16 | 17 | Clickable.defaultProps = { 18 | content: '...', 19 | onClick: noop 20 | }; 21 | 22 | export default Clickable; 23 | -------------------------------------------------------------------------------- /client/components/clickable/Clickable.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | import { shallow } from 'enzyme'; 4 | 5 | import Clickable from './Clickable'; 6 | 7 | describe('', () => { 8 | it('snapshot - default props', () => { 9 | const tree = renderer.create().toJSON(); 10 | expect(tree).toMatchSnapshot(); 11 | }); 12 | 13 | it('snapshot - with TEXT', () => { 14 | const tree = renderer.create().toJSON(); 15 | expect(tree).toMatchSnapshot(); 16 | }); 17 | }); 18 | 19 | describe('Clickable - on Click', () => { 20 | it('should call onClick callback', () => { 21 | const onClickCb = jest.fn(); 22 | 23 | const app = shallow(); 24 | 25 | app.simulate('click'); 26 | 27 | expect(onClickCb).toBeCalled(); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /client/components/clickable/__snapshots__/Clickable.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` snapshot - default props 1`] = ` 4 | 8 | ... 9 | 10 | `; 11 | 12 | exports[` snapshot - with TEXT 1`] = ` 13 | 17 | TEXT 18 | 19 | `; 20 | -------------------------------------------------------------------------------- /client/components/clickable/clickable.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/_palette'; 2 | 3 | .container { 4 | padding: 0.2rem; 5 | border: 0.1px solid $black; 6 | background: $gold; 7 | color: $black; 8 | cursor: pointer; 9 | transition: all .4s cubic-bezier(0.36, -1, 1, 2.65); 10 | 11 | &:hover { 12 | transform: scale(1.05, 1.15); 13 | font-weight: 600; 14 | background: $black; 15 | color: $gold; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/components/clickable/index.js: -------------------------------------------------------------------------------- 1 | import Clickable from './Clickable'; 2 | 3 | export default Clickable; 4 | -------------------------------------------------------------------------------- /client/components/counter/Counter.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import styles from './counter.scss'; 3 | 4 | const leap = step => ({ value }) => ({ value: value + step }); 5 | 6 | class Counter extends Component { 7 | constructor(props) { 8 | super(props); 9 | 10 | this.handleIncrement = this.handleIncrement.bind(this); 11 | this.handleDecrement = this.handleDecrement.bind(this); 12 | this.state = { value: 0 }; 13 | } 14 | 15 | handleIncrement() { 16 | this.setState(leap(1)); 17 | } 18 | 19 | handleDecrement() { 20 | this.setState(leap(-1)); 21 | } 22 | 23 | render() { 24 | return ( 25 | 26 | {this.state.value} 27 | 28 | − 29 | + 30 | 31 | 32 | ); 33 | } 34 | } 35 | 36 | export default Counter; 37 | -------------------------------------------------------------------------------- /client/components/counter/Counter.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ShallowRenderer from 'react-test-renderer/shallow'; 3 | import { shallow } from 'enzyme'; 4 | 5 | import Counter from './Counter'; 6 | 7 | describe('', () => { 8 | it('snapshot', () => { 9 | const renderer = new ShallowRenderer(); 10 | const tree = renderer.render(); 11 | 12 | expect(tree).toMatchSnapshot(); 13 | }); 14 | }); 15 | 16 | describe('Counter', () => { 17 | let tree; 18 | beforeEach(() => { 19 | tree = shallow(); 20 | }); 21 | 22 | it('should start with value in zero', () => { 23 | expect(tree.state()).toEqual({ value: 0 }); 24 | }); 25 | 26 | it('should decrement value', () => { 27 | const minus = tree.find('a').first(); 28 | minus.simulate('click'); 29 | 30 | expect(tree.state()).toEqual({ value: -1 }); 31 | }); 32 | 33 | it('should increment value', () => { 34 | const plus = tree.find('a').last(); 35 | plus.simulate('click'); 36 | 37 | expect(tree.state()).toEqual({ value: 1 }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /client/components/counter/__snapshots__/Counter.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` snapshot 1`] = ` 4 | 7 | 10 | 0 11 | 12 | 15 | 18 | − 19 | 20 | 23 | + 24 | 25 | 26 | 27 | `; 28 | -------------------------------------------------------------------------------- /client/components/counter/counter.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/_mixins'; 2 | @import '~styles/_palette'; 3 | 4 | .container { 5 | background: $gold; 6 | border: 2px solid $black-2; 7 | @include box-shadow(1px, 1px, 4px,$slate); 8 | width: 240px; 9 | margin: 8px auto; 10 | position: relative; 11 | 12 | .value { 13 | cursor: default; 14 | font-size: 8rem; 15 | text-align: center; 16 | user-select: none; 17 | overflow: auto; 18 | } 19 | 20 | .controls { 21 | display: flex; 22 | justify-content: space-around; 23 | padding: 8px 24px; 24 | 25 | a { 26 | user-select: none; 27 | font-weight: bold; 28 | font-size: 3rem; 29 | cursor: pointer; 30 | @include box-shadow(1px, 1px, 4px,$slate); 31 | border-radius: 4px; 32 | width: 56px; 33 | background: $black-2; 34 | color: $gold; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /client/components/counter/index.js: -------------------------------------------------------------------------------- 1 | import Counter from './Counter'; 2 | 3 | export default Counter; 4 | -------------------------------------------------------------------------------- /client/components/nav/Nav.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink } from 'react-router-dom'; 3 | import links from '../../routes/links'; 4 | import styles from './nav.scss'; 5 | 6 | const Nav = () => ( 7 | 8 | 9 | 10 | Home 11 | 12 | 13 | 14 | 15 | About 16 | 17 | 18 | 19 | ); 20 | 21 | export default Nav; 22 | -------------------------------------------------------------------------------- /client/components/nav/Nav.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ShallowRenderer from 'react-test-renderer/shallow'; 3 | 4 | import Nav from './Nav'; 5 | 6 | describe('', () => { 7 | it('snapshot', () => { 8 | const renderer = new ShallowRenderer(); 9 | const tree = renderer.render(); 10 | 11 | expect(tree).toMatchSnapshot(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /client/components/nav/__snapshots__/Nav.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` snapshot 1`] = ` 4 | 7 | 8 | 15 | Home 16 | 17 | 18 | 19 | 26 | About 27 | 28 | 29 | 30 | `; 31 | -------------------------------------------------------------------------------- /client/components/nav/index.js: -------------------------------------------------------------------------------- 1 | import Nav from './Nav'; 2 | 3 | export default Nav; 4 | -------------------------------------------------------------------------------- /client/components/nav/nav.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/_palette'; 2 | @import '~styles/_mixins'; 3 | 4 | ul.nav { 5 | width: 150px; 6 | margin: 0 auto; 7 | border-bottom: 1px dashed $black-2; 8 | border-width: 1px 0; 9 | list-style: none; 10 | text-align: center; 11 | padding: 0; 12 | 13 | li { 14 | display: inline-block; 15 | 16 | &:not(:last-child)::after { 17 | content: '|'; 18 | color: $black-2; 19 | } 20 | } 21 | 22 | .link { 23 | display: inline-block; 24 | text-decoration: none; 25 | padding: 8px; 26 | margin: 0 8px; 27 | @include blurry(); 28 | } 29 | 30 | .active { 31 | font-weight: 600; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /client/components/not-found/NotFound.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import links from '../../routes/links'; 4 | import styles from './not-found.scss'; 5 | 6 | const NotFound = () => ( 7 | 8 | 404 9 | Page Not Found 10 | 11 | Go Home! 12 | 13 | 14 | ); 15 | 16 | export default NotFound; 17 | -------------------------------------------------------------------------------- /client/components/not-found/NotFound.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ShallowRenderer from 'react-test-renderer/shallow'; 3 | import NotFound from './NotFound'; 4 | 5 | describe('', () => { 6 | it('snapshot', () => { 7 | const renderer = new ShallowRenderer(); 8 | const tree = renderer.render(); 9 | expect(tree).toMatchSnapshot(); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /client/components/not-found/__snapshots__/NotFound.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` snapshot 1`] = ` 4 | 7 | 8 | 404 9 | 10 | 11 | Page Not Found 12 | 13 | 18 | Go Home! 19 | 20 | 21 | `; 22 | -------------------------------------------------------------------------------- /client/components/not-found/index.js: -------------------------------------------------------------------------------- 1 | import NotFound from './NotFound'; 2 | 3 | export default NotFound; 4 | -------------------------------------------------------------------------------- /client/components/not-found/not-found.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/_palette'; 2 | @import '~styles/_mixins'; 3 | 4 | .container { 5 | text-align: center; 6 | 7 | h1, h2 { 8 | color: $black-2; 9 | } 10 | 11 | .link { 12 | text-decoration: none; 13 | @include blurry(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/config/webpack.config.common.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | const webpack = require('webpack'); 3 | const glob = require('glob'); 4 | const path = require('path'); 5 | 6 | const TARGET = process.env.npm_lifecycle_event; 7 | const PATHS = { 8 | root: path.join(__dirname, '..', '..'), 9 | app: path.join(__dirname, '..'), 10 | dist: path.join(__dirname, '../../dist'), 11 | style: glob.sync('./**/*.scss'), 12 | test: glob.sync('./**/*.spec.js') 13 | }; 14 | 15 | process.env.BABEL_ENV = TARGET; 16 | 17 | const common = { 18 | context: PATHS.app, 19 | entry: { 20 | app: [PATHS.app] 21 | }, 22 | resolve: { 23 | extensions: ['.js', '.jsx'], 24 | alias: { 25 | styles: path.resolve(__dirname, '../styles'), 26 | utils: path.resolve(__dirname, '../utils') 27 | } 28 | }, 29 | output: { 30 | path: PATHS.dist, 31 | filename: '[name].js', 32 | publicPath: '/' 33 | }, 34 | module: { 35 | rules: [ 36 | { 37 | test: /\.html$/, 38 | use: 'raw-loader', 39 | exclude: ['../index.html'] 40 | }, 41 | { 42 | test: /\.(jpg|png|gif|svg)$/, 43 | use: 'file-loader' 44 | }, 45 | { 46 | test: /\.jsx?$/, 47 | use: ['babel-loader?cacheDirectory'], 48 | exclude: /node_modules/, 49 | include: PATHS.app 50 | } 51 | ] 52 | }, 53 | plugins: [ 54 | new HtmlWebpackPlugin({ 55 | template: '../node_modules/html-webpack-template/index.ejs', 56 | title: 'UruIT React Seed', 57 | favicon: 'favicon.ico', 58 | appMountId: 'app', 59 | inject: false, 60 | minify: { 61 | collapseWhitespace: true, 62 | conservativeCollapse: true, 63 | preserveLineBreaks: true, 64 | useShortDoctype: true, 65 | html5: true 66 | }, 67 | mobile: true 68 | }), 69 | new webpack.optimize.ModuleConcatenationPlugin() 70 | ], 71 | stats: { 72 | children: false 73 | } 74 | }; 75 | 76 | module.exports = { 77 | common, 78 | PATHS 79 | }; 80 | -------------------------------------------------------------------------------- /client/config/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | const { common, PATHS } = require('./webpack.config.common'); 2 | const merge = require('webpack-merge'); 3 | const webpack = require('webpack'); 4 | 5 | module.exports = merge(common, { 6 | mode: 'development', 7 | devtool: 'eval', 8 | module: { 9 | rules: [ 10 | { 11 | test: /\.jsx?$/, 12 | use: ['eslint-loader'], 13 | include: PATHS.app, 14 | exclude: /node_modules/, 15 | enforce: 'pre' 16 | }, 17 | { 18 | test: /\.scss$/, 19 | use: [ 20 | { 21 | loader: 'style-loader' 22 | }, 23 | { 24 | loader: 'css-loader', 25 | options: { 26 | modules: true, 27 | camelCase: 'dashes', 28 | localIdentName: '[path][name]__[local]' 29 | } 30 | }, 31 | { 32 | loader: 'postcss-loader' 33 | }, 34 | { 35 | loader: 'resolve-url-loader' 36 | }, 37 | { 38 | loader: 'sass-loader' 39 | } 40 | ] 41 | } 42 | ] 43 | }, 44 | plugins: [ 45 | new webpack.HotModuleReplacementPlugin(), 46 | new webpack.NamedModulesPlugin() 47 | ] 48 | }); 49 | -------------------------------------------------------------------------------- /client/config/webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | const CleanPlugin = require('clean-webpack-plugin'); 2 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 4 | const merge = require('webpack-merge'); 5 | const path = require('path'); 6 | 7 | const { common, PATHS } = require('./webpack.config.common'); 8 | 9 | module.exports = merge(common, { 10 | mode: 'production', 11 | output: { 12 | chunkFilename: '[chunkhash].js', 13 | filename: '[name].[chunkhash].js' 14 | }, 15 | optimization: { 16 | runtimeChunk: 'single', 17 | splitChunks: { 18 | chunks: 'all' 19 | } 20 | }, 21 | devtool: 'hidden-source-map', 22 | module: { 23 | rules: [ 24 | { 25 | test: /\.jsx?$/, 26 | use: ['eslint-loader'], 27 | include: PATHS.app, 28 | enforce: 'pre' 29 | }, 30 | { 31 | test: /\.scss$/, 32 | use: [ 33 | MiniCssExtractPlugin.loader, 34 | { 35 | loader: 'css-loader', 36 | options: { 37 | modules: true, 38 | camelCase: 'dashes', 39 | minimize: true 40 | } 41 | }, 42 | { 43 | loader: 'postcss-loader' 44 | }, 45 | { 46 | loader: 'resolve-url-loader' 47 | }, 48 | { 49 | loader: 'sass-loader' 50 | } 51 | ] 52 | } 53 | ] 54 | }, 55 | plugins: [ 56 | new CleanPlugin([PATHS.dist], { 57 | root: PATHS.root, 58 | verbose: false 59 | }), 60 | new MiniCssExtractPlugin('[name].[chunkhash].css'), 61 | new CopyWebpackPlugin([ 62 | { 63 | from: path.join(PATHS.app, 'favicon.ico'), 64 | to: path.join(PATHS.dist, 'favicon.ico') 65 | } 66 | ]) 67 | ] 68 | }); 69 | -------------------------------------------------------------------------------- /client/config/webpack.config.test.js: -------------------------------------------------------------------------------- 1 | const { common, PATHS } = require('./webpack.config.common'); 2 | const merge = require('webpack-merge'); 3 | 4 | module.exports = merge(common, { 5 | entry: null, 6 | devtool: 'inline-source-map', 7 | resolve: { 8 | alias: { 9 | app: PATHS.app 10 | } 11 | }, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.jsx?$/, 16 | use: ['istanbul-instrumenter-loader'], 17 | include: PATHS.app, 18 | enforce: 'pre' 19 | }, 20 | { 21 | test: /\.jsx?$/, 22 | use: ['babel-loader?cacheDirectory'], 23 | include: PATHS.app 24 | } 25 | ] 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /client/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UruIT/react-seed/09db5e6f020f72f2cc74facb8af0821e65da6a83/client/favicon.ico -------------------------------------------------------------------------------- /client/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import './index.scss'; 4 | 5 | import Root from './routes/Root'; 6 | 7 | render(, document.getElementById('app')); 8 | -------------------------------------------------------------------------------- /client/index.scss: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-seed-client", 3 | "version": "1.0.0", 4 | "description": "react seed - client", 5 | "main": "index.js", 6 | "repository": "https://github.com/UruIT/react-seed.git", 7 | "author": "UruIT (https://github.com/UruIT)", 8 | "license": "MIT", 9 | "scripts": { 10 | "lint": "eslint . --ext .js,.jsx --ignore-path .gitignore --cache", 11 | "stats": "webpack --profile --json > stats.json", 12 | "test": "jest", 13 | "tdd": "jest --watchAll", 14 | "coverage": "jest --coverage", 15 | "build": "webpack" 16 | }, 17 | "dependencies": { 18 | "es6-promise": "^4.1.1", 19 | "prop-types": "^15.5.9", 20 | "react": "^16.1.1", 21 | "react-dom": "^16.1.1", 22 | "react-router-dom": "^4.2.0", 23 | "whatwg-fetch": "^2.0.3" 24 | }, 25 | "devDependencies": { 26 | "babel-preset-react-hmre": "^1.1.1", 27 | "enzyme": "^3.2.0", 28 | "enzyme-adapter-react-16": "^1.1.0", 29 | "jest": "^21.2.1", 30 | "react-test-renderer": "^16.1.1" 31 | }, 32 | "jest": { 33 | "moduleNameMapper": { 34 | "\\.(scss)$": "/components/__mocks__/styles.js", 35 | "^utils(.*)$": "/utils$1" 36 | }, 37 | "testPathIgnorePatterns": [ 38 | "/node_modules/", 39 | "webpack" 40 | ], 41 | "setupFiles": [ 42 | "/__setup__/shim.js", 43 | "/__setup__/enzyme.js" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /client/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [require('autoprefixer')] 3 | }; 4 | -------------------------------------------------------------------------------- /client/routes/Root.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { BrowserRouter, Switch, Route } from 'react-router-dom'; 3 | 4 | import App from '../components/app'; 5 | import About from '../components/about'; 6 | import NotFound from '../components/not-found'; 7 | import Nav from '../components/nav/Nav'; 8 | 9 | import links from './links'; 10 | 11 | const Root = () => ( 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | ); 23 | 24 | export default Root; 25 | -------------------------------------------------------------------------------- /client/routes/links.js: -------------------------------------------------------------------------------- 1 | const index = '/'; 2 | 3 | const about = '/about'; 4 | 5 | const chucknorris = 'https://api.chucknorris.io/jokes/random'; 6 | 7 | const api = { 8 | sample: '/api/sample' 9 | }; 10 | 11 | export default { 12 | index, 13 | about, 14 | chucknorris, 15 | api 16 | }; 17 | -------------------------------------------------------------------------------- /client/styles/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin box-shadow($top, $left, $blur, $color, $inset: false) { 2 | @if $inset { 3 | -webkit-box-shadow:inset $top $left $blur $color; 4 | -moz-box-shadow:inset $top $left $blur $color; 5 | box-shadow:inset $top $left $blur $color; 6 | } @else { 7 | -webkit-box-shadow: $top $left $blur $color; 8 | -moz-box-shadow: $top $left $blur $color; 9 | box-shadow: $top $left $blur $color; 10 | } 11 | } 12 | 13 | @mixin blurry { 14 | color: $black-2; 15 | text-shadow: 0 0 4px $black-05; 16 | } 17 | -------------------------------------------------------------------------------- /client/styles/_palette.scss: -------------------------------------------------------------------------------- 1 | $black: #000; 2 | $black-2: #222; 3 | $black-05: rgba(0,0,0,0.5); 4 | $gold: #fbfb09; 5 | $slate: #415361; 6 | $white: #fff; 7 | -------------------------------------------------------------------------------- /client/utils/__mocks__/fetch-response.mock.js: -------------------------------------------------------------------------------- 1 | function mockResponse(status, statusText, response = '{}') { 2 | return new Response(response, { 3 | status, 4 | statusText, 5 | headers: { 6 | 'Content-Type': 'application/json' 7 | } 8 | }); 9 | } 10 | 11 | export default mockResponse; 12 | -------------------------------------------------------------------------------- /client/utils/__tests__/classes.spec.js: -------------------------------------------------------------------------------- 1 | import classes from '../classes'; 2 | 3 | describe('classes', () => { 4 | it('return all classnames separated by spaces', () => { 5 | const result = classes('classname1', 'classname2', 'classname3', 'classname4'); 6 | 7 | expect(result).toBe('classname1 classname2 classname3 classname4'); 8 | }); 9 | 10 | it('return only truthy classnames', () => { 11 | const result = classes('classname1', '', 1, ''); 12 | 13 | expect(result).toBe('classname1 1'); 14 | }); 15 | 16 | it('return truthy expressions', () => { 17 | const condition = b => !!b; 18 | 19 | const result = classes('classnameN', condition(0) && 'classnameO', condition(1) && 'classnameP'); 20 | 21 | expect(result).toBe('classnameN classnameP'); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /client/utils/__tests__/fetch.spec.js: -------------------------------------------------------------------------------- 1 | import mockResponse from '../__mocks__/fetch-response.mock'; 2 | import { getJson } from '../fetch'; 3 | 4 | const _fetch = window.fetch; 5 | 6 | describe('fetch', () => { 7 | it('should call then part when response was successful', () => { 8 | const RESPONSE = { joke: 'kcuhC' }; 9 | window.fetch = jest.fn(() => Promise.resolve(mockResponse(200, null, JSON.stringify(RESPONSE)))); 10 | 11 | return getJson('//url').then(response => { 12 | expect(response).toEqual(RESPONSE); 13 | }); 14 | }); 15 | 16 | it('should call catch part when response was not successful', () => { 17 | const RESPONSE = { status: 404, statusText: '404 Error' }; 18 | window.fetch = jest.fn(() => Promise.resolve(mockResponse(404, '404 Error', JSON.stringify(RESPONSE)))); 19 | 20 | return getJson('//url').catch(error => { 21 | expect(error.status).toEqual(RESPONSE.status); 22 | }); 23 | }); 24 | 25 | it('has window.fetch polyfill', () => { 26 | expect(_fetch.polyfill).toBe(true); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /client/utils/__tests__/noop.spec.js: -------------------------------------------------------------------------------- 1 | import noop from '../noop'; 2 | 3 | describe('noop', () => { 4 | it('return nothing', () => { 5 | const result = noop(); 6 | expect(result).toBeFalsy(); 7 | }) 8 | }); 9 | -------------------------------------------------------------------------------- /client/utils/classes.js: -------------------------------------------------------------------------------- 1 | function classes(...classnames) { 2 | return classnames.filter(classname => !!classname).join(' '); 3 | } 4 | 5 | export default classes; 6 | -------------------------------------------------------------------------------- /client/utils/fetch.js: -------------------------------------------------------------------------------- 1 | import 'es6-promise/auto'; 2 | import 'whatwg-fetch'; 3 | 4 | const _errorHandle = response => { 5 | if (response.ok) { 6 | return response; 7 | } 8 | 9 | let error = new Error(response.statusText); 10 | error.status = response.status; 11 | error.response = response; 12 | 13 | throw error; 14 | }; 15 | 16 | const _parseJson = response => response.json(); 17 | 18 | export const getJson = url => fetch(url).then(_errorHandle).then(_parseJson); 19 | 20 | export default { getJson }; 21 | -------------------------------------------------------------------------------- /client/utils/noop.js: -------------------------------------------------------------------------------- 1 | export default () => {}; 2 | -------------------------------------------------------------------------------- /client/webpack.config.js: -------------------------------------------------------------------------------- 1 | const TARGET = process.env.npm_lifecycle_event; 2 | const ENV = process.env.NODE_ENV; 3 | process.env.BABEL_ENV = TARGET; 4 | 5 | if (TARGET === 'start' || !TARGET || ENV === 'development') { 6 | module.exports = require('./config/webpack.config.dev'); 7 | } 8 | if (TARGET === 'build' || TARGET === 'stats' || ENV === 'production') { 9 | module.exports = require('./config/webpack.config.prod'); 10 | } 11 | if (TARGET === 'test' || TARGET === 'tdd') { 12 | module.exports = require('./config/webpack.config.test'); 13 | } 14 | -------------------------------------------------------------------------------- /docs/issue_template.md: -------------------------------------------------------------------------------- 1 | # Select an issue type 2 | 3 | - [ ] bug 4 | - [ ] new feature 5 | 6 | --- 7 | 8 | ## * Bug template 9 | 10 | ### Test Case 11 | 12 | 13 | ### Steps to reproduce 14 | 15 | 16 | ### Expected Behavior 17 | 18 | 19 | ### Actual Behavior 20 | 21 | 22 | ### Version 23 | 24 | --- 25 | 26 | ## * New Feature template 27 | 28 | ### Description 29 | 30 | -------------------------------------------------------------------------------- /docs/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Changes proposed on this pull request: 2 | 3 | - 4 | - 5 | 6 | ### Fixes # . 7 | 8 | #### Reviewers 9 | 10 | * @carloluis 11 | * @matiasdelgado 12 | * @rrivem 13 | * @marina-acosta 14 | -------------------------------------------------------------------------------- /docs/setup.md: -------------------------------------------------------------------------------- 1 | # React Seed - Setup 2 | 3 | ## Install dependencies 4 | 5 | Restore all packages from root folder: 6 | 7 | ```bash 8 | $ yarn # yarn install 9 | ``` 10 | 11 | ## Database 12 | 13 | You can install Postgres or run your server in a Docker container. 14 | 15 | ### Postgres 16 | 17 | * Install [postgres](https://www.postgresql.org/). When prompted to enter the `postgres` user password, use `sa.pg.01` or one of your choice 18 | * Make sure to update the password in `server/datastore/create/knexfile.js` if you picked a different password. 19 | * For more information about setting up the server see [official docs](https://wiki.postgresql.org/wiki/First_steps) 20 | 21 | ### Docker 22 | 23 | * Install [Docker](https://docs.docker.com/engine/installation/) and [docker-compose](https://docs.docker.com/compose/install/) 24 | * Run `yarn database:docker-server` to create the docker container for the Postgres Server (volume to persist data will be mounted) 25 | 26 | #### Structure and Data 27 | 28 | Once you have configured your Postgres server, run the following commands: 29 | 30 | * Create the `reactseeddb` database, `pg` user and grant access to database: 31 | * `yarn database:create` 32 | * Create tables running migrations: 33 | * `yarn migrate:latest` 34 | * Populate database running the seeds: 35 | * `yarn seed:run` 36 | 37 | ## Start the app 38 | 39 | ### Execute the express server 40 | 41 | #### Development (with nodemon) 42 | 43 | ```bash 44 | $ yarn dev # or (yarn run dev) 45 | ``` 46 | 47 | #### Production 48 | 49 | ```bash 50 | $ yarn prod # build and start 51 | ``` 52 | 53 | Open browser on [localhost:4000](http://localhost:4000/) 54 | 55 | ## NPM Scripts 56 | 57 | ```bash 58 | $ yarn build # build production assets 59 | 60 | $ yarn start # execute production server 61 | 62 | $ yarn test # execute all tests 63 | 64 | $ yarn lint # execute linting 65 | ``` 66 | 67 | 68 | 69 | #### More scripts 70 | 71 | * `test`: exec all test (client uses `jest`, server uses `tape`) 72 | * `test:client`: exec client test 73 | * `test:server`: exec server test 74 | * `tdd`: exec test (on watch mode) 75 | * `lint`: exec linting (`eslint`) 76 | * `migrate:*`: knex migrations 77 | * `migrate:make`: create migration script 78 | * `migrate:latest`: exec migrations 79 | * `migrate:rollback`: rollback migration 80 | * `seed:*`: data seeds 81 | * `seed:make`: create seed script 82 | * `seed:run`: exec seeds 83 | * `stats`: run `npm` stats 84 | * `build`: build production assets 85 | * `start`: exec production server 86 | * `dev`: exec development server 87 | * `prod`: build production assets and exec production server 88 | 89 | 90 | 91 | ## Development 92 | 93 | ### Git Hooks (using `husky`) 94 | 95 | In develop we use [git hooks](https://git-scm.com/docs/githooks) for automate linting and testing. 96 | 97 | Using [husky](https://github.com/typicode/husky) with `yarn`: 98 | 99 | ```bash 100 | $ yarn add husky --dev --force # ensures hooks will be installed 101 | ``` 102 | 103 | Install hooks manually (using `node`): 104 | 105 | ```bash 106 | $ node node_modules/husky/bin/install 107 | ``` 108 | -------------------------------------------------------------------------------- /docs/vscode.md: -------------------------------------------------------------------------------- 1 | # VS Code Config 2 | 3 | Our team use this editor as IDE. 4 | Here are some useful configs and extensions we use 5 | 6 | ## Code Formatter 7 | 8 | We encourage the use of [prettier](https://github.com/prettier/prettier) in your editor for consistent code style. 9 | 10 | ## Settings.json 11 | 12 | ```javascript 13 | // Settings we use to overwrite the default ones 14 | { 15 | "editor.rulers": [120], 16 | "editor.renderWhitespace": "boundary", 17 | "editor.dragAndDrop": true, 18 | 19 | // settings for `relative path` extension 20 | "relativePath.removeExtension": true, 21 | "relativePath.removeLeadingDot": true 22 | } 23 | ``` 24 | 25 | ## Extensions 26 | 27 | * [EditorConfig for VS Code](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) 28 | * [Mithril Emmet](https://marketplace.visualstudio.com/items?itemName=FallenMax.mithril-emmet) 29 | * [Prettier - JavaScript formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) 30 | * [Relative Path](https://marketplace.visualstudio.com/items?itemName=jakob101.RelativePath) 31 | * [UruIT React Snippets](https://marketplace.visualstudio.com/items?itemName=UruIT.uruit-react-snippets) 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-seed", 3 | "version": "1.1.0", 4 | "description": "react seed - uruit", 5 | "main": "index.js", 6 | "repository": "https://github.com/UruIT/react-seed.git", 7 | "author": "UruIT (https://github.com/UruIT)", 8 | "license": "MIT", 9 | "bugs": { 10 | "url": "https://github.com/UruIT/wealth-mastery/issues" 11 | }, 12 | "homepage": "https://github.com/UruIT/wealth-mastery#readme", 13 | "keywords": [ 14 | "react", 15 | "seed", 16 | "node", 17 | "express", 18 | "uruit" 19 | ], 20 | "scripts": { 21 | "precommit": "lint-staged", 22 | "prepush": "npm test", 23 | "slint": "eslint . --ext .js,.jsx --ignore-path .gitignore --cache --ignore-pattern plop-templates", 24 | "stats": "cd client && npm run stats", 25 | "build": "cd client && npm run build", 26 | "start": "cd server && npm start", 27 | "dev": "cd server && npm run dev", 28 | "prod": "npm run build && cd server && npm start", 29 | "test": "npm run test:client && npm run test:server", 30 | "test:client": "cd client && npm test", 31 | "test:server": "cd server && npm test", 32 | "test:coverage": "cd client && npm run coverage", 33 | "coveralls": "npm run test:coverage && cat ./client/coverage/lcov.info | coveralls", 34 | "tdd": "cd client && npm run tdd", 35 | "serve": "npm run build && http-server build/ -p 8081", 36 | "database:docker-server": "cd server && npm run database:docker-server", 37 | "database:create": "cd server && npm run database:create", 38 | "migrate:make": "cd server && npm run migrate:make", 39 | "migrate:latest": "cd server && npm run migrate:latest", 40 | "migrate:rollback": "cd server && npm run migrate:rollback", 41 | "seed:make": "cd server && npm run seed:make", 42 | "seed:run": "cd server && npm run seed:run", 43 | "postinstall": "npm run install:client && npm run install:server", 44 | "install:client": "cd client && yarn", 45 | "install:server": "cd server && yarn", 46 | "plop": "plop" 47 | }, 48 | "lint-staged": { 49 | "*.{js,jsx}": [ 50 | "npm run slint" 51 | ] 52 | }, 53 | "devDependencies": { 54 | "autoprefixer": "^8.2.0", 55 | "babel-core": "^6.26.0", 56 | "babel-eslint": "^8.0.1", 57 | "babel-loader": "^7.1.2", 58 | "babel-preset-es2015": "^6.24.1", 59 | "babel-preset-react": "^6.24.1", 60 | "babel-preset-survivejs-kanban": "^0.3.3", 61 | "clean-webpack-plugin": "^0.1.16", 62 | "copy-webpack-plugin": "^4.0.1", 63 | "coveralls": "^3.0.0", 64 | "css-loader": "^0.28.5", 65 | "eslint": "^4.5.0", 66 | "eslint-loader": "^2.0.0", 67 | "eslint-plugin-react": "^7.3.0", 68 | "file-loader": "^1.1.11", 69 | "html-webpack-plugin": "^3.1.0", 70 | "html-webpack-template": "^6.0.1", 71 | "http-server": "^0.11.1", 72 | "husky": "^0.14.3", 73 | "istanbul-instrumenter-loader": "^3.0.0", 74 | "lint-staged": "^7.0.0", 75 | "mini-css-extract-plugin": "0.4.0", 76 | "node-sass": "^4.5.3", 77 | "nodemon": "^1.11.0", 78 | "npm-install-webpack-plugin": "^4.0.5", 79 | "plop": "^2.0.0", 80 | "postcss-loader": "^2.0.6", 81 | "raw-loader": "^0.5.1", 82 | "resolve-url-loader": "^2.1.0", 83 | "sass-loader": "^6.0.6", 84 | "style-loader": "^0.20.3", 85 | "webpack": "^4.2.0", 86 | "webpack-cli": "^2.0.13", 87 | "webpack-dev-middleware": "^3.0.1", 88 | "webpack-hot-middleware": "^2.18.2", 89 | "webpack-merge": "^4.1.0" 90 | }, 91 | "engines": { 92 | "node": ">=8" 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /plop-templates/component/{{ dashCase name }}.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/_palette'; 2 | 3 | .container { 4 | background: red; 5 | } 6 | -------------------------------------------------------------------------------- /plop-templates/component/{{ pascalCase name }}.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import styles from './{{ dashCase name }}.scss'; 4 | 5 | class {{ pascalCase name }} extends React.Component { 6 | render() { 7 | return ( 8 | 9 | {{ name }} component template... 10 | 11 | ); 12 | } 13 | } 14 | 15 | {{ pascalCase name }}.propTypes = { 16 | 17 | }; 18 | 19 | export default {{ pascalCase name }}; 20 | -------------------------------------------------------------------------------- /plop-templates/component/{{ pascalCase name }}.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {{ pascalCase name }} from './{{ pascalCase name }}'; 3 | import renderer from 'react-test-renderer'; 4 | import { shallow } from 'enzyme'; 5 | 6 | const PROPS = {}; 7 | 8 | describe('{{ pascalCase name }}', () => { 9 | it('should render', () => { 10 | const wrapper = shallow(<{{ pascalCase name }} {...PROPS} />); 11 | expect(wrapper.find('div').node).toBeTruthy(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /plopfile.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = function(plop) { 4 | plop.setHelper('pwd', file => { 5 | const plopPath = process.cwd().substring(plop.getPlopfilePath().length + 1); 6 | return path.join(plopPath, file); 7 | }); 8 | 9 | plop.setGenerator('component', { 10 | prompts: [ 11 | { 12 | type: 'input', 13 | name: 'name', 14 | message: 'Component name:' 15 | } 16 | ], 17 | actions: [ 18 | { 19 | type: 'addMany', 20 | destination: '{{ pwd (dashCase name) }}', 21 | base: 'plop-templates/component', 22 | templateFiles: 'plop-templates/component/*' 23 | } 24 | ] 25 | }); 26 | }; 27 | -------------------------------------------------------------------------------- /server/README.md: -------------------------------------------------------------------------------- 1 | # React Seed Server 2 | 3 | ## Dependencies 4 | 5 | Add new packages to the `server` sub-project: 6 | 7 | ```bash 8 | yarn add 9 | ``` 10 | 11 | ## Migrations 12 | 13 | ### Create migration script 14 | ```bash 15 | yarn run migrate:make -- 16 | ``` 17 | Example: 18 | `yarn run migrate:make -- create_person_table` 19 | 20 | ### Execute migration 21 | ```bash 22 | yarn run migrate:latest 23 | ``` 24 | 25 | ### Rollback migration 26 | ```bash 27 | yarn run migrate:rollback 28 | ``` 29 | 30 | ## Seeds 31 | 32 | ### Create seed script 33 | ```bash 34 | yarn run seed:make 35 | ``` 36 | 37 | ### Execute seed 38 | ```bash 39 | yarn run seed:run 40 | ``` 41 | 42 | ### [Setup info here](../docs/setup.md) 43 | -------------------------------------------------------------------------------- /server/app.dev.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const express = require('express'); 3 | const http = require('http'); 4 | 5 | const webpackDevMiddleware = require('webpack-dev-middleware'); 6 | const webpackHotMiddleware = require('webpack-hot-middleware'); 7 | const webpack = require('webpack'); 8 | 9 | const logger = require('./utils/logger'); 10 | const { 11 | configureBodyParser, 12 | configureCors, 13 | configureDevErrorHandler, 14 | configureRequestLogger 15 | } = require('./utils/app.configure'); 16 | 17 | const DEVELOPMENT = 'development'; 18 | process.env.NODE_ENV = DEVELOPMENT; 19 | 20 | const wpConfig = require('../client/webpack.config'); 21 | const appConfig = require('./config'); 22 | 23 | wpConfig.entry.app.unshift('webpack/hot/dev-server', 'webpack-hot-middleware/client'); 24 | const HTML_FILE = path.join(wpConfig.output.path, 'index.html'); 25 | 26 | const compiler = webpack(wpConfig); 27 | 28 | const app = express(); 29 | 30 | configureRequestLogger(app); 31 | configureBodyParser(app); 32 | configureCors(app); 33 | 34 | app.use(webpackDevMiddleware(compiler, { 35 | publicPath: wpConfig.output.publicPath || '/', 36 | stats: { colors: true } 37 | })); 38 | 39 | app.use(webpackHotMiddleware(compiler)); 40 | 41 | require('./routes')(app); 42 | 43 | app.get('*', (req, res, next) => { 44 | compiler.outputFileSystem.readFile(HTML_FILE, (err, result) => { 45 | if (err) { 46 | return next(err); 47 | } 48 | 49 | res.set('content-type', 'text/html'); 50 | res.send(result); 51 | res.end(); 52 | }); 53 | }); 54 | 55 | configureDevErrorHandler(app); 56 | 57 | http.createServer(app).listen(appConfig.port, () => { 58 | logger.info(`server listening at http://localhost:${appConfig.port}`); 59 | }); 60 | 61 | module.exports = app; 62 | -------------------------------------------------------------------------------- /server/app.prod.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | 4 | const { 5 | configureBodyParser, 6 | configureCompression, 7 | configureCors, 8 | configureErrorHandler 9 | } = require('./utils/app.configure'); 10 | 11 | const DIST_DIR = path.join(__dirname, '../dist'); 12 | const HTML_FILE = path.join(DIST_DIR, 'index.html'); 13 | 14 | const app = express(); 15 | 16 | init(app); 17 | 18 | app.use(express.static(DIST_DIR)); 19 | 20 | app.get('*', (req, res) => res.sendFile(HTML_FILE)); 21 | 22 | function init(app) { 23 | configureCors(app); 24 | configureBodyParser(app); 25 | configureErrorHandler(app); 26 | configureCompression(app); 27 | 28 | require('./routes')(app); 29 | } 30 | 31 | module.exports = app; 32 | -------------------------------------------------------------------------------- /server/app.test.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./app.prod'); 2 | -------------------------------------------------------------------------------- /server/config/index.js: -------------------------------------------------------------------------------- 1 | const commonConfig = { 2 | database: { 3 | connectionString: process.env.DATABASE_URL || 'postgres://pg:pg@localhost:5432/reactseeddb' 4 | }, 5 | port: 4000, 6 | sslPort: 4443 7 | // Add here settings shared by all the environments 8 | }; 9 | 10 | const appConfigurations = { 11 | development: Object.assign({}, commonConfig, { 12 | // Add development configurations here 13 | }), 14 | stage: Object.assign({}, commonConfig, { 15 | // Add stage configurations here 16 | }), 17 | production: Object.assign({}, commonConfig, { 18 | // Add production configurations here 19 | }) 20 | }; 21 | 22 | module.exports = appConfigurations[process.env.NODE_ENV || 'development']; 23 | -------------------------------------------------------------------------------- /server/datastore/connection/knexfile.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const config = require('../../config'); 3 | 4 | const connection = { 5 | client: 'postgresql', 6 | connection: config.database.connectionString, 7 | migrations: { 8 | directory: path.resolve(__dirname, '../migrations') 9 | }, 10 | seeds: { 11 | directory: path.resolve(__dirname, '../seeds') 12 | } 13 | }; 14 | 15 | module.exports = connection; 16 | -------------------------------------------------------------------------------- /server/datastore/constants/postgres-errors.js: -------------------------------------------------------------------------------- 1 | // For more details, see https://www.postgresql.org/docs/9.4/static/errcodes-appendix.html 2 | 3 | module.exports = { 4 | NOT_NULL_VIOLATION: '23502', 5 | FOREIGN_KEY_VIOLATION: '23503' 6 | }; 7 | -------------------------------------------------------------------------------- /server/datastore/create/knexfile.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const connection = { 4 | client: 'postgresql', 5 | connection: { 6 | host: 'localhost', 7 | user: 'postgres', 8 | password: 'sa.pg.01', 9 | database: 'postgres' 10 | }, 11 | seeds: { 12 | directory: path.resolve(__dirname, './seeds') 13 | } 14 | }; 15 | 16 | module.exports = connection; 17 | -------------------------------------------------------------------------------- /server/datastore/create/seeds/create.js: -------------------------------------------------------------------------------- 1 | exports.seed = function(knex) { 2 | const db = 'reactseeddb'; 3 | const user = 'pg'; 4 | const pass = 'pg'; 5 | 6 | const createDb = () => knex.raw(`CREATE DATABASE ${db}`); 7 | const createUser = () => knex.raw(`CREATE USER ${user} WITH PASSWORD '${pass}'`); 8 | const grantUserToDb = () => knex.raw(`GRANT ALL ON DATABASE ${db} TO ${user}`); 9 | 10 | return createDb().then(createUser).then(grantUserToDb); 11 | }; 12 | -------------------------------------------------------------------------------- /server/datastore/migrations/20170518181737_create_sample_table.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function (knex) { 3 | return Promise.resolve( 4 | knex.schema.createTable('sample', function (table) { 5 | table.integer('id').primary(); 6 | table.string('description').notNullable(); 7 | }) 8 | ); 9 | }; 10 | 11 | exports.down = function (knex) { 12 | return Promise.resolve( 13 | knex.schema.dropTable('sample') 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /server/datastore/migrations/20170613154653_create_lookup_table.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex, Promise) { 3 | return Promise.all([ 4 | knex.schema.createTable('lookup', function (table) { 5 | table.integer('id').primary(); 6 | table.string('description').notNullable(); 7 | }), 8 | knex.schema.table('sample', function (table) { 9 | table.integer('value').references('id').inTable('lookup'); 10 | }) 11 | ]) 12 | }; 13 | 14 | exports.down = function(knex, Promise) { 15 | return Promise.all([ 16 | knex.schema.table('sample', function (table) { 17 | table.dropColumn('value'); 18 | }), 19 | knex.schema.dropTable('lookup') 20 | ]) 21 | }; 22 | -------------------------------------------------------------------------------- /server/datastore/seeds/populate_sample_table.js: -------------------------------------------------------------------------------- 1 | exports.seed = function(knex) { 2 | // Deletes ALL existing entries 3 | return knex('sample').del().then(function() { 4 | // Inserts seed entries 5 | return knex('sample').insert([ 6 | { id: 1, description: 'UruIT' }, 7 | { id: 2, description: 'React' }, 8 | { id: 3, description: 'Seed' } 9 | ]); 10 | }); 11 | }; 12 | -------------------------------------------------------------------------------- /server/datastore/server/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | postgres: 5 | image: postgres 6 | environment: 7 | - POSTGRES_PASSWORD=sa.pg.01 8 | volumes: 9 | - data:/var/lib/postgresql/data 10 | ports: 11 | - "5432:5432" 12 | 13 | volumes: 14 | data: 15 | -------------------------------------------------------------------------------- /server/datastore/stores/base.store.js: -------------------------------------------------------------------------------- 1 | const knexfile = require('../connection/knexfile'); 2 | const Knex = require('knex')(knexfile); 3 | 4 | const PostgresErrors = require('../constants/postgres-errors'); 5 | const { BadRequestException, ValidationException } = require('../../exceptions'); 6 | 7 | class BaseStore { 8 | constructor(tableName) { 9 | this.tableName = tableName; 10 | } 11 | 12 | get table() { 13 | return Knex(this.tableName); 14 | } 15 | 16 | get(id) { 17 | return this.table 18 | .first('*') 19 | .where('id', id); 20 | } 21 | 22 | getAll() { 23 | return this.table.select('*'); 24 | } 25 | 26 | // Accepts an object or an array of objects to insert 27 | insert(entity, trx, returning = 'id') { 28 | return this.table 29 | .transacting(trx) 30 | .insert(entity, returning) 31 | .catch(err => { 32 | if (err.code === PostgresErrors.NOT_NULL_VIOLATION) { 33 | throw new BadRequestException(); 34 | } 35 | if (err.code === PostgresErrors.FOREIGN_KEY_VIOLATION) { 36 | throw new ValidationException(); 37 | } 38 | throw err; 39 | }); 40 | } 41 | 42 | update(id, entity, trx, returning = 'id') { 43 | return this.table 44 | .transacting(trx) 45 | .where('id', id) 46 | .update(entity, returning); 47 | } 48 | } 49 | 50 | module.exports = BaseStore; 51 | -------------------------------------------------------------------------------- /server/datastore/stores/lookup.store.js: -------------------------------------------------------------------------------- 1 | const BaseStore = require('./base.store'); 2 | 3 | class LookupStore extends BaseStore { 4 | constructor() { 5 | super('lookup'); 6 | } 7 | } 8 | 9 | module.exports = new LookupStore(); 10 | -------------------------------------------------------------------------------- /server/datastore/stores/sample.store.js: -------------------------------------------------------------------------------- 1 | const BaseStore = require('./base.store'); 2 | 3 | class SampleStore extends BaseStore { 4 | constructor() { 5 | super('sample'); 6 | } 7 | } 8 | 9 | module.exports = new SampleStore(); 10 | -------------------------------------------------------------------------------- /server/exceptions/bad-request.exception.js: -------------------------------------------------------------------------------- 1 | class BadRequestException extends Error { 2 | constructor() { 3 | super('Inconsistent data'); 4 | 5 | Error.captureStackTrace(this, BadRequestException); 6 | this.name = this.constructor.name; 7 | this.status = 400; 8 | } 9 | } 10 | 11 | module.exports = BadRequestException; 12 | -------------------------------------------------------------------------------- /server/exceptions/generic.exception.js: -------------------------------------------------------------------------------- 1 | 2 | class GenericException extends Error { 3 | constructor() { 4 | super('Unexpected error'); 5 | 6 | Error.captureStackTrace(this, GenericException); 7 | this.name = this.constructor.name; 8 | this.status = 500; 9 | } 10 | } 11 | 12 | module.exports = GenericException; 13 | -------------------------------------------------------------------------------- /server/exceptions/index.js: -------------------------------------------------------------------------------- 1 | const BadRequestException = require('./bad-request.exception'); 2 | const GenericException = require('./generic.exception'); 3 | const NotFoundException = require('./not-found.exception'); 4 | const ValidationException = require('./validation.exception'); 5 | 6 | module.exports = { 7 | BadRequestException, 8 | GenericException, 9 | NotFoundException, 10 | ValidationException 11 | }; 12 | -------------------------------------------------------------------------------- /server/exceptions/not-found.exception.js: -------------------------------------------------------------------------------- 1 | 2 | class NotFoundException extends Error { 3 | constructor() { 4 | super(); 5 | 6 | Error.captureStackTrace(this, NotFoundException); 7 | this.name = this.constructor.name; 8 | this.status = 404; 9 | } 10 | } 11 | 12 | module.exports = NotFoundException; 13 | -------------------------------------------------------------------------------- /server/exceptions/validation.exception.js: -------------------------------------------------------------------------------- 1 | class ValidationException extends Error { 2 | constructor() { 3 | super('Data validation error'); 4 | 5 | Error.captureStackTrace(this, ValidationException); 6 | this.name = this.constructor.name; 7 | this.status = 409; 8 | } 9 | } 10 | 11 | module.exports = ValidationException; 12 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | 3 | const app = require('./app.prod'); 4 | const appConfig = require('./config'); 5 | const logger = require('./utils/logger'); 6 | 7 | app.set('x-powered-by', false); 8 | 9 | http.createServer(app).listen(appConfig.port); 10 | 11 | logger.info(`http://localhost:${appConfig.port}`); 12 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-seed-server", 3 | "version": "1.0.0", 4 | "description": "react seed - server", 5 | "main": "index.js", 6 | "repository": "https://github.com/UruIT/react-seed.git", 7 | "author": "UruIT (https://github.com/UruIT)", 8 | "license": "MIT", 9 | "scripts": { 10 | "start": "node index.js", 11 | "dev": "nodemon app.dev.js", 12 | "database:docker-server": "docker-compose --file datastore/server/docker-compose.yml up -d", 13 | "database:create": "knex seed:run --knexfile ./datastore/create/knexfile.js", 14 | "migrate:make": "knex migrate:make --knexfile ./datastore/connection/knexfile.js", 15 | "migrate:latest": "knex migrate:latest --knexfile ./datastore/connection/knexfile.js", 16 | "migrate:rollback": "knex migrate:rollback --knexfile ./datastore/connection/knexfile.js", 17 | "seed:make": "knex seed:make --knexfile ./datastore/connection/knexfile.js", 18 | "seed:run": "knex seed:run --knexfile ./datastore/connection/knexfile.js", 19 | "test": "tape tests/*.js | tap-spec" 20 | }, 21 | "dependencies": { 22 | "body-parser": "^1.17.2", 23 | "compression": "^1.7.0", 24 | "express": "^4.15.4", 25 | "knex": "^0.13.0", 26 | "pg": "^7.2.0", 27 | "winston": "^2.3.1" 28 | }, 29 | "devDependencies": { 30 | "supertest": "^3.0.0", 31 | "tap-spec": "^4.1.1", 32 | "tape": "^4.8.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /server/routes/index.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | app.use('/api/sample', require('./sample.route')); 3 | // Add routes here 4 | }; 5 | -------------------------------------------------------------------------------- /server/routes/sample.route.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const sampleSvc = require('../services/sample.service'); 4 | 5 | router.get('/', (request, response, next) => { 6 | sampleSvc 7 | .getAll() 8 | .then(result => response.send(result)) 9 | .catch(err => next(err)); 10 | }); 11 | 12 | router.get('/:id', (request, response, next) => { 13 | sampleSvc 14 | .get(request.params.id) 15 | .then(result => response.send(result)) 16 | .catch(err => next(err)); 17 | }); 18 | 19 | router.post('/', (request, response, next) => { 20 | sampleSvc 21 | .insert(request.body) 22 | .then(result => response.send(result)) 23 | .catch(err => next(err)); 24 | }); 25 | 26 | module.exports = router; 27 | -------------------------------------------------------------------------------- /server/services/sample.service.js: -------------------------------------------------------------------------------- 1 | const sampleStore = require('../datastore/stores/sample.store'); 2 | const { NotFoundException } = require('../exceptions'); 3 | 4 | class SampleService { 5 | getAll() { 6 | return sampleStore.getAll(); 7 | } 8 | 9 | get(id) { 10 | return sampleStore.get(id).then(sample => { 11 | if (!sample) { 12 | throw new NotFoundException(); 13 | } 14 | return sample; 15 | }); 16 | } 17 | 18 | insert(sample) { 19 | return sampleStore.insert(sample); 20 | } 21 | } 22 | 23 | module.exports = new SampleService(); 24 | -------------------------------------------------------------------------------- /server/tests/config.js: -------------------------------------------------------------------------------- 1 | const test = require('tape'); 2 | 3 | test.onFinish(() => process.exit(0)); 4 | -------------------------------------------------------------------------------- /server/tests/sample.test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape'); 2 | const request = require('supertest'); 3 | const app = require('../app.test'); 4 | 5 | test('GET samples', t => { 6 | request(app) 7 | .get('/api/sample') 8 | .expect(200) 9 | .end((err, res) => { 10 | t.error(err, 'Expectations fullfilled'); 11 | t.same(res.body.length, 3, 'Samples as expected'); 12 | t.end(); 13 | }); 14 | }); 15 | 16 | test('GET sample one', t => { 17 | request(app) 18 | .get('/api/sample/1') 19 | .expect('Content-Type', /json/) 20 | .expect(200) 21 | .end((err, res) => { 22 | const expected = { id: 1, description: 'UruIT', value: null }; 23 | t.error(err, 'Expectations fullfilled'); 24 | t.same(res.body, expected, 'Sample as expected'); 25 | t.end(); 26 | }); 27 | }); 28 | 29 | test('GET inexistent sample', t => { 30 | request(app) 31 | .get('/api/sample/100') 32 | .expect('Content-Type', /html/) 33 | .expect(404) 34 | .end((err, res) => { 35 | const expected = { }; 36 | t.error(err, 'Expectations fullfilled'); 37 | t.same(res.body, expected, 'Empty sample'); 38 | t.end(); 39 | }); 40 | }); 41 | 42 | test('POST data with missing values', t => { 43 | request(app) 44 | .post('/api/sample') 45 | .send({ id: 3 }) // missing description 46 | .expect(400) 47 | .end((err) => { 48 | t.error(err, 'Expectations fullfilled'); 49 | t.end(); 50 | }) 51 | }); 52 | 53 | test('POST data with incorrect values', t => { 54 | request(app) 55 | .post('/api/sample') 56 | .send({ id: 4, description: 'my test', value: 8 }) // value: 8 does not exists in table `lookup` 57 | .expect(409) 58 | .end((err) => { 59 | t.error(err, 'Status code'); 60 | t.end(); 61 | }) 62 | }); 63 | -------------------------------------------------------------------------------- /server/utils/app.configure.js: -------------------------------------------------------------------------------- 1 | const bodyParser = require('body-parser'); 2 | const compression = require('compression'); 3 | const logger = require('./logger'); 4 | 5 | function configureRequestLogger(app) { 6 | app.use((req, res, next) => { 7 | logger.info(req.method, req.originalUrl); 8 | next(); 9 | }); 10 | } 11 | 12 | function configureCors(app) { 13 | app.use(function(req, res, next) { 14 | res.header('Access-Control-Allow-Origin', '*'); 15 | res.header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS'); 16 | res.header('Access-Control-Allow-Headers', 'Origin, Content-Type, Accept, Authorization'); 17 | res.header('Access-Control-Max-Age', '600'); 18 | res.header('Referrer-Policy', 'no-referrer'); 19 | res.header('X-Content-Type-Options', 'nosniff'); 20 | res.header('X-Frame-Options', 'DENY'); 21 | 22 | next(); 23 | }); 24 | } 25 | 26 | function configureBodyParser(app) { 27 | app.use(bodyParser.json()); 28 | app.use(bodyParser.urlencoded({ extended: false })); 29 | } 30 | 31 | function configureErrorHandler(app) { 32 | app.use(function(err, req, res, next) { 33 | logger.error(err.stack); 34 | const status = (err && err.status) || 500; 35 | res.status(status).send(err.message); 36 | return next(); 37 | }); 38 | } 39 | 40 | function configureDevErrorHandler(app) { 41 | app.use(function(err, req, res, next) { 42 | let message = `${err.message || ''}\nError Stack: ${err.stack}`; 43 | logger.error('Something went wrong: ', err.stack); 44 | const status = (err && err.status) || 500; 45 | res.status(status).send(message); 46 | return next(err); 47 | }); 48 | } 49 | 50 | function configureCompression(app) { 51 | app.use(compression()); 52 | } 53 | 54 | module.exports = { 55 | configureBodyParser, 56 | configureCompression, 57 | configureCors, 58 | configureDevErrorHandler, 59 | configureErrorHandler, 60 | configureRequestLogger 61 | }; 62 | -------------------------------------------------------------------------------- /server/utils/logger.js: -------------------------------------------------------------------------------- 1 | var winston = require('winston'); 2 | 3 | var logger = new winston.Logger({ 4 | transports: [ 5 | new winston.transports.Console({ 6 | level: 'debug', 7 | handleExceptions: true, 8 | json: false, 9 | colorize: true, 10 | timestamp: true, 11 | prettyPrint: true 12 | }) 13 | ] 14 | }); 15 | 16 | module.exports = logger; 17 | -------------------------------------------------------------------------------- /server/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | accepts@~1.3.3: 6 | version "1.3.3" 7 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" 8 | dependencies: 9 | mime-types "~2.1.11" 10 | negotiator "0.6.1" 11 | 12 | accepts@~1.3.4: 13 | version "1.3.4" 14 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.4.tgz#86246758c7dd6d21a6474ff084a4740ec05eb21f" 15 | dependencies: 16 | mime-types "~2.1.16" 17 | negotiator "0.6.1" 18 | 19 | ansi-regex@^2.0.0: 20 | version "2.1.1" 21 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" 22 | 23 | ansi-styles@^2.2.1: 24 | version "2.2.1" 25 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" 26 | 27 | arr-diff@^2.0.0: 28 | version "2.0.0" 29 | resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" 30 | dependencies: 31 | arr-flatten "^1.0.1" 32 | 33 | arr-flatten@^1.0.1: 34 | version "1.0.3" 35 | resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.3.tgz#a274ed85ac08849b6bd7847c4580745dc51adfb1" 36 | 37 | array-flatten@1.1.1: 38 | version "1.1.1" 39 | resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" 40 | 41 | array-unique@^0.2.1: 42 | version "0.2.1" 43 | resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" 44 | 45 | async@~1.0.0: 46 | version "1.0.0" 47 | resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9" 48 | 49 | asynckit@^0.4.0: 50 | version "0.4.0" 51 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" 52 | 53 | babel-runtime@^6.11.6: 54 | version "6.23.0" 55 | resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b" 56 | dependencies: 57 | core-js "^2.4.0" 58 | regenerator-runtime "^0.10.0" 59 | 60 | balanced-match@^1.0.0: 61 | version "1.0.0" 62 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 63 | 64 | bluebird@^3.4.6: 65 | version "3.5.0" 66 | resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" 67 | 68 | body-parser@^1.17.2: 69 | version "1.18.2" 70 | resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454" 71 | dependencies: 72 | bytes "3.0.0" 73 | content-type "~1.0.4" 74 | debug "2.6.9" 75 | depd "~1.1.1" 76 | http-errors "~1.6.2" 77 | iconv-lite "0.4.19" 78 | on-finished "~2.3.0" 79 | qs "6.5.1" 80 | raw-body "2.3.2" 81 | type-is "~1.6.15" 82 | 83 | brace-expansion@^1.1.7: 84 | version "1.1.8" 85 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" 86 | dependencies: 87 | balanced-match "^1.0.0" 88 | concat-map "0.0.1" 89 | 90 | braces@^1.8.2: 91 | version "1.8.5" 92 | resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" 93 | dependencies: 94 | expand-range "^1.8.1" 95 | preserve "^0.2.0" 96 | repeat-element "^1.1.2" 97 | 98 | buffer-writer@1.0.1: 99 | version "1.0.1" 100 | resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-1.0.1.tgz#22a936901e3029afcd7547eb4487ceb697a3bf08" 101 | 102 | bytes@3.0.0: 103 | version "3.0.0" 104 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" 105 | 106 | chalk@^1.0.0: 107 | version "1.1.3" 108 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" 109 | dependencies: 110 | ansi-styles "^2.2.1" 111 | escape-string-regexp "^1.0.2" 112 | has-ansi "^2.0.0" 113 | strip-ansi "^3.0.0" 114 | supports-color "^2.0.0" 115 | 116 | colors@1.0.x: 117 | version "1.0.3" 118 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" 119 | 120 | combined-stream@^1.0.5: 121 | version "1.0.5" 122 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" 123 | dependencies: 124 | delayed-stream "~1.0.0" 125 | 126 | commander@^2.2.0: 127 | version "2.9.0" 128 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" 129 | dependencies: 130 | graceful-readlink ">= 1.0.0" 131 | 132 | component-emitter@^1.2.0: 133 | version "1.2.1" 134 | resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" 135 | 136 | compressible@~2.0.11: 137 | version "2.0.11" 138 | resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.11.tgz#16718a75de283ed8e604041625a2064586797d8a" 139 | dependencies: 140 | mime-db ">= 1.29.0 < 2" 141 | 142 | compression@^1.7.0: 143 | version "1.7.1" 144 | resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.1.tgz#eff2603efc2e22cf86f35d2eb93589f9875373db" 145 | dependencies: 146 | accepts "~1.3.4" 147 | bytes "3.0.0" 148 | compressible "~2.0.11" 149 | debug "2.6.9" 150 | on-headers "~1.0.1" 151 | safe-buffer "5.1.1" 152 | vary "~1.1.2" 153 | 154 | concat-map@0.0.1: 155 | version "0.0.1" 156 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 157 | 158 | content-disposition@0.5.2: 159 | version "0.5.2" 160 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" 161 | 162 | content-type@~1.0.2: 163 | version "1.0.2" 164 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" 165 | 166 | content-type@~1.0.4: 167 | version "1.0.4" 168 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" 169 | 170 | cookie-signature@1.0.6: 171 | version "1.0.6" 172 | resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" 173 | 174 | cookie@0.3.1: 175 | version "0.3.1" 176 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" 177 | 178 | cookiejar@^2.0.6: 179 | version "2.1.1" 180 | resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.1.tgz#41ad57b1b555951ec171412a81942b1e8200d34a" 181 | 182 | core-js@^2.4.0: 183 | version "2.4.1" 184 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" 185 | 186 | core-util-is@~1.0.0: 187 | version "1.0.2" 188 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 189 | 190 | cycle@1.0.x: 191 | version "1.0.3" 192 | resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2" 193 | 194 | debug@2.6.9: 195 | version "2.6.9" 196 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 197 | dependencies: 198 | ms "2.0.0" 199 | 200 | debug@^2.1.3, debug@^2.2.0: 201 | version "2.6.7" 202 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.7.tgz#92bad1f6d05bbb6bba22cca88bcd0ec894c2861e" 203 | dependencies: 204 | ms "2.0.0" 205 | 206 | deep-equal@~1.0.1: 207 | version "1.0.1" 208 | resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" 209 | 210 | define-properties@^1.1.2: 211 | version "1.1.2" 212 | resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" 213 | dependencies: 214 | foreach "^2.0.5" 215 | object-keys "^1.0.8" 216 | 217 | defined@~1.0.0: 218 | version "1.0.0" 219 | resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" 220 | 221 | delayed-stream@~1.0.0: 222 | version "1.0.0" 223 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" 224 | 225 | depd@1.1.1, depd@~1.1.1: 226 | version "1.1.1" 227 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" 228 | 229 | destroy@~1.0.4: 230 | version "1.0.4" 231 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" 232 | 233 | detect-file@^0.1.0: 234 | version "0.1.0" 235 | resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-0.1.0.tgz#4935dedfd9488648e006b0129566e9386711ea63" 236 | dependencies: 237 | fs-exists-sync "^0.1.0" 238 | 239 | duplexer@^0.1.1: 240 | version "0.1.1" 241 | resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" 242 | 243 | ee-first@1.1.1: 244 | version "1.1.1" 245 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 246 | 247 | encodeurl@~1.0.1: 248 | version "1.0.1" 249 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" 250 | 251 | es-abstract@^1.5.0: 252 | version "1.7.0" 253 | resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.7.0.tgz#dfade774e01bfcd97f96180298c449c8623fb94c" 254 | dependencies: 255 | es-to-primitive "^1.1.1" 256 | function-bind "^1.1.0" 257 | is-callable "^1.1.3" 258 | is-regex "^1.0.3" 259 | 260 | es-to-primitive@^1.1.1: 261 | version "1.1.1" 262 | resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" 263 | dependencies: 264 | is-callable "^1.1.1" 265 | is-date-object "^1.0.1" 266 | is-symbol "^1.0.1" 267 | 268 | escape-html@~1.0.3: 269 | version "1.0.3" 270 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 271 | 272 | escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: 273 | version "1.0.5" 274 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 275 | 276 | etag@~1.8.0: 277 | version "1.8.0" 278 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.0.tgz#6f631aef336d6c46362b51764044ce216be3c051" 279 | 280 | etag@~1.8.1: 281 | version "1.8.1" 282 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" 283 | 284 | expand-brackets@^0.1.4: 285 | version "0.1.5" 286 | resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" 287 | dependencies: 288 | is-posix-bracket "^0.1.0" 289 | 290 | expand-range@^1.8.1: 291 | version "1.8.2" 292 | resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" 293 | dependencies: 294 | fill-range "^2.1.0" 295 | 296 | expand-tilde@^1.2.2: 297 | version "1.2.2" 298 | resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-1.2.2.tgz#0b81eba897e5a3d31d1c3d102f8f01441e559449" 299 | dependencies: 300 | os-homedir "^1.0.1" 301 | 302 | express@^4.15.4: 303 | version "4.15.5" 304 | resolved "https://registry.yarnpkg.com/express/-/express-4.15.5.tgz#670235ca9598890a5ae8170b83db722b842ed927" 305 | dependencies: 306 | accepts "~1.3.3" 307 | array-flatten "1.1.1" 308 | content-disposition "0.5.2" 309 | content-type "~1.0.2" 310 | cookie "0.3.1" 311 | cookie-signature "1.0.6" 312 | debug "2.6.9" 313 | depd "~1.1.1" 314 | encodeurl "~1.0.1" 315 | escape-html "~1.0.3" 316 | etag "~1.8.0" 317 | finalhandler "~1.0.6" 318 | fresh "0.5.2" 319 | merge-descriptors "1.0.1" 320 | methods "~1.1.2" 321 | on-finished "~2.3.0" 322 | parseurl "~1.3.1" 323 | path-to-regexp "0.1.7" 324 | proxy-addr "~1.1.5" 325 | qs "6.5.0" 326 | range-parser "~1.2.0" 327 | send "0.15.6" 328 | serve-static "1.12.6" 329 | setprototypeof "1.0.3" 330 | statuses "~1.3.1" 331 | type-is "~1.6.15" 332 | utils-merge "1.0.0" 333 | vary "~1.1.1" 334 | 335 | extend@^3.0.0: 336 | version "3.0.1" 337 | resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" 338 | 339 | extglob@^0.3.1: 340 | version "0.3.2" 341 | resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" 342 | dependencies: 343 | is-extglob "^1.0.0" 344 | 345 | eyes@0.1.x: 346 | version "0.1.8" 347 | resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" 348 | 349 | figures@^1.4.0: 350 | version "1.7.0" 351 | resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" 352 | dependencies: 353 | escape-string-regexp "^1.0.5" 354 | object-assign "^4.1.0" 355 | 356 | filename-regex@^2.0.0: 357 | version "2.0.1" 358 | resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" 359 | 360 | fill-range@^2.1.0: 361 | version "2.2.3" 362 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" 363 | dependencies: 364 | is-number "^2.1.0" 365 | isobject "^2.0.0" 366 | randomatic "^1.1.3" 367 | repeat-element "^1.1.2" 368 | repeat-string "^1.5.2" 369 | 370 | finalhandler@~1.0.6: 371 | version "1.0.6" 372 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.6.tgz#007aea33d1a4d3e42017f624848ad58d212f814f" 373 | dependencies: 374 | debug "2.6.9" 375 | encodeurl "~1.0.1" 376 | escape-html "~1.0.3" 377 | on-finished "~2.3.0" 378 | parseurl "~1.3.2" 379 | statuses "~1.3.1" 380 | unpipe "~1.0.0" 381 | 382 | findup-sync@^0.4.2: 383 | version "0.4.3" 384 | resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.4.3.tgz#40043929e7bc60adf0b7f4827c4c6e75a0deca12" 385 | dependencies: 386 | detect-file "^0.1.0" 387 | is-glob "^2.0.1" 388 | micromatch "^2.3.7" 389 | resolve-dir "^0.1.0" 390 | 391 | flagged-respawn@^0.3.2: 392 | version "0.3.2" 393 | resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-0.3.2.tgz#ff191eddcd7088a675b2610fffc976be9b8074b5" 394 | 395 | for-each@~0.3.2: 396 | version "0.3.2" 397 | resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.2.tgz#2c40450b9348e97f281322593ba96704b9abd4d4" 398 | dependencies: 399 | is-function "~1.0.0" 400 | 401 | for-in@^1.0.1: 402 | version "1.0.2" 403 | resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" 404 | 405 | for-own@^0.1.4: 406 | version "0.1.5" 407 | resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" 408 | dependencies: 409 | for-in "^1.0.1" 410 | 411 | foreach@^2.0.5: 412 | version "2.0.5" 413 | resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" 414 | 415 | form-data@^2.1.1: 416 | version "2.2.0" 417 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.2.0.tgz#9a5e3b9295f980b2623cf64fa238b14cebca707b" 418 | dependencies: 419 | asynckit "^0.4.0" 420 | combined-stream "^1.0.5" 421 | mime-types "^2.1.12" 422 | 423 | formidable@^1.1.1: 424 | version "1.1.1" 425 | resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.1.1.tgz#96b8886f7c3c3508b932d6bd70c4d3a88f35f1a9" 426 | 427 | forwarded@~0.1.0: 428 | version "0.1.0" 429 | resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" 430 | 431 | fresh@0.5.2: 432 | version "0.5.2" 433 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" 434 | 435 | fs-exists-sync@^0.1.0: 436 | version "0.1.0" 437 | resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" 438 | 439 | fs.realpath@^1.0.0: 440 | version "1.0.0" 441 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 442 | 443 | function-bind@^1.0.2, function-bind@^1.1.0, function-bind@~1.1.0: 444 | version "1.1.0" 445 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" 446 | 447 | generic-pool@^2.4.2: 448 | version "2.5.4" 449 | resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.5.4.tgz#38c6188513e14030948ec6e5cf65523d9779299b" 450 | 451 | glob-base@^0.3.0: 452 | version "0.3.0" 453 | resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" 454 | dependencies: 455 | glob-parent "^2.0.0" 456 | is-glob "^2.0.0" 457 | 458 | glob-parent@^2.0.0: 459 | version "2.0.0" 460 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" 461 | dependencies: 462 | is-glob "^2.0.0" 463 | 464 | glob@~7.1.2: 465 | version "7.1.2" 466 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" 467 | dependencies: 468 | fs.realpath "^1.0.0" 469 | inflight "^1.0.4" 470 | inherits "2" 471 | minimatch "^3.0.4" 472 | once "^1.3.0" 473 | path-is-absolute "^1.0.0" 474 | 475 | global-modules@^0.2.3: 476 | version "0.2.3" 477 | resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-0.2.3.tgz#ea5a3bed42c6d6ce995a4f8a1269b5dae223828d" 478 | dependencies: 479 | global-prefix "^0.1.4" 480 | is-windows "^0.2.0" 481 | 482 | global-prefix@^0.1.4: 483 | version "0.1.5" 484 | resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-0.1.5.tgz#8d3bc6b8da3ca8112a160d8d496ff0462bfef78f" 485 | dependencies: 486 | homedir-polyfill "^1.0.0" 487 | ini "^1.3.4" 488 | is-windows "^0.2.0" 489 | which "^1.2.12" 490 | 491 | "graceful-readlink@>= 1.0.0": 492 | version "1.0.1" 493 | resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" 494 | 495 | has-ansi@^2.0.0: 496 | version "2.0.0" 497 | resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" 498 | dependencies: 499 | ansi-regex "^2.0.0" 500 | 501 | has@^1.0.1, has@~1.0.1: 502 | version "1.0.1" 503 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" 504 | dependencies: 505 | function-bind "^1.0.2" 506 | 507 | homedir-polyfill@^1.0.0: 508 | version "1.0.1" 509 | resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" 510 | dependencies: 511 | parse-passwd "^1.0.0" 512 | 513 | http-errors@1.6.2, http-errors@~1.6.2: 514 | version "1.6.2" 515 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" 516 | dependencies: 517 | depd "1.1.1" 518 | inherits "2.0.3" 519 | setprototypeof "1.0.3" 520 | statuses ">= 1.3.1 < 2" 521 | 522 | iconv-lite@0.4.19: 523 | version "0.4.19" 524 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" 525 | 526 | inflight@^1.0.4: 527 | version "1.0.6" 528 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 529 | dependencies: 530 | once "^1.3.0" 531 | wrappy "1" 532 | 533 | inherits@2, inherits@2.0.3, inherits@~2.0.1, inherits@~2.0.3: 534 | version "2.0.3" 535 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 536 | 537 | ini@^1.3.4: 538 | version "1.3.4" 539 | resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" 540 | 541 | interpret@^0.6.5: 542 | version "0.6.6" 543 | resolved "https://registry.yarnpkg.com/interpret/-/interpret-0.6.6.tgz#fecd7a18e7ce5ca6abfb953e1f86213a49f1625b" 544 | 545 | ipaddr.js@1.4.0: 546 | version "1.4.0" 547 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.4.0.tgz#296aca878a821816e5b85d0a285a99bcff4582f0" 548 | 549 | is-buffer@^1.1.5: 550 | version "1.1.5" 551 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" 552 | 553 | is-callable@^1.1.1, is-callable@^1.1.3: 554 | version "1.1.3" 555 | resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" 556 | 557 | is-date-object@^1.0.1: 558 | version "1.0.1" 559 | resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" 560 | 561 | is-dotfile@^1.0.0: 562 | version "1.0.3" 563 | resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" 564 | 565 | is-equal-shallow@^0.1.3: 566 | version "0.1.3" 567 | resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" 568 | dependencies: 569 | is-primitive "^2.0.0" 570 | 571 | is-extendable@^0.1.1: 572 | version "0.1.1" 573 | resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" 574 | 575 | is-extglob@^1.0.0: 576 | version "1.0.0" 577 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" 578 | 579 | is-finite@^1.0.1: 580 | version "1.0.2" 581 | resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" 582 | dependencies: 583 | number-is-nan "^1.0.0" 584 | 585 | is-function@~1.0.0: 586 | version "1.0.1" 587 | resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" 588 | 589 | is-glob@^2.0.0, is-glob@^2.0.1: 590 | version "2.0.1" 591 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" 592 | dependencies: 593 | is-extglob "^1.0.0" 594 | 595 | is-number@^2.1.0: 596 | version "2.1.0" 597 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" 598 | dependencies: 599 | kind-of "^3.0.2" 600 | 601 | is-number@^3.0.0: 602 | version "3.0.0" 603 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" 604 | dependencies: 605 | kind-of "^3.0.2" 606 | 607 | is-posix-bracket@^0.1.0: 608 | version "0.1.1" 609 | resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" 610 | 611 | is-primitive@^2.0.0: 612 | version "2.0.0" 613 | resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" 614 | 615 | is-regex@^1.0.3: 616 | version "1.0.4" 617 | resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" 618 | dependencies: 619 | has "^1.0.1" 620 | 621 | is-symbol@^1.0.1: 622 | version "1.0.1" 623 | resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" 624 | 625 | is-windows@^0.2.0: 626 | version "0.2.0" 627 | resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" 628 | 629 | isarray@0.0.1: 630 | version "0.0.1" 631 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" 632 | 633 | isarray@1.0.0, isarray@~1.0.0: 634 | version "1.0.0" 635 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 636 | 637 | isexe@^2.0.0: 638 | version "2.0.0" 639 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 640 | 641 | isobject@^2.0.0: 642 | version "2.1.0" 643 | resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" 644 | dependencies: 645 | isarray "1.0.0" 646 | 647 | isstream@0.1.x: 648 | version "0.1.2" 649 | resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" 650 | 651 | js-string-escape@1.0.1: 652 | version "1.0.1" 653 | resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" 654 | 655 | kind-of@^3.0.2: 656 | version "3.2.2" 657 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" 658 | dependencies: 659 | is-buffer "^1.1.5" 660 | 661 | kind-of@^4.0.0: 662 | version "4.0.0" 663 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" 664 | dependencies: 665 | is-buffer "^1.1.5" 666 | 667 | knex@^0.13.0: 668 | version "0.13.0" 669 | resolved "https://registry.yarnpkg.com/knex/-/knex-0.13.0.tgz#08dd494f6bb64928934eec9dac34787a14ca5fa4" 670 | dependencies: 671 | babel-runtime "^6.11.6" 672 | bluebird "^3.4.6" 673 | chalk "^1.0.0" 674 | commander "^2.2.0" 675 | debug "^2.1.3" 676 | generic-pool "^2.4.2" 677 | inherits "~2.0.1" 678 | interpret "^0.6.5" 679 | liftoff "~2.2.0" 680 | lodash "^4.6.0" 681 | minimist "~1.1.0" 682 | mkdirp "^0.5.0" 683 | pg-connection-string "^0.1.3" 684 | readable-stream "^1.1.12" 685 | safe-buffer "^5.0.1" 686 | tildify "~1.0.0" 687 | uuid "^3.0.0" 688 | v8flags "^2.0.2" 689 | 690 | liftoff@~2.2.0: 691 | version "2.2.5" 692 | resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-2.2.5.tgz#998c2876cff484b103e4423b93d356da44734c91" 693 | dependencies: 694 | extend "^3.0.0" 695 | findup-sync "^0.4.2" 696 | flagged-respawn "^0.3.2" 697 | rechoir "^0.6.2" 698 | resolve "^1.1.7" 699 | 700 | lodash@^3.6.0: 701 | version "3.10.1" 702 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" 703 | 704 | lodash@^4.6.0: 705 | version "4.17.4" 706 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" 707 | 708 | media-typer@0.3.0: 709 | version "0.3.0" 710 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 711 | 712 | merge-descriptors@1.0.1: 713 | version "1.0.1" 714 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" 715 | 716 | methods@^1.1.1, methods@~1.1.2: 717 | version "1.1.2" 718 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 719 | 720 | micromatch@^2.3.7: 721 | version "2.3.11" 722 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" 723 | dependencies: 724 | arr-diff "^2.0.0" 725 | array-unique "^0.2.1" 726 | braces "^1.8.2" 727 | expand-brackets "^0.1.4" 728 | extglob "^0.3.1" 729 | filename-regex "^2.0.0" 730 | is-extglob "^1.0.0" 731 | is-glob "^2.0.1" 732 | kind-of "^3.0.2" 733 | normalize-path "^2.0.1" 734 | object.omit "^2.0.0" 735 | parse-glob "^3.0.4" 736 | regex-cache "^0.4.2" 737 | 738 | "mime-db@>= 1.29.0 < 2", mime-db@~1.30.0: 739 | version "1.30.0" 740 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" 741 | 742 | mime-db@~1.27.0: 743 | version "1.27.0" 744 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1" 745 | 746 | mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15: 747 | version "2.1.15" 748 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed" 749 | dependencies: 750 | mime-db "~1.27.0" 751 | 752 | mime-types@~2.1.16: 753 | version "2.1.17" 754 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" 755 | dependencies: 756 | mime-db "~1.30.0" 757 | 758 | mime@1.3.4, mime@^1.3.4: 759 | version "1.3.4" 760 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" 761 | 762 | minimatch@^3.0.4: 763 | version "3.0.4" 764 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 765 | dependencies: 766 | brace-expansion "^1.1.7" 767 | 768 | minimist@0.0.8: 769 | version "0.0.8" 770 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 771 | 772 | minimist@~1.1.0: 773 | version "1.1.3" 774 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.1.3.tgz#3bedfd91a92d39016fcfaa1c681e8faa1a1efda8" 775 | 776 | minimist@~1.2.0: 777 | version "1.2.0" 778 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" 779 | 780 | mkdirp@^0.5.0: 781 | version "0.5.1" 782 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 783 | dependencies: 784 | minimist "0.0.8" 785 | 786 | ms@2.0.0: 787 | version "2.0.0" 788 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 789 | 790 | negotiator@0.6.1: 791 | version "0.6.1" 792 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" 793 | 794 | normalize-path@^2.0.1: 795 | version "2.1.1" 796 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" 797 | dependencies: 798 | remove-trailing-separator "^1.0.1" 799 | 800 | number-is-nan@^1.0.0: 801 | version "1.0.1" 802 | resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" 803 | 804 | object-assign@^4.1.0: 805 | version "4.1.1" 806 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 807 | 808 | object-inspect@~1.3.0: 809 | version "1.3.0" 810 | resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.3.0.tgz#5b1eb8e6742e2ee83342a637034d844928ba2f6d" 811 | 812 | object-keys@^1.0.8: 813 | version "1.0.11" 814 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" 815 | 816 | object.omit@^2.0.0: 817 | version "2.0.1" 818 | resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" 819 | dependencies: 820 | for-own "^0.1.4" 821 | is-extendable "^0.1.1" 822 | 823 | on-finished@~2.3.0: 824 | version "2.3.0" 825 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 826 | dependencies: 827 | ee-first "1.1.1" 828 | 829 | on-headers@~1.0.1: 830 | version "1.0.1" 831 | resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" 832 | 833 | once@^1.3.0: 834 | version "1.4.0" 835 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 836 | dependencies: 837 | wrappy "1" 838 | 839 | os-homedir@^1.0.1: 840 | version "1.0.2" 841 | resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" 842 | 843 | packet-reader@0.3.1: 844 | version "0.3.1" 845 | resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-0.3.1.tgz#cd62e60af8d7fea8a705ec4ff990871c46871f27" 846 | 847 | parse-glob@^3.0.4: 848 | version "3.0.4" 849 | resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" 850 | dependencies: 851 | glob-base "^0.3.0" 852 | is-dotfile "^1.0.0" 853 | is-extglob "^1.0.0" 854 | is-glob "^2.0.0" 855 | 856 | parse-ms@^1.0.0: 857 | version "1.0.1" 858 | resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-1.0.1.tgz#56346d4749d78f23430ca0c713850aef91aa361d" 859 | 860 | parse-passwd@^1.0.0: 861 | version "1.0.0" 862 | resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" 863 | 864 | parseurl@~1.3.1: 865 | version "1.3.1" 866 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" 867 | 868 | parseurl@~1.3.2: 869 | version "1.3.2" 870 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" 871 | 872 | path-is-absolute@^1.0.0: 873 | version "1.0.1" 874 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 875 | 876 | path-parse@^1.0.5: 877 | version "1.0.5" 878 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" 879 | 880 | path-to-regexp@0.1.7: 881 | version "0.1.7" 882 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" 883 | 884 | pg-connection-string@0.1.3, pg-connection-string@^0.1.3: 885 | version "0.1.3" 886 | resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-0.1.3.tgz#da1847b20940e42ee1492beaf65d49d91b245df7" 887 | 888 | pg-pool@~2.0.3: 889 | version "2.0.3" 890 | resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-2.0.3.tgz#c022032c8949f312a4f91fb6409ce04076be3257" 891 | 892 | pg-types@~1.12.1: 893 | version "1.12.1" 894 | resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-1.12.1.tgz#d64087e3903b58ffaad279e7595c52208a14c3d2" 895 | dependencies: 896 | postgres-array "~1.0.0" 897 | postgres-bytea "~1.0.0" 898 | postgres-date "~1.0.0" 899 | postgres-interval "^1.1.0" 900 | 901 | pg@^7.2.0: 902 | version "7.3.0" 903 | resolved "https://registry.yarnpkg.com/pg/-/pg-7.3.0.tgz#275e27466e54a645f6b4a16f6acadf6b849ad83b" 904 | dependencies: 905 | buffer-writer "1.0.1" 906 | js-string-escape "1.0.1" 907 | packet-reader "0.3.1" 908 | pg-connection-string "0.1.3" 909 | pg-pool "~2.0.3" 910 | pg-types "~1.12.1" 911 | pgpass "1.x" 912 | semver "4.3.2" 913 | 914 | pgpass@1.x: 915 | version "1.0.2" 916 | resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.2.tgz#2a7bb41b6065b67907e91da1b07c1847c877b306" 917 | dependencies: 918 | split "^1.0.0" 919 | 920 | plur@^1.0.0: 921 | version "1.0.0" 922 | resolved "https://registry.yarnpkg.com/plur/-/plur-1.0.0.tgz#db85c6814f5e5e5a3b49efc28d604fec62975156" 923 | 924 | postgres-array@~1.0.0: 925 | version "1.0.2" 926 | resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-1.0.2.tgz#8e0b32eb03bf77a5c0a7851e0441c169a256a238" 927 | 928 | postgres-bytea@~1.0.0: 929 | version "1.0.0" 930 | resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" 931 | 932 | postgres-date@~1.0.0: 933 | version "1.0.3" 934 | resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.3.tgz#e2d89702efdb258ff9d9cee0fe91bd06975257a8" 935 | 936 | postgres-interval@^1.1.0: 937 | version "1.1.0" 938 | resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.1.0.tgz#1031e7bac34564132862adc9eb6c6d2f3aa75bb4" 939 | dependencies: 940 | xtend "^4.0.0" 941 | 942 | preserve@^0.2.0: 943 | version "0.2.0" 944 | resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" 945 | 946 | pretty-ms@^2.1.0: 947 | version "2.1.0" 948 | resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-2.1.0.tgz#4257c256df3fb0b451d6affaab021884126981dc" 949 | dependencies: 950 | is-finite "^1.0.1" 951 | parse-ms "^1.0.0" 952 | plur "^1.0.0" 953 | 954 | process-nextick-args@~1.0.6: 955 | version "1.0.7" 956 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" 957 | 958 | proxy-addr@~1.1.5: 959 | version "1.1.5" 960 | resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.5.tgz#71c0ee3b102de3f202f3b64f608d173fcba1a918" 961 | dependencies: 962 | forwarded "~0.1.0" 963 | ipaddr.js "1.4.0" 964 | 965 | qs@6.5.0: 966 | version "6.5.0" 967 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.0.tgz#8d04954d364def3efc55b5a0793e1e2c8b1e6e49" 968 | 969 | qs@6.5.1: 970 | version "6.5.1" 971 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" 972 | 973 | qs@^6.1.0: 974 | version "6.4.0" 975 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" 976 | 977 | randomatic@^1.1.3: 978 | version "1.1.7" 979 | resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" 980 | dependencies: 981 | is-number "^3.0.0" 982 | kind-of "^4.0.0" 983 | 984 | range-parser@~1.2.0: 985 | version "1.2.0" 986 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" 987 | 988 | raw-body@2.3.2: 989 | version "2.3.2" 990 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" 991 | dependencies: 992 | bytes "3.0.0" 993 | http-errors "1.6.2" 994 | iconv-lite "0.4.19" 995 | unpipe "1.0.0" 996 | 997 | re-emitter@^1.0.0: 998 | version "1.1.3" 999 | resolved "https://registry.yarnpkg.com/re-emitter/-/re-emitter-1.1.3.tgz#fa9e319ffdeeeb35b27296ef0f3d374dac2f52a7" 1000 | 1001 | readable-stream@^1.1.12: 1002 | version "1.1.14" 1003 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" 1004 | dependencies: 1005 | core-util-is "~1.0.0" 1006 | inherits "~2.0.1" 1007 | isarray "0.0.1" 1008 | string_decoder "~0.10.x" 1009 | 1010 | readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.1.5: 1011 | version "2.3.0" 1012 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.0.tgz#640f5dcda88c91a8dc60787145629170813a1ed2" 1013 | dependencies: 1014 | core-util-is "~1.0.0" 1015 | inherits "~2.0.3" 1016 | isarray "~1.0.0" 1017 | process-nextick-args "~1.0.6" 1018 | safe-buffer "~5.1.0" 1019 | string_decoder "~1.0.0" 1020 | util-deprecate "~1.0.1" 1021 | 1022 | rechoir@^0.6.2: 1023 | version "0.6.2" 1024 | resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" 1025 | dependencies: 1026 | resolve "^1.1.6" 1027 | 1028 | regenerator-runtime@^0.10.0: 1029 | version "0.10.5" 1030 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" 1031 | 1032 | regex-cache@^0.4.2: 1033 | version "0.4.3" 1034 | resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" 1035 | dependencies: 1036 | is-equal-shallow "^0.1.3" 1037 | is-primitive "^2.0.0" 1038 | 1039 | remove-trailing-separator@^1.0.1: 1040 | version "1.0.2" 1041 | resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz#69b062d978727ad14dc6b56ba4ab772fd8d70511" 1042 | 1043 | repeat-element@^1.1.2: 1044 | version "1.1.2" 1045 | resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" 1046 | 1047 | repeat-string@^1.5.2: 1048 | version "1.6.1" 1049 | resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" 1050 | 1051 | resolve-dir@^0.1.0: 1052 | version "0.1.1" 1053 | resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-0.1.1.tgz#b219259a5602fac5c5c496ad894a6e8cc430261e" 1054 | dependencies: 1055 | expand-tilde "^1.2.2" 1056 | global-modules "^0.2.3" 1057 | 1058 | resolve@^1.1.6, resolve@^1.1.7: 1059 | version "1.1.7" 1060 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" 1061 | 1062 | resolve@~1.4.0: 1063 | version "1.4.0" 1064 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86" 1065 | dependencies: 1066 | path-parse "^1.0.5" 1067 | 1068 | resumer@~0.0.0: 1069 | version "0.0.0" 1070 | resolved "https://registry.yarnpkg.com/resumer/-/resumer-0.0.0.tgz#f1e8f461e4064ba39e82af3cdc2a8c893d076759" 1071 | dependencies: 1072 | through "~2.3.4" 1073 | 1074 | safe-buffer@5.1.1: 1075 | version "5.1.1" 1076 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" 1077 | 1078 | safe-buffer@^5.0.1, safe-buffer@~5.1.0: 1079 | version "5.1.0" 1080 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a45e223" 1081 | 1082 | safe-buffer@~5.0.1: 1083 | version "5.0.1" 1084 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" 1085 | 1086 | semver@4.3.2: 1087 | version "4.3.2" 1088 | resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" 1089 | 1090 | send@0.15.6: 1091 | version "0.15.6" 1092 | resolved "https://registry.yarnpkg.com/send/-/send-0.15.6.tgz#20f23a9c925b762ab82705fe2f9db252ace47e34" 1093 | dependencies: 1094 | debug "2.6.9" 1095 | depd "~1.1.1" 1096 | destroy "~1.0.4" 1097 | encodeurl "~1.0.1" 1098 | escape-html "~1.0.3" 1099 | etag "~1.8.1" 1100 | fresh "0.5.2" 1101 | http-errors "~1.6.2" 1102 | mime "1.3.4" 1103 | ms "2.0.0" 1104 | on-finished "~2.3.0" 1105 | range-parser "~1.2.0" 1106 | statuses "~1.3.1" 1107 | 1108 | serve-static@1.12.6: 1109 | version "1.12.6" 1110 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.12.6.tgz#b973773f63449934da54e5beba5e31d9f4211577" 1111 | dependencies: 1112 | encodeurl "~1.0.1" 1113 | escape-html "~1.0.3" 1114 | parseurl "~1.3.2" 1115 | send "0.15.6" 1116 | 1117 | setprototypeof@1.0.3: 1118 | version "1.0.3" 1119 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" 1120 | 1121 | split@^1.0.0: 1122 | version "1.0.0" 1123 | resolved "https://registry.yarnpkg.com/split/-/split-1.0.0.tgz#c4395ce683abcd254bc28fe1dabb6e5c27dcffae" 1124 | dependencies: 1125 | through "2" 1126 | 1127 | stack-trace@0.0.x: 1128 | version "0.0.10" 1129 | resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" 1130 | 1131 | "statuses@>= 1.3.1 < 2", statuses@~1.3.1: 1132 | version "1.3.1" 1133 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" 1134 | 1135 | string.prototype.trim@~1.1.2: 1136 | version "1.1.2" 1137 | resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea" 1138 | dependencies: 1139 | define-properties "^1.1.2" 1140 | es-abstract "^1.5.0" 1141 | function-bind "^1.0.2" 1142 | 1143 | string_decoder@~0.10.x: 1144 | version "0.10.31" 1145 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" 1146 | 1147 | string_decoder@~1.0.0: 1148 | version "1.0.2" 1149 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.2.tgz#b29e1f4e1125fa97a10382b8a533737b7491e179" 1150 | dependencies: 1151 | safe-buffer "~5.0.1" 1152 | 1153 | strip-ansi@^3.0.0: 1154 | version "3.0.1" 1155 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 1156 | dependencies: 1157 | ansi-regex "^2.0.0" 1158 | 1159 | superagent@^3.0.0: 1160 | version "3.5.2" 1161 | resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.5.2.tgz#3361a3971567504c351063abeaae0faa23dbf3f8" 1162 | dependencies: 1163 | component-emitter "^1.2.0" 1164 | cookiejar "^2.0.6" 1165 | debug "^2.2.0" 1166 | extend "^3.0.0" 1167 | form-data "^2.1.1" 1168 | formidable "^1.1.1" 1169 | methods "^1.1.1" 1170 | mime "^1.3.4" 1171 | qs "^6.1.0" 1172 | readable-stream "^2.0.5" 1173 | 1174 | supertest@^3.0.0: 1175 | version "3.0.0" 1176 | resolved "https://registry.yarnpkg.com/supertest/-/supertest-3.0.0.tgz#8d4bb68fd1830ee07033b1c5a5a9a4021c965296" 1177 | dependencies: 1178 | methods "~1.1.2" 1179 | superagent "^3.0.0" 1180 | 1181 | supports-color@^2.0.0: 1182 | version "2.0.0" 1183 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" 1184 | 1185 | tap-out@^1.4.1: 1186 | version "1.4.2" 1187 | resolved "https://registry.yarnpkg.com/tap-out/-/tap-out-1.4.2.tgz#c907ec1bf9405111d088263e92f5608b88cbb37a" 1188 | dependencies: 1189 | re-emitter "^1.0.0" 1190 | readable-stream "^2.0.0" 1191 | split "^1.0.0" 1192 | trim "0.0.1" 1193 | 1194 | tap-spec@^4.1.1: 1195 | version "4.1.1" 1196 | resolved "https://registry.yarnpkg.com/tap-spec/-/tap-spec-4.1.1.tgz#e2e9f26f5208232b1f562288c97624d58a88f05a" 1197 | dependencies: 1198 | chalk "^1.0.0" 1199 | duplexer "^0.1.1" 1200 | figures "^1.4.0" 1201 | lodash "^3.6.0" 1202 | pretty-ms "^2.1.0" 1203 | repeat-string "^1.5.2" 1204 | tap-out "^1.4.1" 1205 | through2 "^2.0.0" 1206 | 1207 | tape@^4.8.0: 1208 | version "4.8.0" 1209 | resolved "https://registry.yarnpkg.com/tape/-/tape-4.8.0.tgz#f6a9fec41cc50a1de50fa33603ab580991f6068e" 1210 | dependencies: 1211 | deep-equal "~1.0.1" 1212 | defined "~1.0.0" 1213 | for-each "~0.3.2" 1214 | function-bind "~1.1.0" 1215 | glob "~7.1.2" 1216 | has "~1.0.1" 1217 | inherits "~2.0.3" 1218 | minimist "~1.2.0" 1219 | object-inspect "~1.3.0" 1220 | resolve "~1.4.0" 1221 | resumer "~0.0.0" 1222 | string.prototype.trim "~1.1.2" 1223 | through "~2.3.8" 1224 | 1225 | through2@^2.0.0: 1226 | version "2.0.3" 1227 | resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" 1228 | dependencies: 1229 | readable-stream "^2.1.5" 1230 | xtend "~4.0.1" 1231 | 1232 | through@2, through@~2.3.4, through@~2.3.8: 1233 | version "2.3.8" 1234 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 1235 | 1236 | tildify@~1.0.0: 1237 | version "1.0.0" 1238 | resolved "https://registry.yarnpkg.com/tildify/-/tildify-1.0.0.tgz#2a021db5e8fbde0a8f8b4df37adaa8fb1d39d7dd" 1239 | dependencies: 1240 | user-home "^1.0.0" 1241 | 1242 | trim@0.0.1: 1243 | version "0.0.1" 1244 | resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" 1245 | 1246 | type-is@~1.6.15: 1247 | version "1.6.15" 1248 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" 1249 | dependencies: 1250 | media-typer "0.3.0" 1251 | mime-types "~2.1.15" 1252 | 1253 | unpipe@1.0.0, unpipe@~1.0.0: 1254 | version "1.0.0" 1255 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 1256 | 1257 | user-home@^1.0.0, user-home@^1.1.1: 1258 | version "1.1.1" 1259 | resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" 1260 | 1261 | util-deprecate@~1.0.1: 1262 | version "1.0.2" 1263 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 1264 | 1265 | utils-merge@1.0.0: 1266 | version "1.0.0" 1267 | resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" 1268 | 1269 | uuid@^3.0.0: 1270 | version "3.1.0" 1271 | resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" 1272 | 1273 | v8flags@^2.0.2: 1274 | version "2.1.1" 1275 | resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" 1276 | dependencies: 1277 | user-home "^1.1.1" 1278 | 1279 | vary@~1.1.1: 1280 | version "1.1.1" 1281 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37" 1282 | 1283 | vary@~1.1.2: 1284 | version "1.1.2" 1285 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" 1286 | 1287 | which@^1.2.12: 1288 | version "1.2.14" 1289 | resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" 1290 | dependencies: 1291 | isexe "^2.0.0" 1292 | 1293 | winston@^2.3.1: 1294 | version "2.3.1" 1295 | resolved "https://registry.yarnpkg.com/winston/-/winston-2.3.1.tgz#0b48420d978c01804cf0230b648861598225a119" 1296 | dependencies: 1297 | async "~1.0.0" 1298 | colors "1.0.x" 1299 | cycle "1.0.x" 1300 | eyes "0.1.x" 1301 | isstream "0.1.x" 1302 | stack-trace "0.0.x" 1303 | 1304 | wrappy@1: 1305 | version "1.0.2" 1306 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1307 | 1308 | xtend@^4.0.0, xtend@~4.0.1: 1309 | version "4.0.1" 1310 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" 1311 | --------------------------------------------------------------------------------